/[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 2536 - (show annotations) (download)
Mon Apr 21 17:49:17 2014 UTC (9 years, 11 months ago) by schoenebeck
File size: 32830 byte(s)
* if there is no region yet, show a red hint text to the user that he may
  right click on the region chooser area to add a region
* added tooltips to main menu entries (was buggy before)
* added tooltips to instruments tree view and samples tree view
* added various tooltips and adjusted some labels on the region settings
  pane

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 20
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 Gdk::Cairo::set_source_rgba(cr, red);
343 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 16) || GTKMM_MAJOR_VERSION < 2
344 pango_cairo_show_layout(cr->cobj(), layout->gobj());
345 #else
346 layout->show_in_cairo_context(cr);
347 #endif
348 }
349 }
350
351 bool RegionChooser::is_black_key(int key) {
352 const int note = (key + 3) % 12;
353 return note == 1 || note == 4 || note == 6 || note == 9 || note == 11;
354 }
355
356 void RegionChooser::draw_digit(const Cairo::RefPtr<Cairo::Context>& cr,
357 int key) {
358 const int h = KEYBOARD_HEIGHT;
359 const int w = get_width() - 1;
360 Glib::RefPtr<Pango::Layout> layout =
361 Pango::Layout::create(get_pango_context());
362 char buf[30];
363 sprintf(buf, "<span size=\"8000\">%d</span>", key / 12 - 1);
364 layout->set_markup(buf);
365 Pango::Rectangle rectangle = layout->get_logical_extents();
366 double text_w = double(rectangle.get_width()) / Pango::SCALE;
367 double text_h = double(rectangle.get_height()) / Pango::SCALE;
368 double x = w * (key + 0.75) / 128.0;
369 Gdk::Cairo::set_source_rgba(cr, black);
370 cr->move_to(int(x - text_w / 2 + 1), int(h1 + h - text_h + 0.5));
371 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 16) || GTKMM_MAJOR_VERSION < 2
372 pango_cairo_show_layout(cr->cobj(), layout->gobj());
373 #else
374 layout->show_in_cairo_context(cr);
375 #endif
376 }
377
378 void RegionChooser::draw_key(const Cairo::RefPtr<Cairo::Context>& cr,
379 int key) {
380 const int h = KEYBOARD_HEIGHT;
381 const int w = get_width() - 1;
382 const int bh = int(h * 0.55);
383
384 Gdk::Cairo::set_source_rgba(cr, activeKeyColor);
385
386 int note = (key + 3) % 12;
387 int x = key_to_x(key, w) + 1;
388 int x2 = key_to_x(key + 1.5, w);
389 int x3 = key_to_x(key + 1, w);
390 int x4 = key_to_x(key - 0.5, w);
391 int w1 = x3 - x;
392 switch (note) {
393 case 0: case 5: case 10:
394 cr->rectangle(x, h1 + 1, w1, bh);
395 cr->fill();
396 cr->rectangle(x4 + 1, h1 + bh + 1, x2 - x4 - 1, h - bh - 2);
397 cr->fill();
398 break;
399 case 2: case 7:
400 cr->rectangle(x, h1 + 1, w1, bh);
401 cr->fill();
402 cr->rectangle(x4 + 1, h1 + bh + 1, x3 - x4 - 1, h - bh - 2);
403 cr->fill();
404 break;
405 case 3: case 8:
406 cr->rectangle(x, h1 + 1, w1, bh);
407 cr->fill();
408 cr->rectangle(x, h1 + bh + 1, x2 - x, h - bh - 2);
409 cr->fill();
410 break;
411 default:
412 cr->rectangle(x, h1 + 1, w1, bh - 1);
413 cr->fill();
414 break;
415 }
416 Gdk::Cairo::set_source_rgba(cr, black);
417 }
418
419 void RegionChooser::set_instrument(gig::Instrument* instrument)
420 {
421 this->instrument = instrument;
422 regions.update(instrument);
423 region = regions.first();
424 queue_draw();
425 region_selected();
426 dimensionManager.set_region(region);
427 }
428
429 bool RegionChooser::on_button_release_event(GdkEventButton* event)
430 {
431 const int k = x_to_key(event->x, get_width() - 1);
432
433 // handle-note off on virtual keyboard
434 if (event->type == GDK_BUTTON_RELEASE) {
435 int velocity = (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
436 int(float(event->y - REGION_BLOCK_HEIGHT) / float(KEYBOARD_HEIGHT) * 128.0f) + 1;
437 if (velocity <= 0) velocity = 1;
438 switch (m_VirtKeybModeChoice.get_value()) {
439 case VIRT_KEYBOARD_MODE_CHORD:
440 if (event->y >= REGION_BLOCK_HEIGHT)
441 keyboard_key_released_signal.emit(k, velocity);
442 break;
443 case VIRT_KEYBOARD_MODE_NORMAL:
444 default:
445 if (currentActiveKey >= 0 && currentActiveKey <= 127) {
446 keyboard_key_released_signal.emit(currentActiveKey, velocity);
447 currentActiveKey = -1;
448 }
449 break;
450 }
451 }
452
453 if (resize.active) {
454 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
455 get_window()->pointer_ungrab(event->time);
456 #else
457 Glib::wrap(event->device, true)->ungrab(event->time);
458 #endif
459 resize.active = false;
460
461 if (!is_in_resize_zone(event->x, event->y) && cursor_is_resize) {
462 get_window()->set_cursor();
463 cursor_is_resize = false;
464 }
465 } else if (move.active) {
466 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
467 get_window()->pointer_ungrab(event->time);
468 #else
469 Glib::wrap(event->device, true)->ungrab(event->time);
470 #endif
471 move.active = false;
472
473 if (is_in_resize_zone(event->x, event->y)) {
474 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
475 get_window()->set_cursor(Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW));
476 #else
477 get_window()->set_cursor(Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW));
478 #endif
479 cursor_is_resize = true;
480 }
481 }
482 return true;
483 }
484
485 void RegionChooser::update_after_resize()
486 {
487 if (resize.mode == resize.moving_high_limit) {
488 if (resize.region->KeyRange.high != resize.pos - 1) {
489 instrument_struct_to_be_changed_signal.emit(instrument);
490 resize.region->SetKeyRange(resize.region->KeyRange.low,
491 resize.pos - 1);
492 regions.update(instrument);
493 instrument_changed.emit();
494 instrument_struct_changed_signal.emit(instrument);
495 }
496 } else if (resize.mode == resize.moving_low_limit) {
497 if (resize.region->KeyRange.low != resize.pos) {
498 instrument_struct_to_be_changed_signal.emit(instrument);
499 resize.region->SetKeyRange(resize.pos,
500 resize.region->KeyRange.high);
501 regions.update(instrument);
502 instrument_changed.emit();
503 instrument_struct_changed_signal.emit(instrument);
504 }
505 }
506 }
507
508 void RegionChooser::update_after_move(int pos)
509 {
510 instrument_struct_to_be_changed_signal.emit(instrument);
511 region->SetKeyRange(pos, pos + region->KeyRange.high -
512 region->KeyRange.low);
513 regions.update(instrument);
514 instrument_changed.emit();
515 instrument_struct_changed_signal.emit(instrument);
516 }
517
518 bool RegionChooser::on_button_press_event(GdkEventButton* event)
519 {
520 if (!instrument) return true;
521
522 const int w = get_width() - 1;
523 const int k = x_to_key(event->x, w);
524
525 if (event->type == GDK_BUTTON_PRESS) {
526 if (event->y >= REGION_BLOCK_HEIGHT) {
527 int velocity = (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
528 int(float(event->y - REGION_BLOCK_HEIGHT) / float(KEYBOARD_HEIGHT) * 128.0f) + 1;
529 currentActiveKey = k;
530 keyboard_key_hit_signal.emit(k, velocity);
531 }
532 }
533
534 if (event->y >= REGION_BLOCK_HEIGHT) return true;
535 if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
536 gig::Region* r = get_region(k);
537 if (r) {
538 region = r;
539 queue_draw();
540 region_selected();
541 dimensionManager.set_region(region);
542 popup_menu_inside_region->popup(event->button, event->time);
543 } else {
544 new_region_pos = k;
545 popup_menu_outside_region->popup(event->button, event->time);
546 }
547 } else {
548 if (is_in_resize_zone(event->x, event->y)) {
549 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
550 get_window()->pointer_grab(false,
551 Gdk::BUTTON_RELEASE_MASK |
552 Gdk::POINTER_MOTION_MASK |
553 Gdk::POINTER_MOTION_HINT_MASK,
554 Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW),
555 event->time);
556 #else
557 Glib::wrap(event->device, true)->grab(get_window(),
558 Gdk::OWNERSHIP_NONE,
559 false,
560 Gdk::BUTTON_RELEASE_MASK |
561 Gdk::POINTER_MOTION_MASK |
562 Gdk::POINTER_MOTION_HINT_MASK,
563 Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW),
564 event->time);
565 #endif
566 resize.active = true;
567 } else {
568 gig::Region* r = get_region(k);
569 if (r) {
570 region = r;
571 queue_draw();
572 region_selected();
573 dimensionManager.set_region(region);
574
575 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
576 get_window()->pointer_grab(false,
577 Gdk::BUTTON_RELEASE_MASK |
578 Gdk::POINTER_MOTION_MASK |
579 Gdk::POINTER_MOTION_HINT_MASK,
580 Gdk::Cursor(Gdk::FLEUR),
581 event->time);
582 #else
583 Glib::wrap(event->device, true)->grab(get_window(),
584 Gdk::OWNERSHIP_NONE,
585 false,
586 Gdk::BUTTON_RELEASE_MASK |
587 Gdk::POINTER_MOTION_MASK |
588 Gdk::POINTER_MOTION_HINT_MASK,
589 Gdk::Cursor::create(Gdk::FLEUR),
590 event->time);
591 #endif
592 move.active = true;
593 move.offset = event->x - key_to_x(region->KeyRange.low, w);
594 }
595 }
596 }
597 return true;
598 }
599
600 gig::Region* RegionChooser::get_region(int key)
601 {
602 for (gig::Region* r = regions.first() ; r ; r = regions.next()) {
603 if (key < r->KeyRange.low) return 0;
604 if (key <= r->KeyRange.high) return r;
605 }
606 return 0;
607 }
608
609 void RegionChooser::motion_resize_region(int x, int y)
610 {
611 const int w = get_width() - 1;
612
613 int k = int(double(x) / w * 128.0 + 0.5);
614
615 if (k < resize.min) k = resize.min;
616 else if (k > resize.max) k = resize.max;
617
618 if (k != resize.pos) {
619 if (resize.mode == resize.undecided) {
620 if (k < resize.pos) {
621 // edit high limit of prev_region
622 resize.max = resize.region->KeyRange.low;
623 resize.region = resize.prev_region;
624 resize.mode = resize.moving_high_limit;
625 } else {
626 // edit low limit of region
627 resize.min = resize.prev_region->KeyRange.high + 1;
628 resize.mode = resize.moving_low_limit;
629 }
630 }
631 resize.pos = k;
632
633 int x1, x2;
634 if (resize.mode == resize.moving_high_limit) {
635 if (resize.region->KeyRange.high < resize.pos - 1) {
636 x1 = resize.region->KeyRange.high;
637 x2 = resize.pos - 1;
638 } else {
639 x1 = resize.pos - 1;
640 x2 = resize.region->KeyRange.high;
641 }
642 } else {
643 if (resize.region->KeyRange.low < resize.pos) {
644 x1 = resize.region->KeyRange.low;
645 x2 = resize.pos;
646 } else {
647 x1 = resize.pos;
648 x2 = resize.region->KeyRange.low;
649 }
650 }
651 x1 = key_to_x(x1, w);
652 x2 = key_to_x(x2 + 1, w) + 1;
653 Gdk::Rectangle rect(x1, 0, x2 - x1, h1);
654
655 update_after_resize();
656
657 get_window()->invalidate_rect(rect, false);
658 }
659 }
660
661 void RegionChooser::motion_move_region(int x, int y)
662 {
663 const int w = get_width() - 1;
664
665 int l = int(double(x - move.offset) / w * 128.0 + 0.5);
666
667 if (l == region->KeyRange.low) return;
668 int new_l;
669 int regionsize = region->KeyRange.high - region->KeyRange.low;
670 int a = 0;
671 if (l > region->KeyRange.low) {
672 for (gig::Region* r = regions.first() ; ; r = regions.next()) {
673 if (r != region) {
674 int b = r ? r->KeyRange.low : 128;
675
676 // gap: from a to b (not inclusive b)
677
678 if (region->KeyRange.high >= b) {
679 // not found the current gap yet, just continue
680 } else {
681
682 if (a > l) {
683 // this gap is too far to the right, break
684 break;
685 }
686
687 int newhigh = std::min(l + regionsize, b - 1);
688 int newlo = newhigh - regionsize;
689
690 if (newlo >= a) {
691 // yes it fits - it's a candidate
692 new_l = newlo;
693 }
694 }
695 if (!r) break;
696 a = r->KeyRange.high + 1;
697 }
698 }
699 } else {
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 (l + regionsize >= b) {
707 // not found the current gap yet, just continue
708 } else {
709
710 if (a > region->KeyRange.low) {
711 // this gap is too far to the right, break
712 break;
713 }
714
715 int newlo = std::max(l, a);
716 int newhigh = newlo + regionsize;
717
718 if (newhigh < b) {
719 // yes it fits - break as the first one is the best
720 new_l = newlo;
721 break;
722 }
723 }
724 if (!r) break;
725 a = r->KeyRange.high + 1;
726 }
727 }
728 }
729 if (new_l == region->KeyRange.low) return;
730
731 int x1 = key_to_x(std::min(int(region->KeyRange.low), new_l), w);
732 int x2 = key_to_x(std::max(int(region->KeyRange.high),
733 new_l + regionsize) + 1, w) + 1;
734
735 Gdk::Rectangle rect(x1, 0, x2 - x1, h1);
736 update_after_move(new_l);
737
738 get_window()->invalidate_rect(rect, false);
739 }
740
741
742 bool RegionChooser::on_motion_notify_event(GdkEventMotion* event)
743 {
744 Glib::RefPtr<Gdk::Window> window = get_window();
745 int x, y;
746 Gdk::ModifierType state = Gdk::ModifierType(0);
747 window->get_pointer(x, y, state);
748
749 // handle virtual MIDI keyboard
750 if (m_VirtKeybModeChoice.get_value() != VIRT_KEYBOARD_MODE_CHORD &&
751 currentActiveKey > 0 &&
752 event->y >= REGION_BLOCK_HEIGHT &&
753 event->y < REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT)
754 {
755 const int k = x_to_key(event->x, get_width() - 1);
756 if (k != currentActiveKey) {
757 int velocity =
758 (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
759 int(float(event->y - REGION_BLOCK_HEIGHT) /
760 float(KEYBOARD_HEIGHT) * 128.0f) + 1;
761 if (velocity <= 0) velocity = 1;
762 keyboard_key_released_signal.emit(currentActiveKey, velocity);
763 currentActiveKey = k;
764 keyboard_key_hit_signal.emit(k, velocity);
765 }
766 }
767
768 if (resize.active) {
769 motion_resize_region(x, y);
770 } else if (move.active) {
771 motion_move_region(x, y);
772 } else {
773 if (is_in_resize_zone(x, y)) {
774 if (!cursor_is_resize) {
775 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
776 window->set_cursor(Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW));
777 #else
778 window->set_cursor(Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW));
779 #endif
780 cursor_is_resize = true;
781 }
782 } else if (cursor_is_resize) {
783 window->set_cursor();
784 cursor_is_resize = false;
785 }
786 }
787
788 return true;
789 }
790
791 bool RegionChooser::is_in_resize_zone(double x, double y) {
792 const int w = get_width() - 1;
793
794 if (instrument && y >= 0 && y <= h1) {
795 gig::Region* prev_region = 0;
796 gig::Region* next_region;
797 for (gig::Region* r = regions.first(); r ; r = next_region) {
798 next_region = regions.next();
799
800 int lo = key_to_x(r->KeyRange.low, w);
801 if (x <= lo - 2) break;
802 if (x < lo + 2) {
803 resize.region = r;
804 resize.pos = r->KeyRange.low;
805 resize.max = r->KeyRange.high;
806
807 if (prev_region && prev_region->KeyRange.high + 1 == r->KeyRange.low) {
808 // we don't know yet if it's the high limit of
809 // prev_region or the low limit of r that's going
810 // to be edited
811 resize.mode = resize.undecided;
812 resize.min = prev_region->KeyRange.low + 1;
813 resize.prev_region = prev_region;
814 return resize.min != resize.max;
815 }
816
817 // edit low limit
818 resize.mode = resize.moving_low_limit;
819 resize.min = prev_region ? prev_region->KeyRange.high + 1 : 0;
820 return resize.min != resize.max;
821 }
822 if (!next_region || r->KeyRange.high + 1 != next_region->KeyRange.low) {
823 int hi = key_to_x(r->KeyRange.high + 1, w);
824 if (x <= hi - 2) break;
825 if (x < hi + 2) {
826 // edit high limit
827 resize.region = r;
828 resize.pos = r->KeyRange.high + 1;
829 resize.mode = resize.moving_high_limit;
830 resize.min = r->KeyRange.low + 1;
831 resize.max = next_region ? next_region->KeyRange.low : 128;
832 return resize.min != resize.max;
833 }
834 }
835 prev_region = r;
836 }
837 }
838 return false;
839 }
840
841 sigc::signal<void>& RegionChooser::signal_region_selected()
842 {
843 return region_selected;
844 }
845
846 sigc::signal<void>& RegionChooser::signal_instrument_changed()
847 {
848 return instrument_changed;
849 }
850
851 void RegionChooser::show_region_properties()
852 {
853 if (!region) return;
854 Gtk::Dialog dialog(_("Region Properties"), true /*modal*/);
855 // add "Keygroup" checkbox
856 Gtk::CheckButton checkBoxKeygroup(_("Member of a Keygroup (Exclusive Group)"));
857 checkBoxKeygroup.set_active(region->KeyGroup);
858 dialog.get_vbox()->pack_start(checkBoxKeygroup);
859 checkBoxKeygroup.show();
860 // add "Keygroup" spinbox
861 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
862 Gtk::Adjustment adjustment(1, 1, 999);
863 Gtk::SpinButton spinBox(adjustment);
864 #else
865 Gtk::SpinButton spinBox(Gtk::Adjustment::create(1, 1, 999));
866 #endif
867 if (region->KeyGroup) spinBox.set_value(region->KeyGroup);
868 dialog.get_vbox()->pack_start(spinBox);
869 spinBox.show();
870 // add OK and CANCEL buttons to the dialog
871 dialog.add_button(Gtk::Stock::OK, 0);
872 dialog.add_button(Gtk::Stock::CANCEL, 1);
873 dialog.show_all_children();
874 if (!dialog.run()) { // OK selected ...
875 region->KeyGroup =
876 (checkBoxKeygroup.get_active()) ? spinBox.get_value_as_int() : 0;
877 }
878 }
879
880 void RegionChooser::add_region()
881 {
882 instrument_struct_to_be_changed_signal.emit(instrument);
883
884 region = instrument->AddRegion();
885 region->SetKeyRange(new_region_pos, new_region_pos);
886
887 instrument_struct_changed_signal.emit(instrument);
888 regions.update(instrument);
889
890 queue_draw();
891 region_selected();
892 dimensionManager.set_region(region);
893 instrument_changed();
894 }
895
896 void RegionChooser::delete_region()
897 {
898 instrument_struct_to_be_changed_signal.emit(instrument);
899 instrument->DeleteRegion(region);
900 instrument_struct_changed_signal.emit(instrument);
901 regions.update(instrument);
902
903 region = 0;
904 queue_draw();
905 region_selected();
906 dimensionManager.set_region(region);
907 instrument_changed();
908 }
909
910 void RegionChooser::manage_dimensions()
911 {
912 gig::Region* region = get_region();
913 if (!region) return;
914 dimensionManager.show(region);
915 }
916
917 void RegionChooser::on_dimension_manager_changed() {
918 region_selected();
919 instrument_changed();
920 }
921
922 sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_to_be_changed() {
923 return instrument_struct_to_be_changed_signal;
924 }
925
926 sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_changed() {
927 return instrument_struct_changed_signal;
928 }
929
930 sigc::signal<void, gig::Region*>& RegionChooser::signal_region_to_be_changed() {
931 return region_to_be_changed_signal;
932 }
933
934 sigc::signal<void, gig::Region*>& RegionChooser::signal_region_changed_signal() {
935 return region_changed_signal;
936 }
937
938 sigc::signal<void, int/*key*/, int/*velocity*/>& RegionChooser::signal_keyboard_key_hit() {
939 return keyboard_key_hit_signal;
940 }
941
942 sigc::signal<void, int/*key*/, int/*velocity*/>& RegionChooser::signal_keyboard_key_released() {
943 return keyboard_key_released_signal;
944 }

  ViewVC Help
Powered by ViewVC