/[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 2442 - (show annotations) (download)
Sun Apr 14 07:29:59 2013 UTC (6 years, 1 month ago) by persson
File size: 31873 byte(s)
* made sure the instruments menu is updated when instruments are
  added, removed or renamed

1 /*
2 * Copyright (C) 2006-2011 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 const int w = get_width() - 1;
216
217 if (clipy2 > h1) {
218 draw_keyboard(cr, clipx1, clipx2);
219 }
220
221 if (clipy1 < h1 && instrument) {
222 draw_regions(cr, clipx1, clipx2);
223 }
224
225 cr->restore();
226
227 return true;
228 }
229
230 void RegionChooser::draw_keyboard(const Cairo::RefPtr<Cairo::Context>& cr,
231 int clip_low, int clip_high) {
232 const int h = KEYBOARD_HEIGHT;
233 const int w = get_width() - 1;
234 const int bh = int(h * 0.55);
235
236 Gdk::Cairo::set_source_rgba(cr, black);
237 cr->rectangle(0.5, h1 + 0.5, w, h - 1);
238 cr->stroke();
239
240 int x1 = key_to_x(20.5, w);
241 Gdk::Cairo::set_source_rgba(cr, grey1);
242 cr->rectangle(1, h1 + 1, x1 - 1, h - 2);
243 cr->fill();
244
245 int x2 = key_to_x(109.5, w);
246 Gdk::Cairo::set_source_rgba(cr, white);
247 cr->rectangle(x1 + 1, h1 + 1, x2 - x1 - 1, h - 2);
248 cr->fill();
249
250 Gdk::Cairo::set_source_rgba(cr, grey1);
251 cr->rectangle(x2 + 1, h1 + 1, w - x2 - 1, h - 2);
252 cr->fill();
253
254 Gdk::Cairo::set_source_rgba(cr, black);
255
256 int clipkey1 = std::max(0, x_to_key_right(clip_low - 1, w));
257 int clipkey2 = std::min(x_to_key_right(clip_high - 1, w) + 1, 128);
258
259 for (int i = clipkey1 ; i < clipkey2 ; i++) {
260 int note = (i + 3) % 12;
261 int x = key_to_x(i, w);
262
263 if (note == 1 || note == 4 || note == 6 ||
264 note == 9 || note == 11) {
265 // black key: short line in the middle, with a rectangle
266 // on top
267 int x2 = key_to_x(i + 0.5, w);
268 cr->move_to(x2 + 0.5, h1 + bh + 0.5);
269 cr->line_to(x2 + 0.5, h1 + h - 1);
270 cr->stroke();
271
272 int x3 = key_to_x(i + 1, w);
273 cr->rectangle(x, h1 + 1, x3 - x + 1, bh);
274 cr->fill();
275 } else if (note == 3 || note == 8) {
276 // C or F: long line to the left
277 cr->move_to(x + 0.5, h1 + 1);
278 cr->line_to(x + 0.5, h1 + h - 1);
279 cr->stroke();
280 }
281
282 if (key_pressed[i]) draw_key(cr, i);
283
284 if (note == 3) draw_digit(cr, i);
285 }
286 }
287
288
289 void RegionChooser::draw_regions(const Cairo::RefPtr<Cairo::Context>& cr,
290 int clip_low, int clip_high) {
291 const int w = get_width() - 1;
292
293 Gdk::Cairo::set_source_rgba(cr, black);
294 gig::Region* next_region;
295 int x3 = -1;
296 for (gig::Region* r = regions.first() ; r ; r = next_region) {
297 next_region = regions.next();
298
299 if (x3 < 0) {
300 x3 = key_to_x(r->KeyRange.low, w);
301 if (x3 >= clip_high) break;
302 }
303 if (!next_region ||
304 r->KeyRange.high + 1 != next_region->KeyRange.low ||
305 r == region || next_region == region) {
306
307 int x2 = key_to_x(r->KeyRange.high + 1, w);
308 if (x2 >= clip_low) {
309 cr->move_to(x3, 0.5);
310 cr->line_to(x2 + 0.5, 0.5);
311 cr->line_to(x2 + 0.5, h1 - 0.5);
312 cr->line_to(x3, h1 - 0.5);
313 cr->stroke();
314
315 Gdk::Cairo::set_source_rgba(cr, region == r ? red : white);
316 cr->rectangle(x3 + 1, 1, x2 - x3 - 1, h1 - 2);
317 cr->fill();
318 Gdk::Cairo::set_source_rgba(cr, black);
319 }
320 x3 = -1;
321 }
322 }
323
324 for (gig::Region* r = regions.first() ; r ; r = regions.next()) {
325 int x = key_to_x(r->KeyRange.low, w);
326
327 if (x < clip_low) continue;
328 if (x >= clip_high) break;
329
330 cr->move_to(x + 0.5, 1);
331 cr->line_to(x + 0.5, h1 - 1);
332 cr->stroke();
333 }
334 }
335
336 bool RegionChooser::is_black_key(int key) {
337 const int note = (key + 3) % 12;
338 return note == 1 || note == 4 || note == 6 || note == 9 || note == 11;
339 }
340
341 void RegionChooser::draw_digit(const Cairo::RefPtr<Cairo::Context>& cr,
342 int key) {
343 const int h = KEYBOARD_HEIGHT;
344 const int w = get_width() - 1;
345 Glib::RefPtr<Pango::Layout> layout =
346 Pango::Layout::create(get_pango_context());
347 char buf[30];
348 sprintf(buf, "<span size=\"8000\">%d</span>", key / 12 - 1);
349 layout->set_markup(buf);
350 Pango::Rectangle rectangle = layout->get_logical_extents();
351 double text_w = double(rectangle.get_width()) / Pango::SCALE;
352 double text_h = double(rectangle.get_height()) / Pango::SCALE;
353 double x = w * (key + 0.75) / 128.0;
354 Gdk::Cairo::set_source_rgba(cr, black);
355 cr->move_to(int(x - text_w / 2 + 1), int(h1 + h - text_h + 0.5));
356 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 16) || GTKMM_MAJOR_VERSION < 2
357 pango_cairo_show_layout(cr->cobj(), layout->gobj());
358 #else
359 layout->show_in_cairo_context(cr);
360 #endif
361 }
362
363 void RegionChooser::draw_key(const Cairo::RefPtr<Cairo::Context>& cr,
364 int key) {
365 const int h = KEYBOARD_HEIGHT;
366 const int w = get_width() - 1;
367 const int bh = int(h * 0.55);
368
369 Gdk::Cairo::set_source_rgba(cr, activeKeyColor);
370
371 int note = (key + 3) % 12;
372 int x = key_to_x(key, w) + 1;
373 int x2 = key_to_x(key + 1.5, w);
374 int x3 = key_to_x(key + 1, w);
375 int x4 = key_to_x(key - 0.5, w);
376 int w1 = x3 - x;
377 switch (note) {
378 case 0: case 5: case 10:
379 cr->rectangle(x, h1 + 1, w1, bh);
380 cr->fill();
381 cr->rectangle(x4 + 1, h1 + bh + 1, x2 - x4 - 1, h - bh - 2);
382 cr->fill();
383 break;
384 case 2: case 7:
385 cr->rectangle(x, h1 + 1, w1, bh);
386 cr->fill();
387 cr->rectangle(x4 + 1, h1 + bh + 1, x3 - x4 - 1, h - bh - 2);
388 cr->fill();
389 break;
390 case 3: case 8:
391 cr->rectangle(x, h1 + 1, w1, bh);
392 cr->fill();
393 cr->rectangle(x, h1 + bh + 1, x2 - x, h - bh - 2);
394 cr->fill();
395 break;
396 default:
397 cr->rectangle(x, h1 + 1, w1, bh - 1);
398 cr->fill();
399 break;
400 }
401 Gdk::Cairo::set_source_rgba(cr, black);
402 }
403
404 void RegionChooser::set_instrument(gig::Instrument* instrument)
405 {
406 this->instrument = instrument;
407 regions.update(instrument);
408 region = regions.first();
409 queue_draw();
410 region_selected();
411 dimensionManager.set_region(region);
412 }
413
414 bool RegionChooser::on_button_release_event(GdkEventButton* event)
415 {
416 const int k = x_to_key(event->x, get_width() - 1);
417
418 // handle-note off on virtual keyboard
419 if (event->type == GDK_BUTTON_RELEASE) {
420 int velocity = (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
421 int(float(event->y - REGION_BLOCK_HEIGHT) / float(KEYBOARD_HEIGHT) * 128.0f) + 1;
422 if (velocity <= 0) velocity = 1;
423 switch (m_VirtKeybModeChoice.get_value()) {
424 case VIRT_KEYBOARD_MODE_CHORD:
425 if (event->y >= REGION_BLOCK_HEIGHT)
426 keyboard_key_released_signal.emit(k, velocity);
427 break;
428 case VIRT_KEYBOARD_MODE_NORMAL:
429 default:
430 if (currentActiveKey >= 0 && currentActiveKey <= 127) {
431 keyboard_key_released_signal.emit(currentActiveKey, velocity);
432 currentActiveKey = -1;
433 }
434 break;
435 }
436 }
437
438 if (resize.active) {
439 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
440 get_window()->pointer_ungrab(event->time);
441 #else
442 Glib::wrap(event->device, true)->ungrab(event->time);
443 #endif
444 resize.active = false;
445
446 if (!is_in_resize_zone(event->x, event->y) && cursor_is_resize) {
447 get_window()->set_cursor();
448 cursor_is_resize = false;
449 }
450 } else if (move.active) {
451 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
452 get_window()->pointer_ungrab(event->time);
453 #else
454 Glib::wrap(event->device, true)->ungrab(event->time);
455 #endif
456 move.active = false;
457
458 if (is_in_resize_zone(event->x, event->y)) {
459 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
460 get_window()->set_cursor(Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW));
461 #else
462 get_window()->set_cursor(Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW));
463 #endif
464 cursor_is_resize = true;
465 }
466 }
467 return true;
468 }
469
470 void RegionChooser::update_after_resize()
471 {
472 if (resize.mode == resize.moving_high_limit) {
473 if (resize.region->KeyRange.high != resize.pos - 1) {
474 instrument_struct_to_be_changed_signal.emit(instrument);
475 resize.region->SetKeyRange(resize.region->KeyRange.low,
476 resize.pos - 1);
477 regions.update(instrument);
478 instrument_changed.emit();
479 instrument_struct_changed_signal.emit(instrument);
480 }
481 } else if (resize.mode == resize.moving_low_limit) {
482 if (resize.region->KeyRange.low != resize.pos) {
483 instrument_struct_to_be_changed_signal.emit(instrument);
484 resize.region->SetKeyRange(resize.pos,
485 resize.region->KeyRange.high);
486 regions.update(instrument);
487 instrument_changed.emit();
488 instrument_struct_changed_signal.emit(instrument);
489 }
490 }
491 }
492
493 void RegionChooser::update_after_move(int pos)
494 {
495 instrument_struct_to_be_changed_signal.emit(instrument);
496 region->SetKeyRange(pos, pos + region->KeyRange.high -
497 region->KeyRange.low);
498 regions.update(instrument);
499 instrument_changed.emit();
500 instrument_struct_changed_signal.emit(instrument);
501 }
502
503 bool RegionChooser::on_button_press_event(GdkEventButton* event)
504 {
505 if (!instrument) return true;
506
507 const int w = get_width() - 1;
508 const int k = x_to_key(event->x, w);
509
510 if (event->type == GDK_BUTTON_PRESS) {
511 if (event->y >= REGION_BLOCK_HEIGHT) {
512 int velocity = (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
513 int(float(event->y - REGION_BLOCK_HEIGHT) / float(KEYBOARD_HEIGHT) * 128.0f) + 1;
514 currentActiveKey = k;
515 keyboard_key_hit_signal.emit(k, velocity);
516 }
517 }
518
519 if (event->y >= REGION_BLOCK_HEIGHT) return true;
520 if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
521 gig::Region* r = get_region(k);
522 if (r) {
523 region = r;
524 queue_draw();
525 region_selected();
526 dimensionManager.set_region(region);
527 popup_menu_inside_region->popup(event->button, event->time);
528 } else {
529 new_region_pos = k;
530 popup_menu_outside_region->popup(event->button, event->time);
531 }
532 } else {
533 if (is_in_resize_zone(event->x, event->y)) {
534 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
535 get_window()->pointer_grab(false,
536 Gdk::BUTTON_RELEASE_MASK |
537 Gdk::POINTER_MOTION_MASK |
538 Gdk::POINTER_MOTION_HINT_MASK,
539 Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW),
540 event->time);
541 #else
542 Glib::wrap(event->device, true)->grab(get_window(),
543 Gdk::OWNERSHIP_NONE,
544 false,
545 Gdk::BUTTON_RELEASE_MASK |
546 Gdk::POINTER_MOTION_MASK |
547 Gdk::POINTER_MOTION_HINT_MASK,
548 Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW),
549 event->time);
550 #endif
551 resize.active = true;
552 } else {
553 gig::Region* r = get_region(k);
554 if (r) {
555 region = r;
556 queue_draw();
557 region_selected();
558 dimensionManager.set_region(region);
559
560 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
561 get_window()->pointer_grab(false,
562 Gdk::BUTTON_RELEASE_MASK |
563 Gdk::POINTER_MOTION_MASK |
564 Gdk::POINTER_MOTION_HINT_MASK,
565 Gdk::Cursor(Gdk::FLEUR),
566 event->time);
567 #else
568 Glib::wrap(event->device, true)->grab(get_window(),
569 Gdk::OWNERSHIP_NONE,
570 false,
571 Gdk::BUTTON_RELEASE_MASK |
572 Gdk::POINTER_MOTION_MASK |
573 Gdk::POINTER_MOTION_HINT_MASK,
574 Gdk::Cursor::create(Gdk::FLEUR),
575 event->time);
576 #endif
577 move.active = true;
578 move.offset = event->x - key_to_x(region->KeyRange.low, w);
579 }
580 }
581 }
582 return true;
583 }
584
585 gig::Region* RegionChooser::get_region(int key)
586 {
587 for (gig::Region* r = regions.first() ; r ; r = regions.next()) {
588 if (key < r->KeyRange.low) return 0;
589 if (key <= r->KeyRange.high) return r;
590 }
591 return 0;
592 }
593
594 void RegionChooser::motion_resize_region(int x, int y)
595 {
596 const int w = get_width() - 1;
597
598 int k = int(double(x) / w * 128.0 + 0.5);
599
600 if (k < resize.min) k = resize.min;
601 else if (k > resize.max) k = resize.max;
602
603 if (k != resize.pos) {
604 if (resize.mode == resize.undecided) {
605 if (k < resize.pos) {
606 // edit high limit of prev_region
607 resize.max = resize.region->KeyRange.low;
608 resize.region = resize.prev_region;
609 resize.mode = resize.moving_high_limit;
610 } else {
611 // edit low limit of region
612 resize.min = resize.prev_region->KeyRange.high + 1;
613 resize.mode = resize.moving_low_limit;
614 }
615 }
616 resize.pos = k;
617
618 int x1, x2;
619 if (resize.mode == resize.moving_high_limit) {
620 if (resize.region->KeyRange.high < resize.pos - 1) {
621 x1 = resize.region->KeyRange.high;
622 x2 = resize.pos - 1;
623 } else {
624 x1 = resize.pos - 1;
625 x2 = resize.region->KeyRange.high;
626 }
627 } else {
628 if (resize.region->KeyRange.low < resize.pos) {
629 x1 = resize.region->KeyRange.low;
630 x2 = resize.pos;
631 } else {
632 x1 = resize.pos;
633 x2 = resize.region->KeyRange.low;
634 }
635 }
636 x1 = key_to_x(x1, w);
637 x2 = key_to_x(x2 + 1, w) + 1;
638 Gdk::Rectangle rect(x1, 0, x2 - x1, h1);
639
640 update_after_resize();
641
642 get_window()->invalidate_rect(rect, false);
643 }
644 }
645
646 void RegionChooser::motion_move_region(int x, int y)
647 {
648 const int w = get_width() - 1;
649
650 int l = int(double(x - move.offset) / w * 128.0 + 0.5);
651
652 if (l == region->KeyRange.low) return;
653 int new_l;
654 int regionsize = region->KeyRange.high - region->KeyRange.low;
655 int a = 0;
656 if (l > region->KeyRange.low) {
657 for (gig::Region* r = regions.first() ; ; r = regions.next()) {
658 if (r != region) {
659 int b = r ? r->KeyRange.low : 128;
660
661 // gap: from a to b (not inclusive b)
662
663 if (region->KeyRange.high >= b) {
664 // not found the current gap yet, just continue
665 } else {
666
667 if (a > l) {
668 // this gap is too far to the right, break
669 break;
670 }
671
672 int newhigh = std::min(l + regionsize, b - 1);
673 int newlo = newhigh - regionsize;
674
675 if (newlo >= a) {
676 // yes it fits - it's a candidate
677 new_l = newlo;
678 }
679 }
680 if (!r) break;
681 a = r->KeyRange.high + 1;
682 }
683 }
684 } else {
685 for (gig::Region* r = regions.first() ; ; r = regions.next()) {
686 if (r != region) {
687 int b = r ? r->KeyRange.low : 128;
688
689 // gap from a to b (not inclusive b)
690
691 if (l + regionsize >= b) {
692 // not found the current gap yet, just continue
693 } else {
694
695 if (a > region->KeyRange.low) {
696 // this gap is too far to the right, break
697 break;
698 }
699
700 int newlo = std::max(l, a);
701 int newhigh = newlo + regionsize;
702
703 if (newhigh < b) {
704 // yes it fits - break as the first one is the best
705 new_l = newlo;
706 break;
707 }
708 }
709 if (!r) break;
710 a = r->KeyRange.high + 1;
711 }
712 }
713 }
714 if (new_l == region->KeyRange.low) return;
715
716 int x1 = key_to_x(std::min(int(region->KeyRange.low), new_l), w);
717 int x2 = key_to_x(std::max(int(region->KeyRange.high),
718 new_l + regionsize) + 1, w) + 1;
719
720 Gdk::Rectangle rect(x1, 0, x2 - x1, h1);
721 update_after_move(new_l);
722
723 get_window()->invalidate_rect(rect, false);
724 }
725
726
727 bool RegionChooser::on_motion_notify_event(GdkEventMotion* event)
728 {
729 Glib::RefPtr<Gdk::Window> window = get_window();
730 int x, y;
731 Gdk::ModifierType state = Gdk::ModifierType(0);
732 window->get_pointer(x, y, state);
733
734 // handle virtual MIDI keyboard
735 if (m_VirtKeybModeChoice.get_value() != VIRT_KEYBOARD_MODE_CHORD &&
736 currentActiveKey > 0 &&
737 event->y >= REGION_BLOCK_HEIGHT &&
738 event->y < REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT)
739 {
740 const int k = x_to_key(event->x, get_width() - 1);
741 if (k != currentActiveKey) {
742 int velocity =
743 (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
744 int(float(event->y - REGION_BLOCK_HEIGHT) /
745 float(KEYBOARD_HEIGHT) * 128.0f) + 1;
746 if (velocity <= 0) velocity = 1;
747 keyboard_key_released_signal.emit(currentActiveKey, velocity);
748 currentActiveKey = k;
749 keyboard_key_hit_signal.emit(k, velocity);
750 }
751 }
752
753 if (resize.active) {
754 motion_resize_region(x, y);
755 } else if (move.active) {
756 motion_move_region(x, y);
757 } else {
758 if (is_in_resize_zone(x, y)) {
759 if (!cursor_is_resize) {
760 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
761 window->set_cursor(Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW));
762 #else
763 window->set_cursor(Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW));
764 #endif
765 cursor_is_resize = true;
766 }
767 } else if (cursor_is_resize) {
768 window->set_cursor();
769 cursor_is_resize = false;
770 }
771 }
772
773 return true;
774 }
775
776 bool RegionChooser::is_in_resize_zone(double x, double y) {
777 const int w = get_width() - 1;
778
779 if (instrument && y >= 0 && y <= h1) {
780 gig::Region* prev_region = 0;
781 gig::Region* next_region;
782 for (gig::Region* r = regions.first(); r ; r = next_region) {
783 next_region = regions.next();
784
785 int lo = key_to_x(r->KeyRange.low, w);
786 if (x <= lo - 2) break;
787 if (x < lo + 2) {
788 resize.region = r;
789 resize.pos = r->KeyRange.low;
790 resize.max = r->KeyRange.high;
791
792 if (prev_region && prev_region->KeyRange.high + 1 == r->KeyRange.low) {
793 // we don't know yet if it's the high limit of
794 // prev_region or the low limit of r that's going
795 // to be edited
796 resize.mode = resize.undecided;
797 resize.min = prev_region->KeyRange.low + 1;
798 resize.prev_region = prev_region;
799 return resize.min != resize.max;
800 }
801
802 // edit low limit
803 resize.mode = resize.moving_low_limit;
804 resize.min = prev_region ? prev_region->KeyRange.high + 1 : 0;
805 return resize.min != resize.max;
806 }
807 if (!next_region || r->KeyRange.high + 1 != next_region->KeyRange.low) {
808 int hi = key_to_x(r->KeyRange.high + 1, w);
809 if (x <= hi - 2) break;
810 if (x < hi + 2) {
811 // edit high limit
812 resize.region = r;
813 resize.pos = r->KeyRange.high + 1;
814 resize.mode = resize.moving_high_limit;
815 resize.min = r->KeyRange.low + 1;
816 resize.max = next_region ? next_region->KeyRange.low : 128;
817 return resize.min != resize.max;
818 }
819 }
820 prev_region = r;
821 }
822 }
823 return false;
824 }
825
826 sigc::signal<void>& RegionChooser::signal_region_selected()
827 {
828 return region_selected;
829 }
830
831 sigc::signal<void>& RegionChooser::signal_instrument_changed()
832 {
833 return instrument_changed;
834 }
835
836 void RegionChooser::show_region_properties()
837 {
838 if (!region) return;
839 Gtk::Dialog dialog(_("Region Properties"), true /*modal*/);
840 // add "Keygroup" checkbox
841 Gtk::CheckButton checkBoxKeygroup(_("Member of a Keygroup (Exclusive Group)"));
842 checkBoxKeygroup.set_active(region->KeyGroup);
843 dialog.get_vbox()->pack_start(checkBoxKeygroup);
844 checkBoxKeygroup.show();
845 // add "Keygroup" spinbox
846 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
847 Gtk::Adjustment adjustment(1, 1, 999);
848 Gtk::SpinButton spinBox(adjustment);
849 #else
850 Gtk::SpinButton spinBox(Gtk::Adjustment::create(1, 1, 999));
851 #endif
852 if (region->KeyGroup) spinBox.set_value(region->KeyGroup);
853 dialog.get_vbox()->pack_start(spinBox);
854 spinBox.show();
855 // add OK and CANCEL buttons to the dialog
856 dialog.add_button(Gtk::Stock::OK, 0);
857 dialog.add_button(Gtk::Stock::CANCEL, 1);
858 dialog.show_all_children();
859 if (!dialog.run()) { // OK selected ...
860 region->KeyGroup =
861 (checkBoxKeygroup.get_active()) ? spinBox.get_value_as_int() : 0;
862 }
863 }
864
865 void RegionChooser::add_region()
866 {
867 instrument_struct_to_be_changed_signal.emit(instrument);
868
869 region = instrument->AddRegion();
870 region->SetKeyRange(new_region_pos, new_region_pos);
871
872 instrument_struct_changed_signal.emit(instrument);
873 regions.update(instrument);
874
875 queue_draw();
876 region_selected();
877 dimensionManager.set_region(region);
878 instrument_changed();
879 }
880
881 void RegionChooser::delete_region()
882 {
883 instrument_struct_to_be_changed_signal.emit(instrument);
884 instrument->DeleteRegion(region);
885 instrument_struct_changed_signal.emit(instrument);
886 regions.update(instrument);
887
888 region = 0;
889 queue_draw();
890 region_selected();
891 dimensionManager.set_region(region);
892 instrument_changed();
893 }
894
895 void RegionChooser::manage_dimensions()
896 {
897 gig::Region* region = get_region();
898 if (!region) return;
899 dimensionManager.show(region);
900 }
901
902 void RegionChooser::on_dimension_manager_changed() {
903 region_selected();
904 instrument_changed();
905 }
906
907 sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_to_be_changed() {
908 return instrument_struct_to_be_changed_signal;
909 }
910
911 sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_changed() {
912 return instrument_struct_changed_signal;
913 }
914
915 sigc::signal<void, gig::Region*>& RegionChooser::signal_region_to_be_changed() {
916 return region_to_be_changed_signal;
917 }
918
919 sigc::signal<void, gig::Region*>& RegionChooser::signal_region_changed_signal() {
920 return region_changed_signal;
921 }
922
923 sigc::signal<void, int/*key*/, int/*velocity*/>& RegionChooser::signal_keyboard_key_hit() {
924 return keyboard_key_hit_signal;
925 }
926
927 sigc::signal<void, int/*key*/, int/*velocity*/>& RegionChooser::signal_keyboard_key_released() {
928 return keyboard_key_released_signal;
929 }

  ViewVC Help
Powered by ViewVC