/[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 3131 - (show annotations) (download)
Thu Apr 27 17:28:01 2017 UTC (6 years, 11 months ago) by schoenebeck
File size: 37566 byte(s)
* Color tweaking on selected regions and selected dimension zones.
* Dimension Region Chooser: Show main selected case in opaque blue color
  (like before), but all other selected dimension zones with a blue hatched
  pattern instead.
* Dimension Region Chooser: Show the label of the currently selected
  dimension in bold font.
* Fix: Keyboard accelerator Ctrl+Left and Ctrl+Right caused the current
  dimension zone to switch as well if Dimension Region Chooser got focus.

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 <gdkmm/pixbuf.h>
28 #include <gtkmm/spinbutton.h>
29 #include <gtkmm/dialog.h>
30
31 #include "global.h"
32 #include "Settings.h"
33 #include "gfx/builtinpix.h"
34
35 #define REGION_BLOCK_HEIGHT 30
36 #define KEYBOARD_HEIGHT 40
37
38 struct RegionFeatures {
39 int sampleRefs;
40 int loops;
41
42 RegionFeatures() {
43 sampleRefs = loops = 0;
44 }
45 };
46
47 static RegionFeatures regionFeatures(gig::Region* rgn) {
48 RegionFeatures f;
49 for (int i = 0; i < rgn->DimensionRegions; ++i) {
50 gig::DimensionRegion* dr = rgn->pDimensionRegions[i];
51 if (dr->pSample) f.sampleRefs++;
52 // the user doesn't care about loop if there is no valid sample reference
53 if (dr->pSample && dr->SampleLoops) f.loops++;
54 }
55 return f;
56 }
57
58 void SortedRegions::update(gig::Instrument* instrument) {
59 // Usually, the regions in a gig file are ordered after their key
60 // range, but there are files where they are not. The
61 // RegionChooser code needs a sorted list of regions.
62 regions.clear();
63 if (instrument) {
64 for (gig::Region* r = instrument->GetFirstRegion() ;
65 r ;
66 r = instrument->GetNextRegion()) {
67 regions.push_back(r);
68 }
69 sort(regions.begin(), regions.end(), *this);
70 }
71 }
72
73 gig::Region* SortedRegions::first() {
74 region_iterator = regions.begin();
75 return region_iterator == regions.end() ? 0 : *region_iterator;
76 }
77
78 gig::Region* SortedRegions::next() {
79 ++region_iterator;
80 return region_iterator == regions.end() ? 0 : *region_iterator;
81 }
82
83
84
85 RegionChooser::RegionChooser() :
86 activeKeyColor("red"),
87 blue("#4796ff"),
88 grey1("grey69"),
89 white("white"),
90 black("black"),
91 m_VirtKeybModeChoice(_("Virtual Keyboard Mode")),
92 currentActiveKey(-1)
93 {
94 set_size_request(500, KEYBOARD_HEIGHT + REGION_BLOCK_HEIGHT);
95
96 loadBuiltInPix();
97
98 instrument = 0;
99 region = 0;
100 resize.active = false;
101 move.active = false;
102 cursor_is_resize = false;
103 h1 = REGION_BLOCK_HEIGHT;
104
105 // properties of the virtual keyboard
106 {
107 const char* choices[] = { _("normal"), _("chord"), 0 };
108 static const virt_keyboard_mode_t values[] = {
109 VIRT_KEYBOARD_MODE_NORMAL,
110 VIRT_KEYBOARD_MODE_CHORD
111 };
112 m_VirtKeybModeChoice.set_choices(choices, values);
113 m_VirtKeybModeChoice.set_value(VIRT_KEYBOARD_MODE_NORMAL);
114 }
115 m_VirtKeybVelocityLabelDescr.set_text(_("Note-On Velocity:"));
116 m_VirtKeybVelocityLabel.set_text("-");
117 m_VirtKeybOffVelocityLabelDescr.set_text(_("Note-Off Velocity:"));
118 m_VirtKeybOffVelocityLabel.set_text("-");
119 m_VirtKeybPropsBox.pack_start(m_VirtKeybModeChoice.label, Gtk::PACK_SHRINK);
120 m_VirtKeybPropsBox.pack_start(m_VirtKeybModeChoice.widget, Gtk::PACK_SHRINK);
121 m_VirtKeybPropsBox.pack_start(m_VirtKeybVelocityLabelDescr, Gtk::PACK_SHRINK);
122 m_VirtKeybPropsBox.pack_start(m_VirtKeybVelocityLabel, Gtk::PACK_SHRINK);
123 m_VirtKeybPropsBox.pack_start(m_VirtKeybOffVelocityLabelDescr, Gtk::PACK_SHRINK);
124 m_VirtKeybPropsBox.pack_start(m_VirtKeybOffVelocityLabel, Gtk::PACK_SHRINK);
125 m_VirtKeybPropsBox.set_spacing(10);
126 m_VirtKeybPropsBox.show();
127 for (int i = 0 ; i < 128 ; i++) key_pressed[i] = false;
128
129 actionGroup = Gtk::ActionGroup::create();
130 actionGroup->add(Gtk::Action::create("Properties", _("_Properties")),
131 sigc::mem_fun(*this,
132 &RegionChooser::show_region_properties));
133 actionGroup->add(Gtk::Action::create("Remove", _("_Remove")),
134 sigc::mem_fun(*this, &RegionChooser::delete_region));
135 actionGroup->add(Gtk::Action::create("Add", _("_Add")),
136 sigc::mem_fun(*this, &RegionChooser::add_region));
137 actionGroup->add(Gtk::Action::create("Dimensions", _("Dimensions...")),
138 sigc::mem_fun(*this, &RegionChooser::manage_dimensions));
139
140 uiManager = Gtk::UIManager::create();
141 uiManager->insert_action_group(actionGroup);
142 Glib::ustring ui_info =
143 "<ui>"
144 " <popup name='PopupMenuInsideRegion'>"
145 " <menuitem action='Properties'/>"
146 " <menuitem action='Dimensions'/>"
147 " <menuitem action='Remove'/>"
148 " </popup>"
149 " <popup name='PopupMenuOutsideRegion'>"
150 " <menuitem action='Add'/>"
151 " </popup>"
152 "</ui>";
153 uiManager->add_ui_from_string(ui_info);
154
155 popup_menu_inside_region = dynamic_cast<Gtk::Menu*>(
156 uiManager->get_widget("/PopupMenuInsideRegion"));
157 popup_menu_outside_region = dynamic_cast<Gtk::Menu*>(
158 uiManager->get_widget("/PopupMenuOutsideRegion"));
159
160 add_events(Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK |
161 Gdk::POINTER_MOTION_MASK | Gdk::POINTER_MOTION_HINT_MASK);
162
163 dimensionManager.region_to_be_changed_signal.connect(
164 region_to_be_changed_signal.make_slot()
165 );
166 dimensionManager.region_changed_signal.connect(
167 region_changed_signal.make_slot()
168 );
169 dimensionManager.region_changed_signal.connect(
170 sigc::hide(
171 sigc::mem_fun(*this, &RegionChooser::on_dimension_manager_changed)
172 )
173 );
174 keyboard_key_hit_signal.connect(
175 sigc::mem_fun(*this, &RegionChooser::on_note_on_event)
176 );
177 keyboard_key_released_signal.connect(
178 sigc::mem_fun(*this, &RegionChooser::on_note_off_event)
179 );
180 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."));
181 }
182
183 RegionChooser::~RegionChooser()
184 {
185 }
186
187 void RegionChooser::invalidate_key(int key) {
188 const int h = KEYBOARD_HEIGHT;
189 const int w = get_width() - 1;
190 int x1 = key_to_x(key - 0.5, w);
191 int x2 = key_to_x(key + 1.5, w);
192
193 Gdk::Rectangle rect(x1 + 1, h1 + 1, x2 - x1 - 1, h - 2);
194 get_window()->invalidate_rect(rect, false);
195 }
196
197 void RegionChooser::on_note_on_event(int key, int velocity) {
198 key_pressed[key] = true;
199 invalidate_key(key);
200 m_VirtKeybVelocityLabel.set_text(ToString(velocity));
201 }
202
203 void RegionChooser::on_note_off_event(int key, int velocity) {
204 key_pressed[key] = false;
205 invalidate_key(key);
206 m_VirtKeybOffVelocityLabel.set_text(ToString(velocity));
207 }
208
209
210 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
211 bool RegionChooser::on_expose_event(GdkEventExpose* e) {
212 double clipx1 = e->area.x;
213 double clipx2 = e->area.x + e->area.width;
214 double clipy1 = e->area.y;
215 double clipy2 = e->area.y + e->area.height;
216
217 const Cairo::RefPtr<Cairo::Context>& cr =
218 get_window()->create_cairo_context();
219 #if 0
220 }
221 #endif
222 #else
223 bool RegionChooser::on_draw(const Cairo::RefPtr<Cairo::Context>& cr) {
224 double clipx1, clipx2, clipy1, clipy2;
225 cr->get_clip_extents(clipx1, clipy1, clipx2, clipy2);
226 #endif
227
228 cr->save();
229 cr->set_line_width(1);
230
231 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
232 const Gdk::Color bg = get_style()->get_bg(Gtk::STATE_NORMAL);
233 #else
234 const Gdk::RGBA bg = get_style_context()->get_background_color();
235 #endif
236 Gdk::Cairo::set_source_rgba(cr, bg);
237 cr->paint();
238
239 if (clipy2 > h1) {
240 draw_keyboard(cr, clipx1, clipx2);
241 }
242
243 if (clipy1 < h1 && instrument) {
244 draw_regions(cr, clipx1, clipx2);
245 }
246
247 cr->restore();
248
249 return true;
250 }
251
252 void RegionChooser::draw_keyboard(const Cairo::RefPtr<Cairo::Context>& cr,
253 int clip_low, int clip_high) {
254 const int h = KEYBOARD_HEIGHT;
255 const int w = get_width() - 1;
256 const int bh = int(h * 0.55);
257
258 Gdk::Cairo::set_source_rgba(cr, black);
259 cr->rectangle(0.5, h1 + 0.5, w, h - 1);
260 cr->stroke();
261
262 int x1 = key_to_x(20.5, w);
263 Gdk::Cairo::set_source_rgba(cr, grey1);
264 cr->rectangle(1, h1 + 1, x1 - 1, h - 2);
265 cr->fill();
266
267 int x2 = key_to_x(109.5, w);
268 Gdk::Cairo::set_source_rgba(cr, white);
269 cr->rectangle(x1 + 1, h1 + 1, x2 - x1 - 1, h - 2);
270 cr->fill();
271
272 Gdk::Cairo::set_source_rgba(cr, grey1);
273 cr->rectangle(x2 + 1, h1 + 1, w - x2 - 1, h - 2);
274 cr->fill();
275
276 Gdk::Cairo::set_source_rgba(cr, black);
277
278 int clipkey1 = std::max(0, x_to_key_right(clip_low - 1, w));
279 int clipkey2 = std::min(x_to_key_right(clip_high - 1, w) + 1, 128);
280
281 for (int i = clipkey1 ; i < clipkey2 ; i++) {
282 int note = (i + 3) % 12;
283 int x = key_to_x(i, w);
284
285 if (note == 1 || note == 4 || note == 6 ||
286 note == 9 || note == 11) {
287 // black key: short line in the middle, with a rectangle
288 // on top
289 int x2 = key_to_x(i + 0.5, w);
290 cr->move_to(x2 + 0.5, h1 + bh + 0.5);
291 cr->line_to(x2 + 0.5, h1 + h - 1);
292 cr->stroke();
293
294 int x3 = key_to_x(i + 1, w);
295 cr->rectangle(x, h1 + 1, x3 - x + 1, bh);
296 cr->fill();
297 } else if (note == 3 || note == 8) {
298 // C or F: long line to the left
299 cr->move_to(x + 0.5, h1 + 1);
300 cr->line_to(x + 0.5, h1 + h - 1);
301 cr->stroke();
302 }
303
304 if (key_pressed[i]) draw_key(cr, i);
305
306 if (note == 3) draw_digit(cr, i);
307 }
308 }
309
310
311 void RegionChooser::draw_regions(const Cairo::RefPtr<Cairo::Context>& cr,
312 int clip_low, int clip_high) {
313 const int w = get_width() - 1;
314
315 Gdk::Cairo::set_source_rgba(cr, black);
316 gig::Region* next_region;
317 int x3 = -1;
318 for (gig::Region* r = regions.first() ; r ; r = next_region) {
319 next_region = regions.next();
320
321 if (x3 < 0) {
322 x3 = key_to_x(r->KeyRange.low, w);
323 if (x3 >= clip_high) break;
324 }
325 if (!next_region ||
326 r->KeyRange.high + 1 != next_region->KeyRange.low ||
327 r == region || next_region == region) {
328
329 int x2 = key_to_x(r->KeyRange.high + 1, w);
330 if (x2 >= clip_low) {
331 cr->move_to(x3, 0.5);
332 cr->line_to(x2 + 0.5, 0.5);
333 cr->line_to(x2 + 0.5, h1 - 0.5);
334 cr->line_to(x3, h1 - 0.5);
335 cr->stroke();
336
337 Gdk::Cairo::set_source_rgba(cr, region == r ? blue : white);
338 cr->rectangle(x3 + 1, 1, x2 - x3 - 1, h1 - 2);
339 cr->fill();
340 Gdk::Cairo::set_source_rgba(cr, black);
341 }
342 x3 = -1;
343 }
344 }
345
346 for (gig::Region* r = regions.first() ; r ; r = regions.next()) {
347 int x = key_to_x(r->KeyRange.low, w);
348 int x2 = key_to_x(r->KeyRange.high + 1, w);
349
350 RegionFeatures features = regionFeatures(r);
351
352 const bool bShowLoopSymbol = features.loops > 0;
353 const bool bShowSampleRefSymbol = features.sampleRefs < r->DimensionRegions;
354 if (bShowLoopSymbol || bShowSampleRefSymbol) {
355 const int margin = 2;
356 const int wRgn = x2 - x;
357 //printf("x=%d x2=%d wRgn=%d\n", x, x2, wRgn);
358
359 cr->save();
360 cr->set_line_width(1);
361 cr->rectangle(x, 1, wRgn, h1 - 1);
362 cr->clip();
363 if (bShowSampleRefSymbol) {
364 const int wPic = 8;
365 const int hPic = 8;
366 Gdk::Cairo::set_source_pixbuf(
367 cr, (features.sampleRefs) ? yellowDot : redDot,
368 x + (wRgn-wPic)/2.f,
369 (bShowLoopSymbol) ? margin : (h1-hPic)/2.f
370 );
371 cr->paint();
372 }
373 if (bShowLoopSymbol) {
374 const int wPic = 12;
375 const int hPic = 14;
376 Gdk::Cairo::set_source_pixbuf(
377 cr, (features.loops == r->DimensionRegions) ? blackLoop : grayLoop,
378 x + (wRgn-wPic)/2.f,
379 (bShowSampleRefSymbol) ? h1 - hPic - margin : (h1-hPic)/2.f
380 );
381 cr->paint();
382 }
383 cr->restore();
384 }
385 }
386
387 for (gig::Region* r = regions.first() ; r ; r = regions.next()) {
388 int x = key_to_x(r->KeyRange.low, w);
389
390 if (x < clip_low) continue;
391 if (x >= clip_high) break;
392
393 cr->move_to(x + 0.5, 1);
394 cr->line_to(x + 0.5, h1 - 1);
395 cr->stroke();
396 }
397
398 // if there is no region yet, show the user some hint text that he may
399 // right click on this area to create a new region
400 if (!regions.first()) {
401 Glib::RefPtr<Pango::Context> context = get_pango_context();
402 Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create(context);
403 layout->set_alignment(Pango::ALIGN_CENTER);
404 layout->set_text(Glib::ustring("*** ") + _("Right click here to create a region.") + " ***");
405 layout->set_width(get_width() * Pango::SCALE);
406 //layout->set_height(get_height() * Pango::SCALE);
407 layout->set_spacing(10);
408 Gdk::Cairo::set_source_rgba(cr, blue);
409 // get the text dimensions
410 int text_width, text_height;
411 layout->get_pixel_size(text_width, text_height);
412 cr->move_to(0, (REGION_BLOCK_HEIGHT - text_height) / 2);
413 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 16) || GTKMM_MAJOR_VERSION < 2
414 pango_cairo_show_layout(cr->cobj(), layout->gobj());
415 #else
416 layout->show_in_cairo_context(cr);
417 #endif
418 }
419 }
420
421 bool RegionChooser::is_black_key(int key) {
422 const int note = (key + 3) % 12;
423 return note == 1 || note == 4 || note == 6 || note == 9 || note == 11;
424 }
425
426 void RegionChooser::draw_digit(const Cairo::RefPtr<Cairo::Context>& cr,
427 int key) {
428 const int h = KEYBOARD_HEIGHT;
429 const int w = get_width() - 1;
430 Glib::RefPtr<Pango::Layout> layout =
431 Pango::Layout::create(get_pango_context());
432 char buf[30];
433 sprintf(buf, "<span size=\"8000\">%d</span>", key / 12 - 1);
434 layout->set_markup(buf);
435 Pango::Rectangle rectangle = layout->get_logical_extents();
436 double text_w = double(rectangle.get_width()) / Pango::SCALE;
437 double text_h = double(rectangle.get_height()) / Pango::SCALE;
438 double x = w * (key + 0.75) / 128.0;
439 Gdk::Cairo::set_source_rgba(cr, black);
440 cr->move_to(int(x - text_w / 2 + 1), int(h1 + h - text_h + 0.5));
441 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 16) || GTKMM_MAJOR_VERSION < 2
442 pango_cairo_show_layout(cr->cobj(), layout->gobj());
443 #else
444 layout->show_in_cairo_context(cr);
445 #endif
446 }
447
448 void RegionChooser::draw_key(const Cairo::RefPtr<Cairo::Context>& cr,
449 int key) {
450 const int h = KEYBOARD_HEIGHT;
451 const int w = get_width() - 1;
452 const int bh = int(h * 0.55);
453
454 Gdk::Cairo::set_source_rgba(cr, activeKeyColor);
455
456 int note = (key + 3) % 12;
457 int x = key_to_x(key, w) + 1;
458 int x2 = key_to_x(key + 1.5, w);
459 int x3 = key_to_x(key + 1, w);
460 int x4 = key_to_x(key - 0.5, w);
461 int w1 = x3 - x;
462 switch (note) {
463 case 0: case 5: case 10:
464 cr->rectangle(x, h1 + 1, w1, bh);
465 cr->fill();
466 cr->rectangle(x4 + 1, h1 + bh + 1, x2 - x4 - 1, h - bh - 2);
467 cr->fill();
468 break;
469 case 2: case 7:
470 cr->rectangle(x, h1 + 1, w1, bh);
471 cr->fill();
472 cr->rectangle(x4 + 1, h1 + bh + 1, x3 - x4 - 1, h - bh - 2);
473 cr->fill();
474 break;
475 case 3: case 8:
476 cr->rectangle(x, h1 + 1, w1, bh);
477 cr->fill();
478 cr->rectangle(x, h1 + bh + 1, x2 - x, h - bh - 2);
479 cr->fill();
480 break;
481 default:
482 cr->rectangle(x, h1 + 1, w1, bh - 1);
483 cr->fill();
484 break;
485 }
486 Gdk::Cairo::set_source_rgba(cr, black);
487 }
488
489 void RegionChooser::set_instrument(gig::Instrument* instrument)
490 {
491 this->instrument = instrument;
492 regions.update(instrument);
493 region = regions.first();
494 queue_draw();
495 region_selected();
496 dimensionManager.set_region(region);
497 }
498
499 bool RegionChooser::on_button_release_event(GdkEventButton* event)
500 {
501 const int k = x_to_key(event->x, get_width() - 1);
502
503 // handle-note off on virtual keyboard
504 if (event->type == GDK_BUTTON_RELEASE) {
505 int velocity = (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
506 int(float(event->y - REGION_BLOCK_HEIGHT) / float(KEYBOARD_HEIGHT) * 128.0f) + 1;
507 if (velocity <= 0) velocity = 1;
508 switch (m_VirtKeybModeChoice.get_value()) {
509 case VIRT_KEYBOARD_MODE_CHORD:
510 if (event->y >= REGION_BLOCK_HEIGHT)
511 keyboard_key_released_signal.emit(k, velocity);
512 break;
513 case VIRT_KEYBOARD_MODE_NORMAL:
514 default:
515 if (currentActiveKey >= 0 && currentActiveKey <= 127) {
516 keyboard_key_released_signal.emit(currentActiveKey, velocity);
517 currentActiveKey = -1;
518 }
519 break;
520 }
521 }
522
523 if (resize.active) {
524 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
525 get_window()->pointer_ungrab(event->time);
526 #else
527 Glib::wrap(event->device, true)->ungrab(event->time);
528 #endif
529 resize.active = false;
530
531 if (!is_in_resize_zone(event->x, event->y) && cursor_is_resize) {
532 get_window()->set_cursor();
533 cursor_is_resize = false;
534 }
535 } else if (move.active) {
536 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
537 get_window()->pointer_ungrab(event->time);
538 #else
539 Glib::wrap(event->device, true)->ungrab(event->time);
540 #endif
541 move.active = false;
542
543 if (is_in_resize_zone(event->x, event->y)) {
544 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
545 get_window()->set_cursor(Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW));
546 #else
547 get_window()->set_cursor(Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW));
548 #endif
549 cursor_is_resize = true;
550 }
551 }
552 return true;
553 }
554
555 void RegionChooser::update_after_resize()
556 {
557 if (resize.mode == resize.moving_high_limit) {
558 if (resize.region->KeyRange.high != resize.pos - 1) {
559 instrument_struct_to_be_changed_signal.emit(instrument);
560 resize.region->SetKeyRange(resize.region->KeyRange.low,
561 resize.pos - 1);
562 regions.update(instrument);
563 instrument_changed.emit();
564 instrument_struct_changed_signal.emit(instrument);
565 }
566 } else if (resize.mode == resize.moving_low_limit) {
567 if (resize.region->KeyRange.low != resize.pos) {
568 instrument_struct_to_be_changed_signal.emit(instrument);
569 resize.region->SetKeyRange(resize.pos,
570 resize.region->KeyRange.high);
571 regions.update(instrument);
572 instrument_changed.emit();
573 instrument_struct_changed_signal.emit(instrument);
574 }
575 }
576 }
577
578 void RegionChooser::update_after_move(int pos)
579 {
580 instrument_struct_to_be_changed_signal.emit(instrument);
581 const int range = region->KeyRange.high - region->KeyRange.low;
582 const int diff = pos - int(region->KeyRange.low);
583 region->SetKeyRange(pos, pos + range);
584 if (Settings::singleton()->moveRootNoteWithRegionMoved) {
585 for (int i = 0; i < 256; ++i) {
586 gig::DimensionRegion* dimrgn = region->pDimensionRegions[i];
587 if (!dimrgn || !dimrgn->pSample || !dimrgn->PitchTrack) continue;
588 dimrgn->UnityNote += diff;
589 }
590 }
591 regions.update(instrument);
592 instrument_changed.emit();
593 instrument_struct_changed_signal.emit(instrument);
594 }
595
596 bool RegionChooser::on_button_press_event(GdkEventButton* event)
597 {
598 if (!instrument) return true;
599
600 const int w = get_width() - 1;
601 const int k = x_to_key(event->x, w);
602
603 if (event->type == GDK_BUTTON_PRESS) {
604 if (event->y >= REGION_BLOCK_HEIGHT) {
605 int velocity = (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
606 int(float(event->y - REGION_BLOCK_HEIGHT) / float(KEYBOARD_HEIGHT) * 128.0f) + 1;
607 currentActiveKey = k;
608 keyboard_key_hit_signal.emit(k, velocity);
609 }
610 }
611
612 // left mouse button double click
613 if (event->type == GDK_2BUTTON_PRESS && event->button == 1) {
614 if (event->y < REGION_BLOCK_HEIGHT) {
615 // show dimension manager dialog for this region
616 manage_dimensions();
617 }
618 }
619
620 if (event->y >= REGION_BLOCK_HEIGHT) return true;
621 if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
622 gig::Region* r = get_region(k);
623 if (r) {
624 region = r;
625 queue_draw();
626 region_selected();
627 dimensionManager.set_region(region);
628 popup_menu_inside_region->popup(event->button, event->time);
629 } else {
630 new_region_pos = k;
631 popup_menu_outside_region->popup(event->button, event->time);
632 }
633 } else {
634 if (is_in_resize_zone(event->x, event->y)) {
635 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
636 get_window()->pointer_grab(false,
637 Gdk::BUTTON_RELEASE_MASK |
638 Gdk::POINTER_MOTION_MASK |
639 Gdk::POINTER_MOTION_HINT_MASK,
640 Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW),
641 event->time);
642 #else
643 Glib::wrap(event->device, true)->grab(get_window(),
644 Gdk::OWNERSHIP_NONE,
645 false,
646 Gdk::BUTTON_RELEASE_MASK |
647 Gdk::POINTER_MOTION_MASK |
648 Gdk::POINTER_MOTION_HINT_MASK,
649 Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW),
650 event->time);
651 #endif
652 resize.active = true;
653 } else {
654 gig::Region* r = get_region(k);
655 if (r) {
656 region = r;
657 queue_draw();
658 region_selected();
659 dimensionManager.set_region(region);
660
661 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
662 get_window()->pointer_grab(false,
663 Gdk::BUTTON_RELEASE_MASK |
664 Gdk::POINTER_MOTION_MASK |
665 Gdk::POINTER_MOTION_HINT_MASK,
666 Gdk::Cursor(Gdk::FLEUR),
667 event->time);
668 #else
669 Glib::wrap(event->device, true)->grab(get_window(),
670 Gdk::OWNERSHIP_NONE,
671 false,
672 Gdk::BUTTON_RELEASE_MASK |
673 Gdk::POINTER_MOTION_MASK |
674 Gdk::POINTER_MOTION_HINT_MASK,
675 Gdk::Cursor::create(Gdk::FLEUR),
676 event->time);
677 #endif
678 move.active = true;
679 move.offset = event->x - key_to_x(region->KeyRange.low, w);
680 }
681 }
682 }
683 return true;
684 }
685
686 gig::Region* RegionChooser::get_region(int key)
687 {
688 for (gig::Region* r = regions.first() ; r ; r = regions.next()) {
689 if (key < r->KeyRange.low) return 0;
690 if (key <= r->KeyRange.high) return r;
691 }
692 return 0;
693 }
694
695 void RegionChooser::set_region(gig::Region* region) {
696 this->region = region;
697 queue_draw();
698 region_selected();
699 dimensionManager.set_region(region);
700 }
701
702 void RegionChooser::select_next_region() {
703 if (!instrument) return;
704 if (!region) {
705 for (int i = 0; i < 128; ++i) {
706 ::gig::Region* rgn = instrument->GetRegion(i);
707 if (rgn) {
708 set_region(rgn);
709 return;
710 }
711 }
712 } else {
713 bool currentFound = false;
714 for (int i = 0; i < 128; ++i) {
715 ::gig::Region* rgn = instrument->GetRegion(i);
716 if (!rgn) continue;
717 if (currentFound) {
718 if (rgn != region) {
719 set_region(rgn);
720 return;
721 }
722 } else {
723 if (rgn == region) currentFound = true;
724 }
725 }
726 }
727 }
728
729 void RegionChooser::select_prev_region() {
730 if (!instrument) return;
731 if (!region) {
732 for (int i = 0; i < 128; ++i) {
733 ::gig::Region* rgn = instrument->GetRegion(i);
734 if (rgn) {
735 set_region(rgn);
736 return;
737 }
738 }
739 } else {
740 bool currentFound = false;
741 for (int i = 127; i >= 0; --i) {
742 ::gig::Region* rgn = instrument->GetRegion(i);
743 if (!rgn) continue;
744 if (currentFound) {
745 if (rgn != region) {
746 set_region(rgn);
747 return;
748 }
749 } else {
750 if (rgn == region) currentFound = true;
751 }
752 }
753 }
754 }
755
756 void RegionChooser::motion_resize_region(int x, int y)
757 {
758 const int w = get_width() - 1;
759
760 int k = int(double(x) / w * 128.0 + 0.5);
761
762 if (k < resize.min) k = resize.min;
763 else if (k > resize.max) k = resize.max;
764
765 if (k != resize.pos) {
766 if (resize.mode == resize.undecided) {
767 if (k < resize.pos) {
768 // edit high limit of prev_region
769 resize.max = resize.region->KeyRange.low;
770 resize.region = resize.prev_region;
771 resize.mode = resize.moving_high_limit;
772 } else {
773 // edit low limit of region
774 resize.min = resize.prev_region->KeyRange.high + 1;
775 resize.mode = resize.moving_low_limit;
776 }
777 }
778 resize.pos = k;
779
780 int x1, x2;
781 if (resize.mode == resize.moving_high_limit) {
782 if (resize.region->KeyRange.high < resize.pos - 1) {
783 x1 = resize.region->KeyRange.high;
784 x2 = resize.pos - 1;
785 } else {
786 x1 = resize.pos - 1;
787 x2 = resize.region->KeyRange.high;
788 }
789 } else {
790 if (resize.region->KeyRange.low < resize.pos) {
791 x1 = resize.region->KeyRange.low;
792 x2 = resize.pos;
793 } else {
794 x1 = resize.pos;
795 x2 = resize.region->KeyRange.low;
796 }
797 }
798 x1 = key_to_x(x1, w);
799 x2 = key_to_x(x2 + 1, w) + 1;
800 Gdk::Rectangle rect(x1, 0, x2 - x1, h1);
801
802 update_after_resize();
803
804 //get_window()->invalidate_rect(rect, false);
805 get_window()->invalidate(false); // repaint entire region, otherwise it would create visual artifacts
806 }
807 }
808
809 void RegionChooser::motion_move_region(int x, int y)
810 {
811 const int w = get_width() - 1;
812
813 int l = int(double(x - move.offset) / w * 128.0 + 0.5);
814
815 if (l == region->KeyRange.low) return;
816 int new_l;
817 int regionsize = region->KeyRange.high - region->KeyRange.low;
818 int a = 0;
819 if (l > region->KeyRange.low) {
820 for (gig::Region* r = regions.first() ; ; r = regions.next()) {
821 if (r != region) {
822 int b = r ? r->KeyRange.low : 128;
823
824 // gap: from a to b (not inclusive b)
825
826 if (region->KeyRange.high >= b) {
827 // not found the current gap yet, just continue
828 } else {
829
830 if (a > l) {
831 // this gap is too far to the right, break
832 break;
833 }
834
835 int newhigh = std::min(l + regionsize, b - 1);
836 int newlo = newhigh - regionsize;
837
838 if (newlo >= a) {
839 // yes it fits - it's a candidate
840 new_l = newlo;
841 }
842 }
843 if (!r) break;
844 a = r->KeyRange.high + 1;
845 }
846 }
847 } else {
848 for (gig::Region* r = regions.first() ; ; r = regions.next()) {
849 if (r != region) {
850 int b = r ? r->KeyRange.low : 128;
851
852 // gap from a to b (not inclusive b)
853
854 if (l + regionsize >= b) {
855 // not found the current gap yet, just continue
856 } else {
857
858 if (a > region->KeyRange.low) {
859 // this gap is too far to the right, break
860 break;
861 }
862
863 int newlo = std::max(l, a);
864 int newhigh = newlo + regionsize;
865
866 if (newhigh < b) {
867 // yes it fits - break as the first one is the best
868 new_l = newlo;
869 break;
870 }
871 }
872 if (!r) break;
873 a = r->KeyRange.high + 1;
874 }
875 }
876 }
877 if (new_l == region->KeyRange.low) return;
878
879 int x1 = key_to_x(std::min(int(region->KeyRange.low), new_l), w);
880 int x2 = key_to_x(std::max(int(region->KeyRange.high),
881 new_l + regionsize) + 1, w) + 1;
882
883 Gdk::Rectangle rect(x1, 0, x2 - x1, h1);
884 update_after_move(new_l);
885
886 get_window()->invalidate_rect(rect, false);
887 }
888
889
890 bool RegionChooser::on_motion_notify_event(GdkEventMotion* event)
891 {
892 Glib::RefPtr<Gdk::Window> window = get_window();
893 int x, y;
894 Gdk::ModifierType state = Gdk::ModifierType(0);
895 window->get_pointer(x, y, state);
896
897 // handle virtual MIDI keyboard
898 if (m_VirtKeybModeChoice.get_value() != VIRT_KEYBOARD_MODE_CHORD &&
899 currentActiveKey > 0 &&
900 event->y >= REGION_BLOCK_HEIGHT &&
901 event->y < REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT)
902 {
903 const int k = x_to_key(event->x, get_width() - 1);
904 if (k != currentActiveKey) {
905 int velocity =
906 (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
907 int(float(event->y - REGION_BLOCK_HEIGHT) /
908 float(KEYBOARD_HEIGHT) * 128.0f) + 1;
909 if (velocity <= 0) velocity = 1;
910 keyboard_key_released_signal.emit(currentActiveKey, velocity);
911 currentActiveKey = k;
912 keyboard_key_hit_signal.emit(k, velocity);
913 }
914 }
915
916 if (resize.active) {
917 motion_resize_region(x, y);
918 } else if (move.active) {
919 motion_move_region(x, y);
920 } else {
921 if (is_in_resize_zone(x, y)) {
922 if (!cursor_is_resize) {
923 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
924 window->set_cursor(Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW));
925 #else
926 window->set_cursor(Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW));
927 #endif
928 cursor_is_resize = true;
929 }
930 } else if (cursor_is_resize) {
931 window->set_cursor();
932 cursor_is_resize = false;
933 }
934 }
935
936 return true;
937 }
938
939 bool RegionChooser::is_in_resize_zone(double x, double y) {
940 const int w = get_width() - 1;
941
942 if (instrument && y >= 0 && y <= h1) {
943 gig::Region* prev_region = 0;
944 gig::Region* next_region;
945 for (gig::Region* r = regions.first(); r ; r = next_region) {
946 next_region = regions.next();
947
948 int lo = key_to_x(r->KeyRange.low, w);
949 if (x <= lo - 2) break;
950 if (x < lo + 2) {
951 resize.region = r;
952 resize.pos = r->KeyRange.low;
953 resize.max = r->KeyRange.high;
954
955 if (prev_region && prev_region->KeyRange.high + 1 == r->KeyRange.low) {
956 // we don't know yet if it's the high limit of
957 // prev_region or the low limit of r that's going
958 // to be edited
959 resize.mode = resize.undecided;
960 resize.min = prev_region->KeyRange.low + 1;
961 resize.prev_region = prev_region;
962 return resize.min != resize.max;
963 }
964
965 // edit low limit
966 resize.mode = resize.moving_low_limit;
967 resize.min = prev_region ? prev_region->KeyRange.high + 1 : 0;
968 return resize.min != resize.max;
969 }
970 if (!next_region || r->KeyRange.high + 1 != next_region->KeyRange.low) {
971 int hi = key_to_x(r->KeyRange.high + 1, w);
972 if (x <= hi - 2) break;
973 if (x < hi + 2) {
974 // edit high limit
975 resize.region = r;
976 resize.pos = r->KeyRange.high + 1;
977 resize.mode = resize.moving_high_limit;
978 resize.min = r->KeyRange.low + 1;
979 resize.max = next_region ? next_region->KeyRange.low : 128;
980 return resize.min != resize.max;
981 }
982 }
983 prev_region = r;
984 }
985 }
986 return false;
987 }
988
989 sigc::signal<void>& RegionChooser::signal_region_selected()
990 {
991 return region_selected;
992 }
993
994 sigc::signal<void>& RegionChooser::signal_instrument_changed()
995 {
996 return instrument_changed;
997 }
998
999 void RegionChooser::show_region_properties()
1000 {
1001 if (!region) return;
1002 Gtk::Dialog dialog(_("Region Properties"), true /*modal*/);
1003 // add "Keygroup" checkbox
1004 Gtk::CheckButton checkBoxKeygroup(_("Member of a Keygroup (Exclusive Group)"));
1005 checkBoxKeygroup.set_active(region->KeyGroup);
1006 dialog.get_vbox()->pack_start(checkBoxKeygroup);
1007 checkBoxKeygroup.show();
1008 // add "Keygroup" spinbox
1009 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
1010 Gtk::Adjustment adjustment(1, 1, 999);
1011 Gtk::SpinButton spinBox(adjustment);
1012 #else
1013 Gtk::SpinButton spinBox(Gtk::Adjustment::create(1, 1, 999));
1014 #endif
1015 if (region->KeyGroup) spinBox.set_value(region->KeyGroup);
1016 dialog.get_vbox()->pack_start(spinBox);
1017 spinBox.show();
1018 // add OK and CANCEL buttons to the dialog
1019 dialog.add_button(_("_OK"), 0);
1020 dialog.add_button(_("_Cancel"), 1);
1021 dialog.show_all_children();
1022 if (!dialog.run()) { // OK selected ...
1023 region->KeyGroup =
1024 (checkBoxKeygroup.get_active()) ? spinBox.get_value_as_int() : 0;
1025 }
1026 }
1027
1028 void RegionChooser::add_region()
1029 {
1030 instrument_struct_to_be_changed_signal.emit(instrument);
1031
1032 region = instrument->AddRegion();
1033 region->SetKeyRange(new_region_pos, new_region_pos);
1034
1035 instrument_struct_changed_signal.emit(instrument);
1036 regions.update(instrument);
1037
1038 queue_draw();
1039 region_selected();
1040 dimensionManager.set_region(region);
1041 instrument_changed();
1042 }
1043
1044 void RegionChooser::delete_region()
1045 {
1046 instrument_struct_to_be_changed_signal.emit(instrument);
1047 instrument->DeleteRegion(region);
1048 instrument_struct_changed_signal.emit(instrument);
1049 regions.update(instrument);
1050
1051 region = 0;
1052 queue_draw();
1053 region_selected();
1054 dimensionManager.set_region(region);
1055 instrument_changed();
1056 }
1057
1058 void RegionChooser::manage_dimensions()
1059 {
1060 gig::Region* region = get_region();
1061 if (!region) return;
1062 dimensionManager.show(region);
1063 }
1064
1065 void RegionChooser::on_dimension_manager_changed() {
1066 region_selected();
1067 instrument_changed();
1068 }
1069
1070 sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_to_be_changed() {
1071 return instrument_struct_to_be_changed_signal;
1072 }
1073
1074 sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_changed() {
1075 return instrument_struct_changed_signal;
1076 }
1077
1078 sigc::signal<void, gig::Region*>& RegionChooser::signal_region_to_be_changed() {
1079 return region_to_be_changed_signal;
1080 }
1081
1082 sigc::signal<void, gig::Region*>& RegionChooser::signal_region_changed_signal() {
1083 return region_changed_signal;
1084 }
1085
1086 sigc::signal<void, int/*key*/, int/*velocity*/>& RegionChooser::signal_keyboard_key_hit() {
1087 return keyboard_key_hit_signal;
1088 }
1089
1090 sigc::signal<void, int/*key*/, int/*velocity*/>& RegionChooser::signal_keyboard_key_released() {
1091 return keyboard_key_released_signal;
1092 }

  ViewVC Help
Powered by ViewVC