/[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 3106 - (show annotations) (download)
Sat Feb 11 17:04:48 2017 UTC (7 years, 2 months ago) by schoenebeck
File size: 36100 byte(s)
* Show graphical symbol on region that uses loop(s).
* Show graphical symbol on region that misses sample
  reference(s).
* Bumped version (1.0.0.svn27).

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 red("#8070ff"),
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 ? red : 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, red);
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::motion_resize_region(int x, int y)
703 {
704 const int w = get_width() - 1;
705
706 int k = int(double(x) / w * 128.0 + 0.5);
707
708 if (k < resize.min) k = resize.min;
709 else if (k > resize.max) k = resize.max;
710
711 if (k != resize.pos) {
712 if (resize.mode == resize.undecided) {
713 if (k < resize.pos) {
714 // edit high limit of prev_region
715 resize.max = resize.region->KeyRange.low;
716 resize.region = resize.prev_region;
717 resize.mode = resize.moving_high_limit;
718 } else {
719 // edit low limit of region
720 resize.min = resize.prev_region->KeyRange.high + 1;
721 resize.mode = resize.moving_low_limit;
722 }
723 }
724 resize.pos = k;
725
726 int x1, x2;
727 if (resize.mode == resize.moving_high_limit) {
728 if (resize.region->KeyRange.high < resize.pos - 1) {
729 x1 = resize.region->KeyRange.high;
730 x2 = resize.pos - 1;
731 } else {
732 x1 = resize.pos - 1;
733 x2 = resize.region->KeyRange.high;
734 }
735 } else {
736 if (resize.region->KeyRange.low < resize.pos) {
737 x1 = resize.region->KeyRange.low;
738 x2 = resize.pos;
739 } else {
740 x1 = resize.pos;
741 x2 = resize.region->KeyRange.low;
742 }
743 }
744 x1 = key_to_x(x1, w);
745 x2 = key_to_x(x2 + 1, w) + 1;
746 Gdk::Rectangle rect(x1, 0, x2 - x1, h1);
747
748 update_after_resize();
749
750 //get_window()->invalidate_rect(rect, false);
751 get_window()->invalidate(false); // repaint entire region, otherwise it would create visual artifacts
752 }
753 }
754
755 void RegionChooser::motion_move_region(int x, int y)
756 {
757 const int w = get_width() - 1;
758
759 int l = int(double(x - move.offset) / w * 128.0 + 0.5);
760
761 if (l == region->KeyRange.low) return;
762 int new_l;
763 int regionsize = region->KeyRange.high - region->KeyRange.low;
764 int a = 0;
765 if (l > region->KeyRange.low) {
766 for (gig::Region* r = regions.first() ; ; r = regions.next()) {
767 if (r != region) {
768 int b = r ? r->KeyRange.low : 128;
769
770 // gap: from a to b (not inclusive b)
771
772 if (region->KeyRange.high >= b) {
773 // not found the current gap yet, just continue
774 } else {
775
776 if (a > l) {
777 // this gap is too far to the right, break
778 break;
779 }
780
781 int newhigh = std::min(l + regionsize, b - 1);
782 int newlo = newhigh - regionsize;
783
784 if (newlo >= a) {
785 // yes it fits - it's a candidate
786 new_l = newlo;
787 }
788 }
789 if (!r) break;
790 a = r->KeyRange.high + 1;
791 }
792 }
793 } else {
794 for (gig::Region* r = regions.first() ; ; r = regions.next()) {
795 if (r != region) {
796 int b = r ? r->KeyRange.low : 128;
797
798 // gap from a to b (not inclusive b)
799
800 if (l + regionsize >= b) {
801 // not found the current gap yet, just continue
802 } else {
803
804 if (a > region->KeyRange.low) {
805 // this gap is too far to the right, break
806 break;
807 }
808
809 int newlo = std::max(l, a);
810 int newhigh = newlo + regionsize;
811
812 if (newhigh < b) {
813 // yes it fits - break as the first one is the best
814 new_l = newlo;
815 break;
816 }
817 }
818 if (!r) break;
819 a = r->KeyRange.high + 1;
820 }
821 }
822 }
823 if (new_l == region->KeyRange.low) return;
824
825 int x1 = key_to_x(std::min(int(region->KeyRange.low), new_l), w);
826 int x2 = key_to_x(std::max(int(region->KeyRange.high),
827 new_l + regionsize) + 1, w) + 1;
828
829 Gdk::Rectangle rect(x1, 0, x2 - x1, h1);
830 update_after_move(new_l);
831
832 get_window()->invalidate_rect(rect, false);
833 }
834
835
836 bool RegionChooser::on_motion_notify_event(GdkEventMotion* event)
837 {
838 Glib::RefPtr<Gdk::Window> window = get_window();
839 int x, y;
840 Gdk::ModifierType state = Gdk::ModifierType(0);
841 window->get_pointer(x, y, state);
842
843 // handle virtual MIDI keyboard
844 if (m_VirtKeybModeChoice.get_value() != VIRT_KEYBOARD_MODE_CHORD &&
845 currentActiveKey > 0 &&
846 event->y >= REGION_BLOCK_HEIGHT &&
847 event->y < REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT)
848 {
849 const int k = x_to_key(event->x, get_width() - 1);
850 if (k != currentActiveKey) {
851 int velocity =
852 (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
853 int(float(event->y - REGION_BLOCK_HEIGHT) /
854 float(KEYBOARD_HEIGHT) * 128.0f) + 1;
855 if (velocity <= 0) velocity = 1;
856 keyboard_key_released_signal.emit(currentActiveKey, velocity);
857 currentActiveKey = k;
858 keyboard_key_hit_signal.emit(k, velocity);
859 }
860 }
861
862 if (resize.active) {
863 motion_resize_region(x, y);
864 } else if (move.active) {
865 motion_move_region(x, y);
866 } else {
867 if (is_in_resize_zone(x, y)) {
868 if (!cursor_is_resize) {
869 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
870 window->set_cursor(Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW));
871 #else
872 window->set_cursor(Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW));
873 #endif
874 cursor_is_resize = true;
875 }
876 } else if (cursor_is_resize) {
877 window->set_cursor();
878 cursor_is_resize = false;
879 }
880 }
881
882 return true;
883 }
884
885 bool RegionChooser::is_in_resize_zone(double x, double y) {
886 const int w = get_width() - 1;
887
888 if (instrument && y >= 0 && y <= h1) {
889 gig::Region* prev_region = 0;
890 gig::Region* next_region;
891 for (gig::Region* r = regions.first(); r ; r = next_region) {
892 next_region = regions.next();
893
894 int lo = key_to_x(r->KeyRange.low, w);
895 if (x <= lo - 2) break;
896 if (x < lo + 2) {
897 resize.region = r;
898 resize.pos = r->KeyRange.low;
899 resize.max = r->KeyRange.high;
900
901 if (prev_region && prev_region->KeyRange.high + 1 == r->KeyRange.low) {
902 // we don't know yet if it's the high limit of
903 // prev_region or the low limit of r that's going
904 // to be edited
905 resize.mode = resize.undecided;
906 resize.min = prev_region->KeyRange.low + 1;
907 resize.prev_region = prev_region;
908 return resize.min != resize.max;
909 }
910
911 // edit low limit
912 resize.mode = resize.moving_low_limit;
913 resize.min = prev_region ? prev_region->KeyRange.high + 1 : 0;
914 return resize.min != resize.max;
915 }
916 if (!next_region || r->KeyRange.high + 1 != next_region->KeyRange.low) {
917 int hi = key_to_x(r->KeyRange.high + 1, w);
918 if (x <= hi - 2) break;
919 if (x < hi + 2) {
920 // edit high limit
921 resize.region = r;
922 resize.pos = r->KeyRange.high + 1;
923 resize.mode = resize.moving_high_limit;
924 resize.min = r->KeyRange.low + 1;
925 resize.max = next_region ? next_region->KeyRange.low : 128;
926 return resize.min != resize.max;
927 }
928 }
929 prev_region = r;
930 }
931 }
932 return false;
933 }
934
935 sigc::signal<void>& RegionChooser::signal_region_selected()
936 {
937 return region_selected;
938 }
939
940 sigc::signal<void>& RegionChooser::signal_instrument_changed()
941 {
942 return instrument_changed;
943 }
944
945 void RegionChooser::show_region_properties()
946 {
947 if (!region) return;
948 Gtk::Dialog dialog(_("Region Properties"), true /*modal*/);
949 // add "Keygroup" checkbox
950 Gtk::CheckButton checkBoxKeygroup(_("Member of a Keygroup (Exclusive Group)"));
951 checkBoxKeygroup.set_active(region->KeyGroup);
952 dialog.get_vbox()->pack_start(checkBoxKeygroup);
953 checkBoxKeygroup.show();
954 // add "Keygroup" spinbox
955 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
956 Gtk::Adjustment adjustment(1, 1, 999);
957 Gtk::SpinButton spinBox(adjustment);
958 #else
959 Gtk::SpinButton spinBox(Gtk::Adjustment::create(1, 1, 999));
960 #endif
961 if (region->KeyGroup) spinBox.set_value(region->KeyGroup);
962 dialog.get_vbox()->pack_start(spinBox);
963 spinBox.show();
964 // add OK and CANCEL buttons to the dialog
965 dialog.add_button(_("_OK"), 0);
966 dialog.add_button(_("_Cancel"), 1);
967 dialog.show_all_children();
968 if (!dialog.run()) { // OK selected ...
969 region->KeyGroup =
970 (checkBoxKeygroup.get_active()) ? spinBox.get_value_as_int() : 0;
971 }
972 }
973
974 void RegionChooser::add_region()
975 {
976 instrument_struct_to_be_changed_signal.emit(instrument);
977
978 region = instrument->AddRegion();
979 region->SetKeyRange(new_region_pos, new_region_pos);
980
981 instrument_struct_changed_signal.emit(instrument);
982 regions.update(instrument);
983
984 queue_draw();
985 region_selected();
986 dimensionManager.set_region(region);
987 instrument_changed();
988 }
989
990 void RegionChooser::delete_region()
991 {
992 instrument_struct_to_be_changed_signal.emit(instrument);
993 instrument->DeleteRegion(region);
994 instrument_struct_changed_signal.emit(instrument);
995 regions.update(instrument);
996
997 region = 0;
998 queue_draw();
999 region_selected();
1000 dimensionManager.set_region(region);
1001 instrument_changed();
1002 }
1003
1004 void RegionChooser::manage_dimensions()
1005 {
1006 gig::Region* region = get_region();
1007 if (!region) return;
1008 dimensionManager.show(region);
1009 }
1010
1011 void RegionChooser::on_dimension_manager_changed() {
1012 region_selected();
1013 instrument_changed();
1014 }
1015
1016 sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_to_be_changed() {
1017 return instrument_struct_to_be_changed_signal;
1018 }
1019
1020 sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_changed() {
1021 return instrument_struct_changed_signal;
1022 }
1023
1024 sigc::signal<void, gig::Region*>& RegionChooser::signal_region_to_be_changed() {
1025 return region_to_be_changed_signal;
1026 }
1027
1028 sigc::signal<void, gig::Region*>& RegionChooser::signal_region_changed_signal() {
1029 return region_changed_signal;
1030 }
1031
1032 sigc::signal<void, int/*key*/, int/*velocity*/>& RegionChooser::signal_keyboard_key_hit() {
1033 return keyboard_key_hit_signal;
1034 }
1035
1036 sigc::signal<void, int/*key*/, int/*velocity*/>& RegionChooser::signal_keyboard_key_released() {
1037 return keyboard_key_released_signal;
1038 }

  ViewVC Help
Powered by ViewVC