/[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 2627 - (show annotations) (download)
Thu Jun 12 16:12:55 2014 UTC (9 years, 10 months ago) by schoenebeck
File size: 33187 byte(s)
* Increased region selector height by 50%.
* Increased dimension region selector height by 20%.
* Some minor fixes.
* Updated "about" dialog: not so immature anymore ;-).
* Configure script: require at least LinuxSampler v1.0.0.

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

  ViewVC Help
Powered by ViewVC