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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

1 /*
2 * Copyright (C) 2006-2014 Andreas Persson
3 *
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
22 #include <algorithm>
23
24 #include <cairomm/context.h>
25 #include <gdkmm/general.h>
26 #include <gdkmm/cursor.h>
27 #include <gtkmm/stock.h>
28 #include <gtkmm/spinbutton.h>
29 #include <gtkmm/dialog.h>
30
31 #include "global.h"
32
33 #define REGION_BLOCK_HEIGHT 20
34 #define KEYBOARD_HEIGHT 40
35
36 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 for (gig::Region* r = instrument->GetFirstRegion() ;
43 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 RegionChooser::RegionChooser() :
64 activeKeyColor("red"),
65 red("#8070ff"),
66 grey1("grey69"),
67 white("white"),
68 black("black"),
69 m_VirtKeybModeChoice(_("Virtual Keyboard Mode")),
70 currentActiveKey(-1)
71 {
72 set_size_request(500, KEYBOARD_HEIGHT + REGION_BLOCK_HEIGHT);
73
74 instrument = 0;
75 region = 0;
76 resize.active = false;
77 move.active = false;
78 cursor_is_resize = false;
79 h1 = REGION_BLOCK_HEIGHT;
80
81 // properties of the virtual keyboard
82 {
83 const char* choices[] = { _("normal"), _("chord"), 0 };
84 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 for (int i = 0 ; i < 128 ; i++) key_pressed[i] = false;
104
105 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 dimensionManager.region_to_be_changed_signal.connect(
141 region_to_be_changed_signal.make_slot()
142 );
143 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 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 }
158
159 RegionChooser::~RegionChooser()
160 {
161 }
162
163 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 void RegionChooser::on_note_on_event(int key, int velocity) {
174 key_pressed[key] = true;
175 invalidate_key(key);
176 m_VirtKeybVelocityLabel.set_text(ToString(velocity));
177 }
178
179 void RegionChooser::on_note_off_event(int key, int velocity) {
180 key_pressed[key] = false;
181 invalidate_key(key);
182 m_VirtKeybOffVelocityLabel.set_text(ToString(velocity));
183 }
184
185
186 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
187 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 }
197 #endif
198 #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
204 cr->save();
205 cr->set_line_width(1);
206
207 #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
215 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 Gdk::Cairo::set_source_rgba(cr, black);
235 cr->rectangle(0.5, h1 + 0.5, w, h - 1);
236 cr->stroke();
237
238 int x1 = key_to_x(20.5, w);
239 Gdk::Cairo::set_source_rgba(cr, grey1);
240 cr->rectangle(1, h1 + 1, x1 - 1, h - 2);
241 cr->fill();
242
243 int x2 = key_to_x(109.5, w);
244 Gdk::Cairo::set_source_rgba(cr, white);
245 cr->rectangle(x1 + 1, h1 + 1, x2 - x1 - 1, h - 2);
246 cr->fill();
247
248 Gdk::Cairo::set_source_rgba(cr, grey1);
249 cr->rectangle(x2 + 1, h1 + 1, w - x2 - 1, h - 2);
250 cr->fill();
251
252 Gdk::Cairo::set_source_rgba(cr, black);
253
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 int note = (i + 3) % 12;
259 int x = key_to_x(i, w);
260
261 if (note == 1 || note == 4 || note == 6 ||
262 note == 9 || note == 11) {
263 // black key: short line in the middle, with a rectangle
264 // on top
265 int x2 = key_to_x(i + 0.5, w);
266 cr->move_to(x2 + 0.5, h1 + bh + 0.5);
267 cr->line_to(x2 + 0.5, h1 + h - 1);
268 cr->stroke();
269
270 int x3 = key_to_x(i + 1, w);
271 cr->rectangle(x, h1 + 1, x3 - x + 1, bh);
272 cr->fill();
273 } else if (note == 3 || note == 8) {
274 // C or F: long line to the left
275 cr->move_to(x + 0.5, h1 + 1);
276 cr->line_to(x + 0.5, h1 + h - 1);
277 cr->stroke();
278 }
279
280 if (key_pressed[i]) draw_key(cr, i);
281
282 if (note == 3) draw_digit(cr, i);
283 }
284 }
285
286
287 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 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
313 Gdk::Cairo::set_source_rgba(cr, region == r ? red : white);
314 cr->rectangle(x3 + 1, 1, x2 - x3 - 1, h1 - 2);
315 cr->fill();
316 Gdk::Cairo::set_source_rgba(cr, black);
317 }
318 x3 = -1;
319 }
320 }
321
322 for (gig::Region* r = regions.first() ; r ; r = regions.next()) {
323 int x = key_to_x(r->KeyRange.low, w);
324
325 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 }
332 }
333
334 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
339 void RegionChooser::draw_digit(const Cairo::RefPtr<Cairo::Context>& cr,
340 int key) {
341 const int h = KEYBOARD_HEIGHT;
342 const int w = get_width() - 1;
343 Glib::RefPtr<Pango::Layout> layout =
344 Pango::Layout::create(get_pango_context());
345 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 Gdk::Cairo::set_source_rgba(cr, black);
353 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 }
360
361 void RegionChooser::draw_key(const Cairo::RefPtr<Cairo::Context>& cr,
362 int key) {
363 const int h = KEYBOARD_HEIGHT;
364 const int w = get_width() - 1;
365 const int bh = int(h * 0.55);
366
367 Gdk::Cairo::set_source_rgba(cr, activeKeyColor);
368
369 int note = (key + 3) % 12;
370 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 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 }
399 Gdk::Cairo::set_source_rgba(cr, black);
400 }
401
402 void RegionChooser::set_instrument(gig::Instrument* instrument)
403 {
404 this->instrument = instrument;
405 regions.update(instrument);
406 region = regions.first();
407 queue_draw();
408 region_selected();
409 dimensionManager.set_region(region);
410 }
411
412 bool RegionChooser::on_button_release_event(GdkEventButton* event)
413 {
414 const int k = x_to_key(event->x, get_width() - 1);
415
416 // handle-note off on virtual keyboard
417 if (event->type == GDK_BUTTON_RELEASE) {
418 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 }
434 }
435
436 if (resize.active) {
437 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
438 get_window()->pointer_ungrab(event->time);
439 #else
440 Glib::wrap(event->device, true)->ungrab(event->time);
441 #endif
442 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 } else if (move.active) {
449 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
450 get_window()->pointer_ungrab(event->time);
451 #else
452 Glib::wrap(event->device, true)->ungrab(event->time);
453 #endif
454 move.active = false;
455
456 if (is_in_resize_zone(event->x, event->y)) {
457 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
458 get_window()->set_cursor(Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW));
459 #else
460 get_window()->set_cursor(Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW));
461 #endif
462 cursor_is_resize = true;
463 }
464 }
465 return true;
466 }
467
468 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 bool RegionChooser::on_button_press_event(GdkEventButton* event)
502 {
503 if (!instrument) return true;
504
505 const int w = get_width() - 1;
506 const int k = x_to_key(event->x, w);
507
508 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 currentActiveKey = k;
513 keyboard_key_hit_signal.emit(k, velocity);
514 }
515 }
516
517 if (event->y >= REGION_BLOCK_HEIGHT) return true;
518 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 region_selected();
524 dimensionManager.set_region(region);
525 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 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
533 get_window()->pointer_grab(false,
534 Gdk::BUTTON_RELEASE_MASK |
535 Gdk::POINTER_MOTION_MASK |
536 Gdk::POINTER_MOTION_HINT_MASK,
537 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 resize.active = true;
550 } else {
551 gig::Region* r = get_region(k);
552 if (r) {
553 region = r;
554 queue_draw();
555 region_selected();
556 dimensionManager.set_region(region);
557
558 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
559 get_window()->pointer_grab(false,
560 Gdk::BUTTON_RELEASE_MASK |
561 Gdk::POINTER_MOTION_MASK |
562 Gdk::POINTER_MOTION_HINT_MASK,
563 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 move.active = true;
576 move.offset = event->x - key_to_x(region->KeyRange.low, w);
577 }
578 }
579 }
580 return true;
581 }
582
583 gig::Region* RegionChooser::get_region(int key)
584 {
585 for (gig::Region* r = regions.first() ; r ; r = regions.next()) {
586 if (key < r->KeyRange.low) return 0;
587 if (key <= r->KeyRange.high) return r;
588 }
589 return 0;
590 }
591
592 void RegionChooser::motion_resize_region(int x, int y)
593 {
594 const int w = get_width() - 1;
595
596 int k = int(double(x) / w * 128.0 + 0.5);
597
598 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 }
613 }
614 resize.pos = k;
615
616 int x1, x2;
617 if (resize.mode == resize.moving_high_limit) {
618 if (resize.region->KeyRange.high < resize.pos - 1) {
619 x1 = resize.region->KeyRange.high;
620 x2 = resize.pos - 1;
621 } else {
622 x1 = resize.pos - 1;
623 x2 = resize.region->KeyRange.high;
624 }
625 } else {
626 if (resize.region->KeyRange.low < resize.pos) {
627 x1 = resize.region->KeyRange.low;
628 x2 = resize.pos;
629 } else {
630 x1 = resize.pos;
631 x2 = resize.region->KeyRange.low;
632 }
633 }
634 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 }
642 }
643
644 void RegionChooser::motion_move_region(int x, int y)
645 {
646 const int w = get_width() - 1;
647
648 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 int a = 0;
654 if (l > region->KeyRange.low) {
655 for (gig::Region* r = regions.first() ; ; r = regions.next()) {
656 if (r != region) {
657 int b = r ? r->KeyRange.low : 128;
658
659 // gap: from a to b (not inclusive b)
660
661 if (region->KeyRange.high >= b) {
662 // not found the current gap yet, just continue
663 } else {
664
665 if (a > l) {
666 // this gap is too far to the right, break
667 break;
668 }
669
670 int newhigh = std::min(l + regionsize, b - 1);
671 int newlo = newhigh - regionsize;
672
673 if (newlo >= a) {
674 // yes it fits - it's a candidate
675 new_l = newlo;
676 }
677 }
678 if (!r) break;
679 a = r->KeyRange.high + 1;
680 }
681 }
682 } else {
683 for (gig::Region* r = regions.first() ; ; r = regions.next()) {
684 if (r != region) {
685 int b = r ? r->KeyRange.low : 128;
686
687 // gap from a to b (not inclusive b)
688
689 if (l + regionsize >= b) {
690 // not found the current gap yet, just continue
691 } else {
692
693 if (a > region->KeyRange.low) {
694 // this gap is too far to the right, break
695 break;
696 }
697
698 int newlo = std::max(l, a);
699 int newhigh = newlo + regionsize;
700
701 if (newhigh < b) {
702 // yes it fits - break as the first one is the best
703 new_l = newlo;
704 break;
705 }
706 }
707 if (!r) break;
708 a = r->KeyRange.high + 1;
709 }
710 }
711 }
712 if (new_l == region->KeyRange.low) return;
713
714 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
718 Gdk::Rectangle rect(x1, 0, x2 - x1, h1);
719 update_after_move(new_l);
720
721 get_window()->invalidate_rect(rect, false);
722 }
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 // 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 const int k = x_to_key(event->x, get_width() - 1);
739 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 }
750
751 if (resize.active) {
752 motion_resize_region(x, y);
753 } else if (move.active) {
754 motion_move_region(x, y);
755 } else {
756 if (is_in_resize_zone(x, y)) {
757 if (!cursor_is_resize) {
758 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
759 window->set_cursor(Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW));
760 #else
761 window->set_cursor(Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW));
762 #endif
763 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 const int w = get_width() - 1;
776
777 if (instrument && y >= 0 && y <= h1) {
778 gig::Region* prev_region = 0;
779 gig::Region* next_region;
780 for (gig::Region* r = regions.first(); r ; r = next_region) {
781 next_region = regions.next();
782
783 int lo = key_to_x(r->KeyRange.low, w);
784 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 return resize.min != resize.max;
798 }
799
800 // edit low limit
801 resize.mode = resize.moving_low_limit;
802 resize.min = prev_region ? prev_region->KeyRange.high + 1 : 0;
803 return resize.min != resize.max;
804 }
805 if (!next_region || r->KeyRange.high + 1 != next_region->KeyRange.low) {
806 int hi = key_to_x(r->KeyRange.high + 1, w);
807 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 return resize.min != resize.max;
816 }
817 }
818 prev_region = r;
819 }
820 }
821 return false;
822 }
823
824 sigc::signal<void>& RegionChooser::signal_region_selected()
825 {
826 return region_selected;
827 }
828
829 sigc::signal<void>& RegionChooser::signal_instrument_changed()
830 {
831 return instrument_changed;
832 }
833
834 void RegionChooser::show_region_properties()
835 {
836 if (!region) return;
837 Gtk::Dialog dialog(_("Region Properties"), true /*modal*/);
838 // add "Keygroup" checkbox
839 Gtk::CheckButton checkBoxKeygroup(_("Member of a Keygroup (Exclusive Group)"));
840 checkBoxKeygroup.set_active(region->KeyGroup);
841 dialog.get_vbox()->pack_start(checkBoxKeygroup);
842 checkBoxKeygroup.show();
843 // add "Keygroup" spinbox
844 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
845 Gtk::Adjustment adjustment(1, 1, 999);
846 Gtk::SpinButton spinBox(adjustment);
847 #else
848 Gtk::SpinButton spinBox(Gtk::Adjustment::create(1, 1, 999));
849 #endif
850 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 instrument_struct_to_be_changed_signal.emit(instrument);
866
867 region = instrument->AddRegion();
868 region->SetKeyRange(new_region_pos, new_region_pos);
869
870 instrument_struct_changed_signal.emit(instrument);
871 regions.update(instrument);
872
873 queue_draw();
874 region_selected();
875 dimensionManager.set_region(region);
876 instrument_changed();
877 }
878
879 void RegionChooser::delete_region()
880 {
881 instrument_struct_to_be_changed_signal.emit(instrument);
882 instrument->DeleteRegion(region);
883 instrument_struct_changed_signal.emit(instrument);
884 regions.update(instrument);
885
886 region = 0;
887 queue_draw();
888 region_selected();
889 dimensionManager.set_region(region);
890 instrument_changed();
891 }
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 region_selected();
902 instrument_changed();
903 }
904
905 sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_to_be_changed() {
906 return instrument_struct_to_be_changed_signal;
907 }
908
909 sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_changed() {
910 return instrument_struct_changed_signal;
911 }
912
913 sigc::signal<void, gig::Region*>& RegionChooser::signal_region_to_be_changed() {
914 return region_to_be_changed_signal;
915 }
916
917 sigc::signal<void, gig::Region*>& RegionChooser::signal_region_changed_signal() {
918 return region_changed_signal;
919 }
920
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