/[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 3202 - (show annotations) (download)
Mon May 22 18:58:46 2017 UTC (6 years, 10 months ago) by persson
File size: 39354 byte(s)
* fixed building with G_DISABLE_DEPRECATED

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

  ViewVC Help
Powered by ViewVC