/[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 2773 - (show annotations) (download)
Fri Jun 12 17:57:52 2015 UTC (8 years, 10 months ago) by schoenebeck
File size: 34004 byte(s)
* Region Chooser: when moving a region around automatically move the
  keyboard position dependent pitch accordingly (can be turned off with
  "Settings"->"Move root key with region moved").

1 /*
2 * Copyright (C) 2006-2015 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 #include "Settings.h"
33
34 #define REGION_BLOCK_HEIGHT 30
35 #define KEYBOARD_HEIGHT 40
36
37 void SortedRegions::update(gig::Instrument* instrument) {
38 // Usually, the regions in a gig file are ordered after their key
39 // range, but there are files where they are not. The
40 // RegionChooser code needs a sorted list of regions.
41 regions.clear();
42 if (instrument) {
43 for (gig::Region* r = instrument->GetFirstRegion() ;
44 r ;
45 r = instrument->GetNextRegion()) {
46 regions.push_back(r);
47 }
48 sort(regions.begin(), regions.end(), *this);
49 }
50 }
51
52 gig::Region* SortedRegions::first() {
53 region_iterator = regions.begin();
54 return region_iterator == regions.end() ? 0 : *region_iterator;
55 }
56
57 gig::Region* SortedRegions::next() {
58 region_iterator++;
59 return region_iterator == regions.end() ? 0 : *region_iterator;
60 }
61
62
63
64 RegionChooser::RegionChooser() :
65 activeKeyColor("red"),
66 red("#8070ff"),
67 grey1("grey69"),
68 white("white"),
69 black("black"),
70 m_VirtKeybModeChoice(_("Virtual Keyboard Mode")),
71 currentActiveKey(-1)
72 {
73 set_size_request(500, KEYBOARD_HEIGHT + REGION_BLOCK_HEIGHT);
74
75 instrument = 0;
76 region = 0;
77 resize.active = false;
78 move.active = false;
79 cursor_is_resize = false;
80 h1 = REGION_BLOCK_HEIGHT;
81
82 // properties of the virtual keyboard
83 {
84 const char* choices[] = { _("normal"), _("chord"), 0 };
85 static const virt_keyboard_mode_t values[] = {
86 VIRT_KEYBOARD_MODE_NORMAL,
87 VIRT_KEYBOARD_MODE_CHORD
88 };
89 m_VirtKeybModeChoice.set_choices(choices, values);
90 m_VirtKeybModeChoice.set_value(VIRT_KEYBOARD_MODE_NORMAL);
91 }
92 m_VirtKeybVelocityLabelDescr.set_text(_("Note-On Velocity:"));
93 m_VirtKeybVelocityLabel.set_text("-");
94 m_VirtKeybOffVelocityLabelDescr.set_text(_("Note-Off Velocity:"));
95 m_VirtKeybOffVelocityLabel.set_text("-");
96 m_VirtKeybPropsBox.pack_start(m_VirtKeybModeChoice.label, Gtk::PACK_SHRINK);
97 m_VirtKeybPropsBox.pack_start(m_VirtKeybModeChoice.widget, Gtk::PACK_SHRINK);
98 m_VirtKeybPropsBox.pack_start(m_VirtKeybVelocityLabelDescr, Gtk::PACK_SHRINK);
99 m_VirtKeybPropsBox.pack_start(m_VirtKeybVelocityLabel, Gtk::PACK_SHRINK);
100 m_VirtKeybPropsBox.pack_start(m_VirtKeybOffVelocityLabelDescr, Gtk::PACK_SHRINK);
101 m_VirtKeybPropsBox.pack_start(m_VirtKeybOffVelocityLabel, Gtk::PACK_SHRINK);
102 m_VirtKeybPropsBox.set_spacing(10);
103 m_VirtKeybPropsBox.show();
104 for (int i = 0 ; i < 128 ; i++) key_pressed[i] = false;
105
106 actionGroup = Gtk::ActionGroup::create();
107 actionGroup->add(Gtk::Action::create("Properties",
108 Gtk::Stock::PROPERTIES),
109 sigc::mem_fun(*this,
110 &RegionChooser::show_region_properties));
111 actionGroup->add(Gtk::Action::create("Remove", Gtk::Stock::REMOVE),
112 sigc::mem_fun(*this, &RegionChooser::delete_region));
113 actionGroup->add(Gtk::Action::create("Add", Gtk::Stock::ADD),
114 sigc::mem_fun(*this, &RegionChooser::add_region));
115 actionGroup->add(Gtk::Action::create("Dimensions", _("Dimensions...")),
116 sigc::mem_fun(*this, &RegionChooser::manage_dimensions));
117
118 uiManager = Gtk::UIManager::create();
119 uiManager->insert_action_group(actionGroup);
120 Glib::ustring ui_info =
121 "<ui>"
122 " <popup name='PopupMenuInsideRegion'>"
123 " <menuitem action='Properties'/>"
124 " <menuitem action='Dimensions'/>"
125 " <menuitem action='Remove'/>"
126 " </popup>"
127 " <popup name='PopupMenuOutsideRegion'>"
128 " <menuitem action='Add'/>"
129 " </popup>"
130 "</ui>";
131 uiManager->add_ui_from_string(ui_info);
132
133 popup_menu_inside_region = dynamic_cast<Gtk::Menu*>(
134 uiManager->get_widget("/PopupMenuInsideRegion"));
135 popup_menu_outside_region = dynamic_cast<Gtk::Menu*>(
136 uiManager->get_widget("/PopupMenuOutsideRegion"));
137
138 add_events(Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK |
139 Gdk::POINTER_MOTION_MASK | Gdk::POINTER_MOTION_HINT_MASK);
140
141 dimensionManager.region_to_be_changed_signal.connect(
142 region_to_be_changed_signal.make_slot()
143 );
144 dimensionManager.region_changed_signal.connect(
145 region_changed_signal.make_slot()
146 );
147 dimensionManager.region_changed_signal.connect(
148 sigc::hide(
149 sigc::mem_fun(*this, &RegionChooser::on_dimension_manager_changed)
150 )
151 );
152 keyboard_key_hit_signal.connect(
153 sigc::mem_fun(*this, &RegionChooser::on_note_on_event)
154 );
155 keyboard_key_released_signal.connect(
156 sigc::mem_fun(*this, &RegionChooser::on_note_off_event)
157 );
158 set_tooltip_text(_("Right click here for adding new region. Use mouse pointer for moving (dragging) or resizing existing regions (by pointing at region's boundary). Right click on an existing region for more actions."));
159 }
160
161 RegionChooser::~RegionChooser()
162 {
163 }
164
165 void RegionChooser::invalidate_key(int key) {
166 const int h = KEYBOARD_HEIGHT;
167 const int w = get_width() - 1;
168 int x1 = key_to_x(key - 0.5, w);
169 int x2 = key_to_x(key + 1.5, w);
170
171 Gdk::Rectangle rect(x1 + 1, h1 + 1, x2 - x1 - 1, h - 2);
172 get_window()->invalidate_rect(rect, false);
173 }
174
175 void RegionChooser::on_note_on_event(int key, int velocity) {
176 key_pressed[key] = true;
177 invalidate_key(key);
178 m_VirtKeybVelocityLabel.set_text(ToString(velocity));
179 }
180
181 void RegionChooser::on_note_off_event(int key, int velocity) {
182 key_pressed[key] = false;
183 invalidate_key(key);
184 m_VirtKeybOffVelocityLabel.set_text(ToString(velocity));
185 }
186
187
188 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
189 bool RegionChooser::on_expose_event(GdkEventExpose* e) {
190 double clipx1 = e->area.x;
191 double clipx2 = e->area.x + e->area.width;
192 double clipy1 = e->area.y;
193 double clipy2 = e->area.y + e->area.height;
194
195 const Cairo::RefPtr<Cairo::Context>& cr =
196 get_window()->create_cairo_context();
197 #if 0
198 }
199 #endif
200 #else
201 bool RegionChooser::on_draw(const Cairo::RefPtr<Cairo::Context>& cr) {
202 double clipx1, clipx2, clipy1, clipy2;
203 cr->get_clip_extents(clipx1, clipy1, clipx2, clipy2);
204 #endif
205
206 cr->save();
207 cr->set_line_width(1);
208
209 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
210 const Gdk::Color bg = get_style()->get_bg(Gtk::STATE_NORMAL);
211 #else
212 const Gdk::RGBA bg = get_style_context()->get_background_color();
213 #endif
214 Gdk::Cairo::set_source_rgba(cr, bg);
215 cr->paint();
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 // if there is no region yet, show the user some hint text that he may
336 // right click on this area to create a new region
337 if (!regions.first()) {
338 Glib::RefPtr<Pango::Context> context = get_pango_context();
339 Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create(context);
340 layout->set_alignment(Pango::ALIGN_CENTER);
341 layout->set_text(Glib::ustring("*** ") + _("Right click here to create a region.") + " ***");
342 layout->set_width(get_width() * Pango::SCALE);
343 //layout->set_height(get_height() * Pango::SCALE);
344 layout->set_spacing(10);
345 Gdk::Cairo::set_source_rgba(cr, red);
346 // get the text dimensions
347 Pango::Rectangle rect = layout->get_logical_extents();
348 int text_width, text_height;
349 layout->get_pixel_size(text_width, text_height);
350 cr->move_to(0, (REGION_BLOCK_HEIGHT - text_height) / 2);
351 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 16) || GTKMM_MAJOR_VERSION < 2
352 pango_cairo_show_layout(cr->cobj(), layout->gobj());
353 #else
354 layout->show_in_cairo_context(cr);
355 #endif
356 }
357 }
358
359 bool RegionChooser::is_black_key(int key) {
360 const int note = (key + 3) % 12;
361 return note == 1 || note == 4 || note == 6 || note == 9 || note == 11;
362 }
363
364 void RegionChooser::draw_digit(const Cairo::RefPtr<Cairo::Context>& cr,
365 int key) {
366 const int h = KEYBOARD_HEIGHT;
367 const int w = get_width() - 1;
368 Glib::RefPtr<Pango::Layout> layout =
369 Pango::Layout::create(get_pango_context());
370 char buf[30];
371 sprintf(buf, "<span size=\"8000\">%d</span>", key / 12 - 1);
372 layout->set_markup(buf);
373 Pango::Rectangle rectangle = layout->get_logical_extents();
374 double text_w = double(rectangle.get_width()) / Pango::SCALE;
375 double text_h = double(rectangle.get_height()) / Pango::SCALE;
376 double x = w * (key + 0.75) / 128.0;
377 Gdk::Cairo::set_source_rgba(cr, black);
378 cr->move_to(int(x - text_w / 2 + 1), int(h1 + h - text_h + 0.5));
379 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 16) || GTKMM_MAJOR_VERSION < 2
380 pango_cairo_show_layout(cr->cobj(), layout->gobj());
381 #else
382 layout->show_in_cairo_context(cr);
383 #endif
384 }
385
386 void RegionChooser::draw_key(const Cairo::RefPtr<Cairo::Context>& cr,
387 int key) {
388 const int h = KEYBOARD_HEIGHT;
389 const int w = get_width() - 1;
390 const int bh = int(h * 0.55);
391
392 Gdk::Cairo::set_source_rgba(cr, activeKeyColor);
393
394 int note = (key + 3) % 12;
395 int x = key_to_x(key, w) + 1;
396 int x2 = key_to_x(key + 1.5, w);
397 int x3 = key_to_x(key + 1, w);
398 int x4 = key_to_x(key - 0.5, w);
399 int w1 = x3 - x;
400 switch (note) {
401 case 0: case 5: case 10:
402 cr->rectangle(x, h1 + 1, w1, bh);
403 cr->fill();
404 cr->rectangle(x4 + 1, h1 + bh + 1, x2 - x4 - 1, h - bh - 2);
405 cr->fill();
406 break;
407 case 2: case 7:
408 cr->rectangle(x, h1 + 1, w1, bh);
409 cr->fill();
410 cr->rectangle(x4 + 1, h1 + bh + 1, x3 - x4 - 1, h - bh - 2);
411 cr->fill();
412 break;
413 case 3: case 8:
414 cr->rectangle(x, h1 + 1, w1, bh);
415 cr->fill();
416 cr->rectangle(x, h1 + bh + 1, x2 - x, h - bh - 2);
417 cr->fill();
418 break;
419 default:
420 cr->rectangle(x, h1 + 1, w1, bh - 1);
421 cr->fill();
422 break;
423 }
424 Gdk::Cairo::set_source_rgba(cr, black);
425 }
426
427 void RegionChooser::set_instrument(gig::Instrument* instrument)
428 {
429 this->instrument = instrument;
430 regions.update(instrument);
431 region = regions.first();
432 queue_draw();
433 region_selected();
434 dimensionManager.set_region(region);
435 }
436
437 bool RegionChooser::on_button_release_event(GdkEventButton* event)
438 {
439 const int k = x_to_key(event->x, get_width() - 1);
440
441 // handle-note off on virtual keyboard
442 if (event->type == GDK_BUTTON_RELEASE) {
443 int velocity = (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
444 int(float(event->y - REGION_BLOCK_HEIGHT) / float(KEYBOARD_HEIGHT) * 128.0f) + 1;
445 if (velocity <= 0) velocity = 1;
446 switch (m_VirtKeybModeChoice.get_value()) {
447 case VIRT_KEYBOARD_MODE_CHORD:
448 if (event->y >= REGION_BLOCK_HEIGHT)
449 keyboard_key_released_signal.emit(k, velocity);
450 break;
451 case VIRT_KEYBOARD_MODE_NORMAL:
452 default:
453 if (currentActiveKey >= 0 && currentActiveKey <= 127) {
454 keyboard_key_released_signal.emit(currentActiveKey, velocity);
455 currentActiveKey = -1;
456 }
457 break;
458 }
459 }
460
461 if (resize.active) {
462 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
463 get_window()->pointer_ungrab(event->time);
464 #else
465 Glib::wrap(event->device, true)->ungrab(event->time);
466 #endif
467 resize.active = false;
468
469 if (!is_in_resize_zone(event->x, event->y) && cursor_is_resize) {
470 get_window()->set_cursor();
471 cursor_is_resize = false;
472 }
473 } else if (move.active) {
474 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
475 get_window()->pointer_ungrab(event->time);
476 #else
477 Glib::wrap(event->device, true)->ungrab(event->time);
478 #endif
479 move.active = false;
480
481 if (is_in_resize_zone(event->x, event->y)) {
482 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
483 get_window()->set_cursor(Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW));
484 #else
485 get_window()->set_cursor(Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW));
486 #endif
487 cursor_is_resize = true;
488 }
489 }
490 return true;
491 }
492
493 void RegionChooser::update_after_resize()
494 {
495 if (resize.mode == resize.moving_high_limit) {
496 if (resize.region->KeyRange.high != resize.pos - 1) {
497 instrument_struct_to_be_changed_signal.emit(instrument);
498 resize.region->SetKeyRange(resize.region->KeyRange.low,
499 resize.pos - 1);
500 regions.update(instrument);
501 instrument_changed.emit();
502 instrument_struct_changed_signal.emit(instrument);
503 }
504 } else if (resize.mode == resize.moving_low_limit) {
505 if (resize.region->KeyRange.low != resize.pos) {
506 instrument_struct_to_be_changed_signal.emit(instrument);
507 resize.region->SetKeyRange(resize.pos,
508 resize.region->KeyRange.high);
509 regions.update(instrument);
510 instrument_changed.emit();
511 instrument_struct_changed_signal.emit(instrument);
512 }
513 }
514 }
515
516 void RegionChooser::update_after_move(int pos)
517 {
518 instrument_struct_to_be_changed_signal.emit(instrument);
519 const int range = region->KeyRange.high - region->KeyRange.low;
520 const int diff = pos - int(region->KeyRange.low);
521 region->SetKeyRange(pos, pos + range);
522 if (Settings::singleton()->moveRootNoteWithRegionMoved) {
523 for (int i = 0; i < 256; ++i) {
524 gig::DimensionRegion* dimrgn = region->pDimensionRegions[i];
525 if (!dimrgn || !dimrgn->pSample || !dimrgn->PitchTrack) continue;
526 dimrgn->UnityNote += diff;
527 }
528 }
529 regions.update(instrument);
530 instrument_changed.emit();
531 instrument_struct_changed_signal.emit(instrument);
532 }
533
534 bool RegionChooser::on_button_press_event(GdkEventButton* event)
535 {
536 if (!instrument) return true;
537
538 const int w = get_width() - 1;
539 const int k = x_to_key(event->x, w);
540
541 if (event->type == GDK_BUTTON_PRESS) {
542 if (event->y >= REGION_BLOCK_HEIGHT) {
543 int velocity = (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
544 int(float(event->y - REGION_BLOCK_HEIGHT) / float(KEYBOARD_HEIGHT) * 128.0f) + 1;
545 currentActiveKey = k;
546 keyboard_key_hit_signal.emit(k, velocity);
547 }
548 }
549
550 // left mouse button double click
551 if (event->type == GDK_2BUTTON_PRESS && event->button == 1) {
552 if (event->y < REGION_BLOCK_HEIGHT) {
553 // show dimension manager dialog for this region
554 manage_dimensions();
555 }
556 }
557
558 if (event->y >= REGION_BLOCK_HEIGHT) return true;
559 if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
560 gig::Region* r = get_region(k);
561 if (r) {
562 region = r;
563 queue_draw();
564 region_selected();
565 dimensionManager.set_region(region);
566 popup_menu_inside_region->popup(event->button, event->time);
567 } else {
568 new_region_pos = k;
569 popup_menu_outside_region->popup(event->button, event->time);
570 }
571 } else {
572 if (is_in_resize_zone(event->x, event->y)) {
573 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
574 get_window()->pointer_grab(false,
575 Gdk::BUTTON_RELEASE_MASK |
576 Gdk::POINTER_MOTION_MASK |
577 Gdk::POINTER_MOTION_HINT_MASK,
578 Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW),
579 event->time);
580 #else
581 Glib::wrap(event->device, true)->grab(get_window(),
582 Gdk::OWNERSHIP_NONE,
583 false,
584 Gdk::BUTTON_RELEASE_MASK |
585 Gdk::POINTER_MOTION_MASK |
586 Gdk::POINTER_MOTION_HINT_MASK,
587 Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW),
588 event->time);
589 #endif
590 resize.active = true;
591 } else {
592 gig::Region* r = get_region(k);
593 if (r) {
594 region = r;
595 queue_draw();
596 region_selected();
597 dimensionManager.set_region(region);
598
599 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
600 get_window()->pointer_grab(false,
601 Gdk::BUTTON_RELEASE_MASK |
602 Gdk::POINTER_MOTION_MASK |
603 Gdk::POINTER_MOTION_HINT_MASK,
604 Gdk::Cursor(Gdk::FLEUR),
605 event->time);
606 #else
607 Glib::wrap(event->device, true)->grab(get_window(),
608 Gdk::OWNERSHIP_NONE,
609 false,
610 Gdk::BUTTON_RELEASE_MASK |
611 Gdk::POINTER_MOTION_MASK |
612 Gdk::POINTER_MOTION_HINT_MASK,
613 Gdk::Cursor::create(Gdk::FLEUR),
614 event->time);
615 #endif
616 move.active = true;
617 move.offset = event->x - key_to_x(region->KeyRange.low, w);
618 }
619 }
620 }
621 return true;
622 }
623
624 gig::Region* RegionChooser::get_region(int key)
625 {
626 for (gig::Region* r = regions.first() ; r ; r = regions.next()) {
627 if (key < r->KeyRange.low) return 0;
628 if (key <= r->KeyRange.high) return r;
629 }
630 return 0;
631 }
632
633 void RegionChooser::set_region(gig::Region* region) {
634 this->region = region;
635 queue_draw();
636 region_selected();
637 dimensionManager.set_region(region);
638 }
639
640 void RegionChooser::motion_resize_region(int x, int y)
641 {
642 const int w = get_width() - 1;
643
644 int k = int(double(x) / w * 128.0 + 0.5);
645
646 if (k < resize.min) k = resize.min;
647 else if (k > resize.max) k = resize.max;
648
649 if (k != resize.pos) {
650 if (resize.mode == resize.undecided) {
651 if (k < resize.pos) {
652 // edit high limit of prev_region
653 resize.max = resize.region->KeyRange.low;
654 resize.region = resize.prev_region;
655 resize.mode = resize.moving_high_limit;
656 } else {
657 // edit low limit of region
658 resize.min = resize.prev_region->KeyRange.high + 1;
659 resize.mode = resize.moving_low_limit;
660 }
661 }
662 resize.pos = k;
663
664 int x1, x2;
665 if (resize.mode == resize.moving_high_limit) {
666 if (resize.region->KeyRange.high < resize.pos - 1) {
667 x1 = resize.region->KeyRange.high;
668 x2 = resize.pos - 1;
669 } else {
670 x1 = resize.pos - 1;
671 x2 = resize.region->KeyRange.high;
672 }
673 } else {
674 if (resize.region->KeyRange.low < resize.pos) {
675 x1 = resize.region->KeyRange.low;
676 x2 = resize.pos;
677 } else {
678 x1 = resize.pos;
679 x2 = resize.region->KeyRange.low;
680 }
681 }
682 x1 = key_to_x(x1, w);
683 x2 = key_to_x(x2 + 1, w) + 1;
684 Gdk::Rectangle rect(x1, 0, x2 - x1, h1);
685
686 update_after_resize();
687
688 get_window()->invalidate_rect(rect, false);
689 }
690 }
691
692 void RegionChooser::motion_move_region(int x, int y)
693 {
694 const int w = get_width() - 1;
695
696 int l = int(double(x - move.offset) / w * 128.0 + 0.5);
697
698 if (l == region->KeyRange.low) return;
699 int new_l;
700 int regionsize = region->KeyRange.high - region->KeyRange.low;
701 int a = 0;
702 if (l > region->KeyRange.low) {
703 for (gig::Region* r = regions.first() ; ; r = regions.next()) {
704 if (r != region) {
705 int b = r ? r->KeyRange.low : 128;
706
707 // gap: from a to b (not inclusive b)
708
709 if (region->KeyRange.high >= b) {
710 // not found the current gap yet, just continue
711 } else {
712
713 if (a > l) {
714 // this gap is too far to the right, break
715 break;
716 }
717
718 int newhigh = std::min(l + regionsize, b - 1);
719 int newlo = newhigh - regionsize;
720
721 if (newlo >= a) {
722 // yes it fits - it's a candidate
723 new_l = newlo;
724 }
725 }
726 if (!r) break;
727 a = r->KeyRange.high + 1;
728 }
729 }
730 } else {
731 for (gig::Region* r = regions.first() ; ; r = regions.next()) {
732 if (r != region) {
733 int b = r ? r->KeyRange.low : 128;
734
735 // gap from a to b (not inclusive b)
736
737 if (l + regionsize >= b) {
738 // not found the current gap yet, just continue
739 } else {
740
741 if (a > region->KeyRange.low) {
742 // this gap is too far to the right, break
743 break;
744 }
745
746 int newlo = std::max(l, a);
747 int newhigh = newlo + regionsize;
748
749 if (newhigh < b) {
750 // yes it fits - break as the first one is the best
751 new_l = newlo;
752 break;
753 }
754 }
755 if (!r) break;
756 a = r->KeyRange.high + 1;
757 }
758 }
759 }
760 if (new_l == region->KeyRange.low) return;
761
762 int x1 = key_to_x(std::min(int(region->KeyRange.low), new_l), w);
763 int x2 = key_to_x(std::max(int(region->KeyRange.high),
764 new_l + regionsize) + 1, w) + 1;
765
766 Gdk::Rectangle rect(x1, 0, x2 - x1, h1);
767 update_after_move(new_l);
768
769 get_window()->invalidate_rect(rect, false);
770 }
771
772
773 bool RegionChooser::on_motion_notify_event(GdkEventMotion* event)
774 {
775 Glib::RefPtr<Gdk::Window> window = get_window();
776 int x, y;
777 Gdk::ModifierType state = Gdk::ModifierType(0);
778 window->get_pointer(x, y, state);
779
780 // handle virtual MIDI keyboard
781 if (m_VirtKeybModeChoice.get_value() != VIRT_KEYBOARD_MODE_CHORD &&
782 currentActiveKey > 0 &&
783 event->y >= REGION_BLOCK_HEIGHT &&
784 event->y < REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT)
785 {
786 const int k = x_to_key(event->x, get_width() - 1);
787 if (k != currentActiveKey) {
788 int velocity =
789 (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
790 int(float(event->y - REGION_BLOCK_HEIGHT) /
791 float(KEYBOARD_HEIGHT) * 128.0f) + 1;
792 if (velocity <= 0) velocity = 1;
793 keyboard_key_released_signal.emit(currentActiveKey, velocity);
794 currentActiveKey = k;
795 keyboard_key_hit_signal.emit(k, velocity);
796 }
797 }
798
799 if (resize.active) {
800 motion_resize_region(x, y);
801 } else if (move.active) {
802 motion_move_region(x, y);
803 } else {
804 if (is_in_resize_zone(x, y)) {
805 if (!cursor_is_resize) {
806 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
807 window->set_cursor(Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW));
808 #else
809 window->set_cursor(Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW));
810 #endif
811 cursor_is_resize = true;
812 }
813 } else if (cursor_is_resize) {
814 window->set_cursor();
815 cursor_is_resize = false;
816 }
817 }
818
819 return true;
820 }
821
822 bool RegionChooser::is_in_resize_zone(double x, double y) {
823 const int w = get_width() - 1;
824
825 if (instrument && y >= 0 && y <= h1) {
826 gig::Region* prev_region = 0;
827 gig::Region* next_region;
828 for (gig::Region* r = regions.first(); r ; r = next_region) {
829 next_region = regions.next();
830
831 int lo = key_to_x(r->KeyRange.low, w);
832 if (x <= lo - 2) break;
833 if (x < lo + 2) {
834 resize.region = r;
835 resize.pos = r->KeyRange.low;
836 resize.max = r->KeyRange.high;
837
838 if (prev_region && prev_region->KeyRange.high + 1 == r->KeyRange.low) {
839 // we don't know yet if it's the high limit of
840 // prev_region or the low limit of r that's going
841 // to be edited
842 resize.mode = resize.undecided;
843 resize.min = prev_region->KeyRange.low + 1;
844 resize.prev_region = prev_region;
845 return resize.min != resize.max;
846 }
847
848 // edit low limit
849 resize.mode = resize.moving_low_limit;
850 resize.min = prev_region ? prev_region->KeyRange.high + 1 : 0;
851 return resize.min != resize.max;
852 }
853 if (!next_region || r->KeyRange.high + 1 != next_region->KeyRange.low) {
854 int hi = key_to_x(r->KeyRange.high + 1, w);
855 if (x <= hi - 2) break;
856 if (x < hi + 2) {
857 // edit high limit
858 resize.region = r;
859 resize.pos = r->KeyRange.high + 1;
860 resize.mode = resize.moving_high_limit;
861 resize.min = r->KeyRange.low + 1;
862 resize.max = next_region ? next_region->KeyRange.low : 128;
863 return resize.min != resize.max;
864 }
865 }
866 prev_region = r;
867 }
868 }
869 return false;
870 }
871
872 sigc::signal<void>& RegionChooser::signal_region_selected()
873 {
874 return region_selected;
875 }
876
877 sigc::signal<void>& RegionChooser::signal_instrument_changed()
878 {
879 return instrument_changed;
880 }
881
882 void RegionChooser::show_region_properties()
883 {
884 if (!region) return;
885 Gtk::Dialog dialog(_("Region Properties"), true /*modal*/);
886 // add "Keygroup" checkbox
887 Gtk::CheckButton checkBoxKeygroup(_("Member of a Keygroup (Exclusive Group)"));
888 checkBoxKeygroup.set_active(region->KeyGroup);
889 dialog.get_vbox()->pack_start(checkBoxKeygroup);
890 checkBoxKeygroup.show();
891 // add "Keygroup" spinbox
892 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
893 Gtk::Adjustment adjustment(1, 1, 999);
894 Gtk::SpinButton spinBox(adjustment);
895 #else
896 Gtk::SpinButton spinBox(Gtk::Adjustment::create(1, 1, 999));
897 #endif
898 if (region->KeyGroup) spinBox.set_value(region->KeyGroup);
899 dialog.get_vbox()->pack_start(spinBox);
900 spinBox.show();
901 // add OK and CANCEL buttons to the dialog
902 dialog.add_button(Gtk::Stock::OK, 0);
903 dialog.add_button(Gtk::Stock::CANCEL, 1);
904 dialog.show_all_children();
905 if (!dialog.run()) { // OK selected ...
906 region->KeyGroup =
907 (checkBoxKeygroup.get_active()) ? spinBox.get_value_as_int() : 0;
908 }
909 }
910
911 void RegionChooser::add_region()
912 {
913 instrument_struct_to_be_changed_signal.emit(instrument);
914
915 region = instrument->AddRegion();
916 region->SetKeyRange(new_region_pos, new_region_pos);
917
918 instrument_struct_changed_signal.emit(instrument);
919 regions.update(instrument);
920
921 queue_draw();
922 region_selected();
923 dimensionManager.set_region(region);
924 instrument_changed();
925 }
926
927 void RegionChooser::delete_region()
928 {
929 instrument_struct_to_be_changed_signal.emit(instrument);
930 instrument->DeleteRegion(region);
931 instrument_struct_changed_signal.emit(instrument);
932 regions.update(instrument);
933
934 region = 0;
935 queue_draw();
936 region_selected();
937 dimensionManager.set_region(region);
938 instrument_changed();
939 }
940
941 void RegionChooser::manage_dimensions()
942 {
943 gig::Region* region = get_region();
944 if (!region) return;
945 dimensionManager.show(region);
946 }
947
948 void RegionChooser::on_dimension_manager_changed() {
949 region_selected();
950 instrument_changed();
951 }
952
953 sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_to_be_changed() {
954 return instrument_struct_to_be_changed_signal;
955 }
956
957 sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_changed() {
958 return instrument_struct_changed_signal;
959 }
960
961 sigc::signal<void, gig::Region*>& RegionChooser::signal_region_to_be_changed() {
962 return region_to_be_changed_signal;
963 }
964
965 sigc::signal<void, gig::Region*>& RegionChooser::signal_region_changed_signal() {
966 return region_changed_signal;
967 }
968
969 sigc::signal<void, int/*key*/, int/*velocity*/>& RegionChooser::signal_keyboard_key_hit() {
970 return keyboard_key_hit_signal;
971 }
972
973 sigc::signal<void, int/*key*/, int/*velocity*/>& RegionChooser::signal_keyboard_key_released() {
974 return keyboard_key_released_signal;
975 }

  ViewVC Help
Powered by ViewVC