/[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 2841 - (show annotations) (download)
Sun Aug 30 10:00:49 2015 UTC (8 years, 7 months ago) by persson
File size: 33941 byte(s)
* allow building with G_DISABLE_DEPRECATED
* fixed building without liblinuxsampler on Mac
* fixed some compiler and cppcheck warnings

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

  ViewVC Help
Powered by ViewVC