/[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 2641 - (show annotations) (download)
Mon Jun 16 15:24:54 2014 UTC (9 years, 10 months ago) by schoenebeck
File size: 33384 byte(s)
* Region Chooser: Double click opens dimension manager dialog.
* Dimension Manager: Clicking on dimension type cell of an existing
  dimension opens a popup to alter the dimension type.

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

  ViewVC Help
Powered by ViewVC