/[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 2695 - (show annotations) (download)
Tue Jan 6 18:11:27 2015 UTC (9 years, 2 months ago) by schoenebeck
File size: 33614 byte(s)
* Sample Referenve View Dialog: Clicking on a reference in the list closes
  the dialog and jumps directly to the respective instrument, region and
  dimension region the respective sample reference is located at.

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
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::set_region(gig::Region* region) {
625 this->region = region;
626 queue_draw();
627 region_selected();
628 dimensionManager.set_region(region);
629 }
630
631 void RegionChooser::motion_resize_region(int x, int y)
632 {
633 const int w = get_width() - 1;
634
635 int k = int(double(x) / w * 128.0 + 0.5);
636
637 if (k < resize.min) k = resize.min;
638 else if (k > resize.max) k = resize.max;
639
640 if (k != resize.pos) {
641 if (resize.mode == resize.undecided) {
642 if (k < resize.pos) {
643 // edit high limit of prev_region
644 resize.max = resize.region->KeyRange.low;
645 resize.region = resize.prev_region;
646 resize.mode = resize.moving_high_limit;
647 } else {
648 // edit low limit of region
649 resize.min = resize.prev_region->KeyRange.high + 1;
650 resize.mode = resize.moving_low_limit;
651 }
652 }
653 resize.pos = k;
654
655 int x1, x2;
656 if (resize.mode == resize.moving_high_limit) {
657 if (resize.region->KeyRange.high < resize.pos - 1) {
658 x1 = resize.region->KeyRange.high;
659 x2 = resize.pos - 1;
660 } else {
661 x1 = resize.pos - 1;
662 x2 = resize.region->KeyRange.high;
663 }
664 } else {
665 if (resize.region->KeyRange.low < resize.pos) {
666 x1 = resize.region->KeyRange.low;
667 x2 = resize.pos;
668 } else {
669 x1 = resize.pos;
670 x2 = resize.region->KeyRange.low;
671 }
672 }
673 x1 = key_to_x(x1, w);
674 x2 = key_to_x(x2 + 1, w) + 1;
675 Gdk::Rectangle rect(x1, 0, x2 - x1, h1);
676
677 update_after_resize();
678
679 get_window()->invalidate_rect(rect, false);
680 }
681 }
682
683 void RegionChooser::motion_move_region(int x, int y)
684 {
685 const int w = get_width() - 1;
686
687 int l = int(double(x - move.offset) / w * 128.0 + 0.5);
688
689 if (l == region->KeyRange.low) return;
690 int new_l;
691 int regionsize = region->KeyRange.high - region->KeyRange.low;
692 int a = 0;
693 if (l > region->KeyRange.low) {
694 for (gig::Region* r = regions.first() ; ; r = regions.next()) {
695 if (r != region) {
696 int b = r ? r->KeyRange.low : 128;
697
698 // gap: from a to b (not inclusive b)
699
700 if (region->KeyRange.high >= b) {
701 // not found the current gap yet, just continue
702 } else {
703
704 if (a > l) {
705 // this gap is too far to the right, break
706 break;
707 }
708
709 int newhigh = std::min(l + regionsize, b - 1);
710 int newlo = newhigh - regionsize;
711
712 if (newlo >= a) {
713 // yes it fits - it's a candidate
714 new_l = newlo;
715 }
716 }
717 if (!r) break;
718 a = r->KeyRange.high + 1;
719 }
720 }
721 } else {
722 for (gig::Region* r = regions.first() ; ; r = regions.next()) {
723 if (r != region) {
724 int b = r ? r->KeyRange.low : 128;
725
726 // gap from a to b (not inclusive b)
727
728 if (l + regionsize >= b) {
729 // not found the current gap yet, just continue
730 } else {
731
732 if (a > region->KeyRange.low) {
733 // this gap is too far to the right, break
734 break;
735 }
736
737 int newlo = std::max(l, a);
738 int newhigh = newlo + regionsize;
739
740 if (newhigh < b) {
741 // yes it fits - break as the first one is the best
742 new_l = newlo;
743 break;
744 }
745 }
746 if (!r) break;
747 a = r->KeyRange.high + 1;
748 }
749 }
750 }
751 if (new_l == region->KeyRange.low) return;
752
753 int x1 = key_to_x(std::min(int(region->KeyRange.low), new_l), w);
754 int x2 = key_to_x(std::max(int(region->KeyRange.high),
755 new_l + regionsize) + 1, w) + 1;
756
757 Gdk::Rectangle rect(x1, 0, x2 - x1, h1);
758 update_after_move(new_l);
759
760 get_window()->invalidate_rect(rect, false);
761 }
762
763
764 bool RegionChooser::on_motion_notify_event(GdkEventMotion* event)
765 {
766 Glib::RefPtr<Gdk::Window> window = get_window();
767 int x, y;
768 Gdk::ModifierType state = Gdk::ModifierType(0);
769 window->get_pointer(x, y, state);
770
771 // handle virtual MIDI keyboard
772 if (m_VirtKeybModeChoice.get_value() != VIRT_KEYBOARD_MODE_CHORD &&
773 currentActiveKey > 0 &&
774 event->y >= REGION_BLOCK_HEIGHT &&
775 event->y < REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT)
776 {
777 const int k = x_to_key(event->x, get_width() - 1);
778 if (k != currentActiveKey) {
779 int velocity =
780 (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
781 int(float(event->y - REGION_BLOCK_HEIGHT) /
782 float(KEYBOARD_HEIGHT) * 128.0f) + 1;
783 if (velocity <= 0) velocity = 1;
784 keyboard_key_released_signal.emit(currentActiveKey, velocity);
785 currentActiveKey = k;
786 keyboard_key_hit_signal.emit(k, velocity);
787 }
788 }
789
790 if (resize.active) {
791 motion_resize_region(x, y);
792 } else if (move.active) {
793 motion_move_region(x, y);
794 } else {
795 if (is_in_resize_zone(x, y)) {
796 if (!cursor_is_resize) {
797 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
798 window->set_cursor(Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW));
799 #else
800 window->set_cursor(Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW));
801 #endif
802 cursor_is_resize = true;
803 }
804 } else if (cursor_is_resize) {
805 window->set_cursor();
806 cursor_is_resize = false;
807 }
808 }
809
810 return true;
811 }
812
813 bool RegionChooser::is_in_resize_zone(double x, double y) {
814 const int w = get_width() - 1;
815
816 if (instrument && y >= 0 && y <= h1) {
817 gig::Region* prev_region = 0;
818 gig::Region* next_region;
819 for (gig::Region* r = regions.first(); r ; r = next_region) {
820 next_region = regions.next();
821
822 int lo = key_to_x(r->KeyRange.low, w);
823 if (x <= lo - 2) break;
824 if (x < lo + 2) {
825 resize.region = r;
826 resize.pos = r->KeyRange.low;
827 resize.max = r->KeyRange.high;
828
829 if (prev_region && prev_region->KeyRange.high + 1 == r->KeyRange.low) {
830 // we don't know yet if it's the high limit of
831 // prev_region or the low limit of r that's going
832 // to be edited
833 resize.mode = resize.undecided;
834 resize.min = prev_region->KeyRange.low + 1;
835 resize.prev_region = prev_region;
836 return resize.min != resize.max;
837 }
838
839 // edit low limit
840 resize.mode = resize.moving_low_limit;
841 resize.min = prev_region ? prev_region->KeyRange.high + 1 : 0;
842 return resize.min != resize.max;
843 }
844 if (!next_region || r->KeyRange.high + 1 != next_region->KeyRange.low) {
845 int hi = key_to_x(r->KeyRange.high + 1, w);
846 if (x <= hi - 2) break;
847 if (x < hi + 2) {
848 // edit high limit
849 resize.region = r;
850 resize.pos = r->KeyRange.high + 1;
851 resize.mode = resize.moving_high_limit;
852 resize.min = r->KeyRange.low + 1;
853 resize.max = next_region ? next_region->KeyRange.low : 128;
854 return resize.min != resize.max;
855 }
856 }
857 prev_region = r;
858 }
859 }
860 return false;
861 }
862
863 sigc::signal<void>& RegionChooser::signal_region_selected()
864 {
865 return region_selected;
866 }
867
868 sigc::signal<void>& RegionChooser::signal_instrument_changed()
869 {
870 return instrument_changed;
871 }
872
873 void RegionChooser::show_region_properties()
874 {
875 if (!region) return;
876 Gtk::Dialog dialog(_("Region Properties"), true /*modal*/);
877 // add "Keygroup" checkbox
878 Gtk::CheckButton checkBoxKeygroup(_("Member of a Keygroup (Exclusive Group)"));
879 checkBoxKeygroup.set_active(region->KeyGroup);
880 dialog.get_vbox()->pack_start(checkBoxKeygroup);
881 checkBoxKeygroup.show();
882 // add "Keygroup" spinbox
883 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
884 Gtk::Adjustment adjustment(1, 1, 999);
885 Gtk::SpinButton spinBox(adjustment);
886 #else
887 Gtk::SpinButton spinBox(Gtk::Adjustment::create(1, 1, 999));
888 #endif
889 if (region->KeyGroup) spinBox.set_value(region->KeyGroup);
890 dialog.get_vbox()->pack_start(spinBox);
891 spinBox.show();
892 // add OK and CANCEL buttons to the dialog
893 dialog.add_button(Gtk::Stock::OK, 0);
894 dialog.add_button(Gtk::Stock::CANCEL, 1);
895 dialog.show_all_children();
896 if (!dialog.run()) { // OK selected ...
897 region->KeyGroup =
898 (checkBoxKeygroup.get_active()) ? spinBox.get_value_as_int() : 0;
899 }
900 }
901
902 void RegionChooser::add_region()
903 {
904 instrument_struct_to_be_changed_signal.emit(instrument);
905
906 region = instrument->AddRegion();
907 region->SetKeyRange(new_region_pos, new_region_pos);
908
909 instrument_struct_changed_signal.emit(instrument);
910 regions.update(instrument);
911
912 queue_draw();
913 region_selected();
914 dimensionManager.set_region(region);
915 instrument_changed();
916 }
917
918 void RegionChooser::delete_region()
919 {
920 instrument_struct_to_be_changed_signal.emit(instrument);
921 instrument->DeleteRegion(region);
922 instrument_struct_changed_signal.emit(instrument);
923 regions.update(instrument);
924
925 region = 0;
926 queue_draw();
927 region_selected();
928 dimensionManager.set_region(region);
929 instrument_changed();
930 }
931
932 void RegionChooser::manage_dimensions()
933 {
934 gig::Region* region = get_region();
935 if (!region) return;
936 dimensionManager.show(region);
937 }
938
939 void RegionChooser::on_dimension_manager_changed() {
940 region_selected();
941 instrument_changed();
942 }
943
944 sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_to_be_changed() {
945 return instrument_struct_to_be_changed_signal;
946 }
947
948 sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_changed() {
949 return instrument_struct_changed_signal;
950 }
951
952 sigc::signal<void, gig::Region*>& RegionChooser::signal_region_to_be_changed() {
953 return region_to_be_changed_signal;
954 }
955
956 sigc::signal<void, gig::Region*>& RegionChooser::signal_region_changed_signal() {
957 return region_changed_signal;
958 }
959
960 sigc::signal<void, int/*key*/, int/*velocity*/>& RegionChooser::signal_keyboard_key_hit() {
961 return keyboard_key_hit_signal;
962 }
963
964 sigc::signal<void, int/*key*/, int/*velocity*/>& RegionChooser::signal_keyboard_key_released() {
965 return keyboard_key_released_signal;
966 }

  ViewVC Help
Powered by ViewVC