/[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 2845 - (show annotations) (download)
Sun Sep 20 10:18:22 2015 UTC (4 years ago) by persson
File size: 33845 byte(s)
* avoid using gtk stock items, as they are deprecated in gtk 3.10

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

  ViewVC Help
Powered by ViewVC