/[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 3148 - (show annotations) (download)
Thu May 4 11:47:45 2017 UTC (6 years, 11 months ago) by schoenebeck
File size: 39258 byte(s)
* Region Chooser: Mark all regions which are auto selected by check box
  "All Regions" with gray hatched pattern.
* Bumped version (1.0.0.svn34).

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

  ViewVC Help
Powered by ViewVC