/[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 3307 - (show annotations) (download)
Tue Jul 11 23:06:38 2017 UTC (6 years, 9 months ago) by schoenebeck
File size: 39511 byte(s)
* Fixed loop and sample reference icons being displayed
  incorrectly sometimes, because unused dimension regions
  were not ignored.
* Bumped version (1.0.0.svn57).

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

  ViewVC Help
Powered by ViewVC