/[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 2663 - (show annotations) (download)
Wed Jul 2 23:53:21 2014 UTC (9 years, 8 months ago) by schoenebeck
File size: 33448 byte(s)
* Fix: double click on the virtual MIDI keyboard opened the
  dimension manager dialog.

1 /*
2 * Copyright (C) 2006-2014 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
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",
107 Gtk::Stock::PROPERTIES),
108 sigc::mem_fun(*this,
109 &RegionChooser::show_region_properties));
110 actionGroup->add(Gtk::Action::create("Remove", Gtk::Stock::REMOVE),
111 sigc::mem_fun(*this, &RegionChooser::delete_region));
112 actionGroup->add(Gtk::Action::create("Add", Gtk::Stock::ADD),
113 sigc::mem_fun(*this, &RegionChooser::add_region));
114 actionGroup->add(Gtk::Action::create("Dimensions", _("Dimensions...")),
115 sigc::mem_fun(*this, &RegionChooser::manage_dimensions));
116
117 uiManager = Gtk::UIManager::create();
118 uiManager->insert_action_group(actionGroup);
119 Glib::ustring ui_info =
120 "<ui>"
121 " <popup name='PopupMenuInsideRegion'>"
122 " <menuitem action='Properties'/>"
123 " <menuitem action='Dimensions'/>"
124 " <menuitem action='Remove'/>"
125 " </popup>"
126 " <popup name='PopupMenuOutsideRegion'>"
127 " <menuitem action='Add'/>"
128 " </popup>"
129 "</ui>";
130 uiManager->add_ui_from_string(ui_info);
131
132 popup_menu_inside_region = dynamic_cast<Gtk::Menu*>(
133 uiManager->get_widget("/PopupMenuInsideRegion"));
134 popup_menu_outside_region = dynamic_cast<Gtk::Menu*>(
135 uiManager->get_widget("/PopupMenuOutsideRegion"));
136
137 add_events(Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK |
138 Gdk::POINTER_MOTION_MASK | Gdk::POINTER_MOTION_HINT_MASK);
139
140 dimensionManager.region_to_be_changed_signal.connect(
141 region_to_be_changed_signal.make_slot()
142 );
143 dimensionManager.region_changed_signal.connect(
144 region_changed_signal.make_slot()
145 );
146 dimensionManager.region_changed_signal.connect(
147 sigc::hide(
148 sigc::mem_fun(*this, &RegionChooser::on_dimension_manager_changed)
149 )
150 );
151 keyboard_key_hit_signal.connect(
152 sigc::mem_fun(*this, &RegionChooser::on_note_on_event)
153 );
154 keyboard_key_released_signal.connect(
155 sigc::mem_fun(*this, &RegionChooser::on_note_off_event)
156 );
157 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."));
158 }
159
160 RegionChooser::~RegionChooser()
161 {
162 }
163
164 void RegionChooser::invalidate_key(int key) {
165 const int h = KEYBOARD_HEIGHT;
166 const int w = get_width() - 1;
167 int x1 = key_to_x(key - 0.5, w);
168 int x2 = key_to_x(key + 1.5, w);
169
170 Gdk::Rectangle rect(x1 + 1, h1 + 1, x2 - x1 - 1, h - 2);
171 get_window()->invalidate_rect(rect, false);
172 }
173
174 void RegionChooser::on_note_on_event(int key, int velocity) {
175 key_pressed[key] = true;
176 invalidate_key(key);
177 m_VirtKeybVelocityLabel.set_text(ToString(velocity));
178 }
179
180 void RegionChooser::on_note_off_event(int key, int velocity) {
181 key_pressed[key] = false;
182 invalidate_key(key);
183 m_VirtKeybOffVelocityLabel.set_text(ToString(velocity));
184 }
185
186
187 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
188 bool RegionChooser::on_expose_event(GdkEventExpose* e) {
189 double clipx1 = e->area.x;
190 double clipx2 = e->area.x + e->area.width;
191 double clipy1 = e->area.y;
192 double clipy2 = e->area.y + e->area.height;
193
194 const Cairo::RefPtr<Cairo::Context>& cr =
195 get_window()->create_cairo_context();
196 #if 0
197 }
198 #endif
199 #else
200 bool RegionChooser::on_draw(const Cairo::RefPtr<Cairo::Context>& cr) {
201 double clipx1, clipx2, clipy1, clipy2;
202 cr->get_clip_extents(clipx1, clipy1, clipx2, clipy2);
203 #endif
204
205 cr->save();
206 cr->set_line_width(1);
207
208 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
209 const Gdk::Color bg = get_style()->get_bg(Gtk::STATE_NORMAL);
210 #else
211 const Gdk::RGBA bg = get_style_context()->get_background_color();
212 #endif
213 Gdk::Cairo::set_source_rgba(cr, bg);
214 cr->paint();
215
216 if (clipy2 > h1) {
217 draw_keyboard(cr, clipx1, clipx2);
218 }
219
220 if (clipy1 < h1 && instrument) {
221 draw_regions(cr, clipx1, clipx2);
222 }
223
224 cr->restore();
225
226 return true;
227 }
228
229 void RegionChooser::draw_keyboard(const Cairo::RefPtr<Cairo::Context>& cr,
230 int clip_low, int clip_high) {
231 const int h = KEYBOARD_HEIGHT;
232 const int w = get_width() - 1;
233 const int bh = int(h * 0.55);
234
235 Gdk::Cairo::set_source_rgba(cr, black);
236 cr->rectangle(0.5, h1 + 0.5, w, h - 1);
237 cr->stroke();
238
239 int x1 = key_to_x(20.5, w);
240 Gdk::Cairo::set_source_rgba(cr, grey1);
241 cr->rectangle(1, h1 + 1, x1 - 1, h - 2);
242 cr->fill();
243
244 int x2 = key_to_x(109.5, w);
245 Gdk::Cairo::set_source_rgba(cr, white);
246 cr->rectangle(x1 + 1, h1 + 1, x2 - x1 - 1, h - 2);
247 cr->fill();
248
249 Gdk::Cairo::set_source_rgba(cr, grey1);
250 cr->rectangle(x2 + 1, h1 + 1, w - x2 - 1, h - 2);
251 cr->fill();
252
253 Gdk::Cairo::set_source_rgba(cr, black);
254
255 int clipkey1 = std::max(0, x_to_key_right(clip_low - 1, w));
256 int clipkey2 = std::min(x_to_key_right(clip_high - 1, w) + 1, 128);
257
258 for (int i = clipkey1 ; i < clipkey2 ; i++) {
259 int note = (i + 3) % 12;
260 int x = key_to_x(i, w);
261
262 if (note == 1 || note == 4 || note == 6 ||
263 note == 9 || note == 11) {
264 // black key: short line in the middle, with a rectangle
265 // on top
266 int x2 = key_to_x(i + 0.5, w);
267 cr->move_to(x2 + 0.5, h1 + bh + 0.5);
268 cr->line_to(x2 + 0.5, h1 + h - 1);
269 cr->stroke();
270
271 int x3 = key_to_x(i + 1, w);
272 cr->rectangle(x, h1 + 1, x3 - x + 1, bh);
273 cr->fill();
274 } else if (note == 3 || note == 8) {
275 // C or F: long line to the left
276 cr->move_to(x + 0.5, h1 + 1);
277 cr->line_to(x + 0.5, h1 + h - 1);
278 cr->stroke();
279 }
280
281 if (key_pressed[i]) draw_key(cr, i);
282
283 if (note == 3) draw_digit(cr, i);
284 }
285 }
286
287
288 void RegionChooser::draw_regions(const Cairo::RefPtr<Cairo::Context>& cr,
289 int clip_low, int clip_high) {
290 const int w = get_width() - 1;
291
292 Gdk::Cairo::set_source_rgba(cr, black);
293 gig::Region* next_region;
294 int x3 = -1;
295 for (gig::Region* r = regions.first() ; r ; r = next_region) {
296 next_region = regions.next();
297
298 if (x3 < 0) {
299 x3 = key_to_x(r->KeyRange.low, w);
300 if (x3 >= clip_high) break;
301 }
302 if (!next_region ||
303 r->KeyRange.high + 1 != next_region->KeyRange.low ||
304 r == region || next_region == region) {
305
306 int x2 = key_to_x(r->KeyRange.high + 1, w);
307 if (x2 >= clip_low) {
308 cr->move_to(x3, 0.5);
309 cr->line_to(x2 + 0.5, 0.5);
310 cr->line_to(x2 + 0.5, h1 - 0.5);
311 cr->line_to(x3, h1 - 0.5);
312 cr->stroke();
313
314 Gdk::Cairo::set_source_rgba(cr, region == r ? red : white);
315 cr->rectangle(x3 + 1, 1, x2 - x3 - 1, h1 - 2);
316 cr->fill();
317 Gdk::Cairo::set_source_rgba(cr, black);
318 }
319 x3 = -1;
320 }
321 }
322
323 for (gig::Region* r = regions.first() ; r ; r = regions.next()) {
324 int x = key_to_x(r->KeyRange.low, w);
325
326 if (x < clip_low) continue;
327 if (x >= clip_high) break;
328
329 cr->move_to(x + 0.5, 1);
330 cr->line_to(x + 0.5, h1 - 1);
331 cr->stroke();
332 }
333
334 // if there is no region yet, show the user some hint text that he may
335 // right click on this area to create a new region
336 if (!regions.first()) {
337 Glib::RefPtr<Pango::Context> context = get_pango_context();
338 Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create(context);
339 layout->set_alignment(Pango::ALIGN_CENTER);
340 layout->set_text(Glib::ustring("*** ") + _("Right click here to create a region.") + " ***");
341 layout->set_width(get_width() * Pango::SCALE);
342 //layout->set_height(get_height() * Pango::SCALE);
343 layout->set_spacing(10);
344 Gdk::Cairo::set_source_rgba(cr, red);
345 // get the text dimensions
346 Pango::Rectangle rect = layout->get_logical_extents();
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 region->SetKeyRange(pos, pos + region->KeyRange.high -
519 region->KeyRange.low);
520 regions.update(instrument);
521 instrument_changed.emit();
522 instrument_struct_changed_signal.emit(instrument);
523 }
524
525 bool RegionChooser::on_button_press_event(GdkEventButton* event)
526 {
527 if (!instrument) return true;
528
529 const int w = get_width() - 1;
530 const int k = x_to_key(event->x, w);
531
532 if (event->type == GDK_BUTTON_PRESS) {
533 if (event->y >= REGION_BLOCK_HEIGHT) {
534 int velocity = (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
535 int(float(event->y - REGION_BLOCK_HEIGHT) / float(KEYBOARD_HEIGHT) * 128.0f) + 1;
536 currentActiveKey = k;
537 keyboard_key_hit_signal.emit(k, velocity);
538 }
539 }
540
541 // left mouse button double click
542 if (event->type == GDK_2BUTTON_PRESS && event->button == 1) {
543 if (event->y < REGION_BLOCK_HEIGHT) {
544 // show dimension manager dialog for this region
545 manage_dimensions();
546 }
547 }
548
549 if (event->y >= REGION_BLOCK_HEIGHT) return true;
550 if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
551 gig::Region* r = get_region(k);
552 if (r) {
553 region = r;
554 queue_draw();
555 region_selected();
556 dimensionManager.set_region(region);
557 popup_menu_inside_region->popup(event->button, event->time);
558 } else {
559 new_region_pos = k;
560 popup_menu_outside_region->popup(event->button, event->time);
561 }
562 } else {
563 if (is_in_resize_zone(event->x, event->y)) {
564 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
565 get_window()->pointer_grab(false,
566 Gdk::BUTTON_RELEASE_MASK |
567 Gdk::POINTER_MOTION_MASK |
568 Gdk::POINTER_MOTION_HINT_MASK,
569 Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW),
570 event->time);
571 #else
572 Glib::wrap(event->device, true)->grab(get_window(),
573 Gdk::OWNERSHIP_NONE,
574 false,
575 Gdk::BUTTON_RELEASE_MASK |
576 Gdk::POINTER_MOTION_MASK |
577 Gdk::POINTER_MOTION_HINT_MASK,
578 Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW),
579 event->time);
580 #endif
581 resize.active = true;
582 } else {
583 gig::Region* r = get_region(k);
584 if (r) {
585 region = r;
586 queue_draw();
587 region_selected();
588 dimensionManager.set_region(region);
589
590 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
591 get_window()->pointer_grab(false,
592 Gdk::BUTTON_RELEASE_MASK |
593 Gdk::POINTER_MOTION_MASK |
594 Gdk::POINTER_MOTION_HINT_MASK,
595 Gdk::Cursor(Gdk::FLEUR),
596 event->time);
597 #else
598 Glib::wrap(event->device, true)->grab(get_window(),
599 Gdk::OWNERSHIP_NONE,
600 false,
601 Gdk::BUTTON_RELEASE_MASK |
602 Gdk::POINTER_MOTION_MASK |
603 Gdk::POINTER_MOTION_HINT_MASK,
604 Gdk::Cursor::create(Gdk::FLEUR),
605 event->time);
606 #endif
607 move.active = true;
608 move.offset = event->x - key_to_x(region->KeyRange.low, w);
609 }
610 }
611 }
612 return true;
613 }
614
615 gig::Region* RegionChooser::get_region(int key)
616 {
617 for (gig::Region* r = regions.first() ; r ; r = regions.next()) {
618 if (key < r->KeyRange.low) return 0;
619 if (key <= r->KeyRange.high) return r;
620 }
621 return 0;
622 }
623
624 void RegionChooser::motion_resize_region(int x, int y)
625 {
626 const int w = get_width() - 1;
627
628 int k = int(double(x) / w * 128.0 + 0.5);
629
630 if (k < resize.min) k = resize.min;
631 else if (k > resize.max) k = resize.max;
632
633 if (k != resize.pos) {
634 if (resize.mode == resize.undecided) {
635 if (k < resize.pos) {
636 // edit high limit of prev_region
637 resize.max = resize.region->KeyRange.low;
638 resize.region = resize.prev_region;
639 resize.mode = resize.moving_high_limit;
640 } else {
641 // edit low limit of region
642 resize.min = resize.prev_region->KeyRange.high + 1;
643 resize.mode = resize.moving_low_limit;
644 }
645 }
646 resize.pos = k;
647
648 int x1, x2;
649 if (resize.mode == resize.moving_high_limit) {
650 if (resize.region->KeyRange.high < resize.pos - 1) {
651 x1 = resize.region->KeyRange.high;
652 x2 = resize.pos - 1;
653 } else {
654 x1 = resize.pos - 1;
655 x2 = resize.region->KeyRange.high;
656 }
657 } else {
658 if (resize.region->KeyRange.low < resize.pos) {
659 x1 = resize.region->KeyRange.low;
660 x2 = resize.pos;
661 } else {
662 x1 = resize.pos;
663 x2 = resize.region->KeyRange.low;
664 }
665 }
666 x1 = key_to_x(x1, w);
667 x2 = key_to_x(x2 + 1, w) + 1;
668 Gdk::Rectangle rect(x1, 0, x2 - x1, h1);
669
670 update_after_resize();
671
672 get_window()->invalidate_rect(rect, false);
673 }
674 }
675
676 void RegionChooser::motion_move_region(int x, int y)
677 {
678 const int w = get_width() - 1;
679
680 int l = int(double(x - move.offset) / w * 128.0 + 0.5);
681
682 if (l == region->KeyRange.low) return;
683 int new_l;
684 int regionsize = region->KeyRange.high - region->KeyRange.low;
685 int a = 0;
686 if (l > region->KeyRange.low) {
687 for (gig::Region* r = regions.first() ; ; r = regions.next()) {
688 if (r != region) {
689 int b = r ? r->KeyRange.low : 128;
690
691 // gap: from a to b (not inclusive b)
692
693 if (region->KeyRange.high >= b) {
694 // not found the current gap yet, just continue
695 } else {
696
697 if (a > l) {
698 // this gap is too far to the right, break
699 break;
700 }
701
702 int newhigh = std::min(l + regionsize, b - 1);
703 int newlo = newhigh - regionsize;
704
705 if (newlo >= a) {
706 // yes it fits - it's a candidate
707 new_l = newlo;
708 }
709 }
710 if (!r) break;
711 a = r->KeyRange.high + 1;
712 }
713 }
714 } else {
715 for (gig::Region* r = regions.first() ; ; r = regions.next()) {
716 if (r != region) {
717 int b = r ? r->KeyRange.low : 128;
718
719 // gap from a to b (not inclusive b)
720
721 if (l + regionsize >= b) {
722 // not found the current gap yet, just continue
723 } else {
724
725 if (a > region->KeyRange.low) {
726 // this gap is too far to the right, break
727 break;
728 }
729
730 int newlo = std::max(l, a);
731 int newhigh = newlo + regionsize;
732
733 if (newhigh < b) {
734 // yes it fits - break as the first one is the best
735 new_l = newlo;
736 break;
737 }
738 }
739 if (!r) break;
740 a = r->KeyRange.high + 1;
741 }
742 }
743 }
744 if (new_l == region->KeyRange.low) return;
745
746 int x1 = key_to_x(std::min(int(region->KeyRange.low), new_l), w);
747 int x2 = key_to_x(std::max(int(region->KeyRange.high),
748 new_l + regionsize) + 1, w) + 1;
749
750 Gdk::Rectangle rect(x1, 0, x2 - x1, h1);
751 update_after_move(new_l);
752
753 get_window()->invalidate_rect(rect, false);
754 }
755
756
757 bool RegionChooser::on_motion_notify_event(GdkEventMotion* event)
758 {
759 Glib::RefPtr<Gdk::Window> window = get_window();
760 int x, y;
761 Gdk::ModifierType state = Gdk::ModifierType(0);
762 window->get_pointer(x, y, state);
763
764 // handle virtual MIDI keyboard
765 if (m_VirtKeybModeChoice.get_value() != VIRT_KEYBOARD_MODE_CHORD &&
766 currentActiveKey > 0 &&
767 event->y >= REGION_BLOCK_HEIGHT &&
768 event->y < REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT)
769 {
770 const int k = x_to_key(event->x, get_width() - 1);
771 if (k != currentActiveKey) {
772 int velocity =
773 (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
774 int(float(event->y - REGION_BLOCK_HEIGHT) /
775 float(KEYBOARD_HEIGHT) * 128.0f) + 1;
776 if (velocity <= 0) velocity = 1;
777 keyboard_key_released_signal.emit(currentActiveKey, velocity);
778 currentActiveKey = k;
779 keyboard_key_hit_signal.emit(k, velocity);
780 }
781 }
782
783 if (resize.active) {
784 motion_resize_region(x, y);
785 } else if (move.active) {
786 motion_move_region(x, y);
787 } else {
788 if (is_in_resize_zone(x, y)) {
789 if (!cursor_is_resize) {
790 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
791 window->set_cursor(Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW));
792 #else
793 window->set_cursor(Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW));
794 #endif
795 cursor_is_resize = true;
796 }
797 } else if (cursor_is_resize) {
798 window->set_cursor();
799 cursor_is_resize = false;
800 }
801 }
802
803 return true;
804 }
805
806 bool RegionChooser::is_in_resize_zone(double x, double y) {
807 const int w = get_width() - 1;
808
809 if (instrument && y >= 0 && y <= h1) {
810 gig::Region* prev_region = 0;
811 gig::Region* next_region;
812 for (gig::Region* r = regions.first(); r ; r = next_region) {
813 next_region = regions.next();
814
815 int lo = key_to_x(r->KeyRange.low, w);
816 if (x <= lo - 2) break;
817 if (x < lo + 2) {
818 resize.region = r;
819 resize.pos = r->KeyRange.low;
820 resize.max = r->KeyRange.high;
821
822 if (prev_region && prev_region->KeyRange.high + 1 == r->KeyRange.low) {
823 // we don't know yet if it's the high limit of
824 // prev_region or the low limit of r that's going
825 // to be edited
826 resize.mode = resize.undecided;
827 resize.min = prev_region->KeyRange.low + 1;
828 resize.prev_region = prev_region;
829 return resize.min != resize.max;
830 }
831
832 // edit low limit
833 resize.mode = resize.moving_low_limit;
834 resize.min = prev_region ? prev_region->KeyRange.high + 1 : 0;
835 return resize.min != resize.max;
836 }
837 if (!next_region || r->KeyRange.high + 1 != next_region->KeyRange.low) {
838 int hi = key_to_x(r->KeyRange.high + 1, w);
839 if (x <= hi - 2) break;
840 if (x < hi + 2) {
841 // edit high limit
842 resize.region = r;
843 resize.pos = r->KeyRange.high + 1;
844 resize.mode = resize.moving_high_limit;
845 resize.min = r->KeyRange.low + 1;
846 resize.max = next_region ? next_region->KeyRange.low : 128;
847 return resize.min != resize.max;
848 }
849 }
850 prev_region = r;
851 }
852 }
853 return false;
854 }
855
856 sigc::signal<void>& RegionChooser::signal_region_selected()
857 {
858 return region_selected;
859 }
860
861 sigc::signal<void>& RegionChooser::signal_instrument_changed()
862 {
863 return instrument_changed;
864 }
865
866 void RegionChooser::show_region_properties()
867 {
868 if (!region) return;
869 Gtk::Dialog dialog(_("Region Properties"), true /*modal*/);
870 // add "Keygroup" checkbox
871 Gtk::CheckButton checkBoxKeygroup(_("Member of a Keygroup (Exclusive Group)"));
872 checkBoxKeygroup.set_active(region->KeyGroup);
873 dialog.get_vbox()->pack_start(checkBoxKeygroup);
874 checkBoxKeygroup.show();
875 // add "Keygroup" spinbox
876 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
877 Gtk::Adjustment adjustment(1, 1, 999);
878 Gtk::SpinButton spinBox(adjustment);
879 #else
880 Gtk::SpinButton spinBox(Gtk::Adjustment::create(1, 1, 999));
881 #endif
882 if (region->KeyGroup) spinBox.set_value(region->KeyGroup);
883 dialog.get_vbox()->pack_start(spinBox);
884 spinBox.show();
885 // add OK and CANCEL buttons to the dialog
886 dialog.add_button(Gtk::Stock::OK, 0);
887 dialog.add_button(Gtk::Stock::CANCEL, 1);
888 dialog.show_all_children();
889 if (!dialog.run()) { // OK selected ...
890 region->KeyGroup =
891 (checkBoxKeygroup.get_active()) ? spinBox.get_value_as_int() : 0;
892 }
893 }
894
895 void RegionChooser::add_region()
896 {
897 instrument_struct_to_be_changed_signal.emit(instrument);
898
899 region = instrument->AddRegion();
900 region->SetKeyRange(new_region_pos, new_region_pos);
901
902 instrument_struct_changed_signal.emit(instrument);
903 regions.update(instrument);
904
905 queue_draw();
906 region_selected();
907 dimensionManager.set_region(region);
908 instrument_changed();
909 }
910
911 void RegionChooser::delete_region()
912 {
913 instrument_struct_to_be_changed_signal.emit(instrument);
914 instrument->DeleteRegion(region);
915 instrument_struct_changed_signal.emit(instrument);
916 regions.update(instrument);
917
918 region = 0;
919 queue_draw();
920 region_selected();
921 dimensionManager.set_region(region);
922 instrument_changed();
923 }
924
925 void RegionChooser::manage_dimensions()
926 {
927 gig::Region* region = get_region();
928 if (!region) return;
929 dimensionManager.show(region);
930 }
931
932 void RegionChooser::on_dimension_manager_changed() {
933 region_selected();
934 instrument_changed();
935 }
936
937 sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_to_be_changed() {
938 return instrument_struct_to_be_changed_signal;
939 }
940
941 sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_changed() {
942 return instrument_struct_changed_signal;
943 }
944
945 sigc::signal<void, gig::Region*>& RegionChooser::signal_region_to_be_changed() {
946 return region_to_be_changed_signal;
947 }
948
949 sigc::signal<void, gig::Region*>& RegionChooser::signal_region_changed_signal() {
950 return region_changed_signal;
951 }
952
953 sigc::signal<void, int/*key*/, int/*velocity*/>& RegionChooser::signal_keyboard_key_hit() {
954 return keyboard_key_hit_signal;
955 }
956
957 sigc::signal<void, int/*key*/, int/*velocity*/>& RegionChooser::signal_keyboard_key_released() {
958 return keyboard_key_released_signal;
959 }

  ViewVC Help
Powered by ViewVC