/[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 2246 - (show annotations) (download)
Fri Aug 19 10:55:41 2011 UTC (12 years, 7 months ago) by persson
File size: 32008 byte(s)
* gtkmm 3 fix: rewrote the custom widgets (regionchooser and
  dimregionchooser) so they only draw pixels in the on_draw
  method. This should make them work again in newer gtkmm 3
  environments.

1 /*
2 * Copyright (C) 2006-2011 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 #include <sstream>
24
25 #include <cairomm/context.h>
26 #include <gdkmm/general.h>
27 #include <gdkmm/cursor.h>
28 #include <gtkmm/stock.h>
29 #include <gtkmm/spinbutton.h>
30 #include <gtkmm/dialog.h>
31
32 #include "global.h"
33
34 #define REGION_BLOCK_HEIGHT 20
35 #define KEYBOARD_HEIGHT 40
36
37 void SortedRegions::update(gig::Instrument* instrument) {
38 // Usually, the regions in a gig file are ordered after their key
39 // range, but there are files where they are not. The
40 // RegionChooser code needs a sorted list of regions.
41 regions.clear();
42 if (instrument) {
43 for (gig::Region* r = instrument->GetFirstRegion() ;
44 r ;
45 r = instrument->GetNextRegion()) {
46 regions.push_back(r);
47 }
48 sort(regions.begin(), regions.end(), *this);
49 }
50 }
51
52 gig::Region* SortedRegions::first() {
53 region_iterator = regions.begin();
54 return region_iterator == regions.end() ? 0 : *region_iterator;
55 }
56
57 gig::Region* SortedRegions::next() {
58 region_iterator++;
59 return region_iterator == regions.end() ? 0 : *region_iterator;
60 }
61
62
63
64 RegionChooser::RegionChooser() :
65 activeKeyColor("red"),
66 red("#8070ff"),
67 grey1("grey69"),
68 white("white"),
69 black("black"),
70 m_VirtKeybModeChoice(_("Virtual Keyboard Mode")),
71 currentActiveKey(-1)
72 {
73 set_size_request(500, KEYBOARD_HEIGHT + REGION_BLOCK_HEIGHT);
74
75 instrument = 0;
76 region = 0;
77 resize.active = false;
78 move.active = false;
79 cursor_is_resize = false;
80 h1 = REGION_BLOCK_HEIGHT;
81
82 // properties of the virtual keyboard
83 {
84 const char* choices[] = { _("normal"), _("chord"), 0 };
85 static const virt_keyboard_mode_t values[] = {
86 VIRT_KEYBOARD_MODE_NORMAL,
87 VIRT_KEYBOARD_MODE_CHORD
88 };
89 m_VirtKeybModeChoice.set_choices(choices, values);
90 m_VirtKeybModeChoice.set_value(VIRT_KEYBOARD_MODE_NORMAL);
91 }
92 m_VirtKeybVelocityLabelDescr.set_text(_("Note-On Velocity:"));
93 m_VirtKeybVelocityLabel.set_text("-");
94 m_VirtKeybOffVelocityLabelDescr.set_text(_("Note-Off Velocity:"));
95 m_VirtKeybOffVelocityLabel.set_text("-");
96 m_VirtKeybPropsBox.pack_start(m_VirtKeybModeChoice.label, Gtk::PACK_SHRINK);
97 m_VirtKeybPropsBox.pack_start(m_VirtKeybModeChoice.widget, Gtk::PACK_SHRINK);
98 m_VirtKeybPropsBox.pack_start(m_VirtKeybVelocityLabelDescr, Gtk::PACK_SHRINK);
99 m_VirtKeybPropsBox.pack_start(m_VirtKeybVelocityLabel, Gtk::PACK_SHRINK);
100 m_VirtKeybPropsBox.pack_start(m_VirtKeybOffVelocityLabelDescr, Gtk::PACK_SHRINK);
101 m_VirtKeybPropsBox.pack_start(m_VirtKeybOffVelocityLabel, Gtk::PACK_SHRINK);
102 m_VirtKeybPropsBox.set_spacing(10);
103 m_VirtKeybPropsBox.show();
104 for (int i = 0 ; i < 128 ; i++) key_pressed[i] = false;
105
106 actionGroup = Gtk::ActionGroup::create();
107 actionGroup->add(Gtk::Action::create("Properties",
108 Gtk::Stock::PROPERTIES),
109 sigc::mem_fun(*this,
110 &RegionChooser::show_region_properties));
111 actionGroup->add(Gtk::Action::create("Remove", Gtk::Stock::REMOVE),
112 sigc::mem_fun(*this, &RegionChooser::delete_region));
113 actionGroup->add(Gtk::Action::create("Add", Gtk::Stock::ADD),
114 sigc::mem_fun(*this, &RegionChooser::add_region));
115 actionGroup->add(Gtk::Action::create("Dimensions", _("Dimensions...")),
116 sigc::mem_fun(*this, &RegionChooser::manage_dimensions));
117
118 uiManager = Gtk::UIManager::create();
119 uiManager->insert_action_group(actionGroup);
120 Glib::ustring ui_info =
121 "<ui>"
122 " <popup name='PopupMenuInsideRegion'>"
123 " <menuitem action='Properties'/>"
124 " <menuitem action='Dimensions'/>"
125 " <menuitem action='Remove'/>"
126 " </popup>"
127 " <popup name='PopupMenuOutsideRegion'>"
128 " <menuitem action='Add'/>"
129 " </popup>"
130 "</ui>";
131 uiManager->add_ui_from_string(ui_info);
132
133 popup_menu_inside_region = dynamic_cast<Gtk::Menu*>(
134 uiManager->get_widget("/PopupMenuInsideRegion"));
135 popup_menu_outside_region = dynamic_cast<Gtk::Menu*>(
136 uiManager->get_widget("/PopupMenuOutsideRegion"));
137
138 add_events(Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK |
139 Gdk::POINTER_MOTION_MASK | Gdk::POINTER_MOTION_HINT_MASK);
140
141 dimensionManager.region_to_be_changed_signal.connect(
142 region_to_be_changed_signal.make_slot()
143 );
144 dimensionManager.region_changed_signal.connect(
145 region_changed_signal.make_slot()
146 );
147 dimensionManager.region_changed_signal.connect(
148 sigc::hide(
149 sigc::mem_fun(*this, &RegionChooser::on_dimension_manager_changed)
150 )
151 );
152 keyboard_key_hit_signal.connect(
153 sigc::mem_fun(*this, &RegionChooser::on_note_on_event)
154 );
155 keyboard_key_released_signal.connect(
156 sigc::mem_fun(*this, &RegionChooser::on_note_off_event)
157 );
158 }
159
160 RegionChooser::~RegionChooser()
161 {
162 }
163
164 template<class T> inline std::string ToString(T o) {
165 std::stringstream ss;
166 ss << o;
167 return ss.str();
168 }
169
170 void RegionChooser::invalidate_key(int key) {
171 const int h = KEYBOARD_HEIGHT;
172 const int w = get_width() - 1;
173 int x1 = key_to_x(key - 0.5, w);
174 int x2 = key_to_x(key + 1.5, w);
175
176 Gdk::Rectangle rect(x1 + 1, h1 + 1, x2 - x1 - 1, h - 2);
177 get_window()->invalidate_rect(rect, false);
178 }
179
180 void RegionChooser::on_note_on_event(int key, int velocity) {
181 key_pressed[key] = true;
182 invalidate_key(key);
183 m_VirtKeybVelocityLabel.set_text(ToString(velocity));
184 }
185
186 void RegionChooser::on_note_off_event(int key, int velocity) {
187 key_pressed[key] = false;
188 invalidate_key(key);
189 m_VirtKeybOffVelocityLabel.set_text(ToString(velocity));
190 }
191
192
193 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
194 bool RegionChooser::on_expose_event(GdkEventExpose* e) {
195 double clipx1 = e->area.x;
196 double clipx2 = e->area.x + e->area.width;
197 double clipy1 = e->area.y;
198 double clipy2 = e->area.y + e->area.height;
199
200 const Cairo::RefPtr<Cairo::Context>& cr =
201 get_window()->create_cairo_context();
202 #if 0
203 }
204 #endif
205 #else
206 bool RegionChooser::on_draw(const Cairo::RefPtr<Cairo::Context>& cr) {
207 double clipx1, clipx2, clipy1, clipy2;
208 cr->get_clip_extents(clipx1, clipy1, clipx2, clipy2);
209 #endif
210
211 cr->save();
212 cr->set_line_width(1);
213
214 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
215 const Gdk::Color bg = get_style()->get_bg(Gtk::STATE_NORMAL);
216 #else
217 const Gdk::RGBA bg = get_style_context()->get_background_color();
218 #endif
219 Gdk::Cairo::set_source_rgba(cr, bg);
220 cr->paint();
221
222 const int w = get_width() - 1;
223
224 if (clipy2 > h1) {
225 draw_keyboard(cr, clipx1, clipx2);
226 }
227
228 if (clipy1 < h1 && instrument) {
229 draw_regions(cr, clipx1, clipx2);
230 }
231
232 cr->restore();
233
234 return true;
235 }
236
237 void RegionChooser::draw_keyboard(const Cairo::RefPtr<Cairo::Context>& cr,
238 int clip_low, int clip_high) {
239 const int h = KEYBOARD_HEIGHT;
240 const int w = get_width() - 1;
241 const int bh = int(h * 0.55);
242
243 Gdk::Cairo::set_source_rgba(cr, black);
244 cr->rectangle(0.5, h1 + 0.5, w, h - 1);
245 cr->stroke();
246
247 int x1 = key_to_x(20.5, w);
248 Gdk::Cairo::set_source_rgba(cr, grey1);
249 cr->rectangle(1, h1 + 1, x1 - 1, h - 2);
250 cr->fill();
251
252 int x2 = key_to_x(109.5, w);
253 Gdk::Cairo::set_source_rgba(cr, white);
254 cr->rectangle(x1 + 1, h1 + 1, x2 - x1 - 1, h - 2);
255 cr->fill();
256
257 Gdk::Cairo::set_source_rgba(cr, grey1);
258 cr->rectangle(x2 + 1, h1 + 1, w - x2 - 1, h - 2);
259 cr->fill();
260
261 Gdk::Cairo::set_source_rgba(cr, black);
262
263 int clipkey1 = std::max(0, x_to_key_right(clip_low - 1, w));
264 int clipkey2 = std::min(x_to_key_right(clip_high - 1, w) + 1, 128);
265
266 for (int i = clipkey1 ; i < clipkey2 ; i++) {
267 int note = (i + 3) % 12;
268 int x = key_to_x(i, w);
269
270 if (note == 1 || note == 4 || note == 6 ||
271 note == 9 || note == 11) {
272 // black key: short line in the middle, with a rectangle
273 // on top
274 int x2 = key_to_x(i + 0.5, w);
275 cr->move_to(x2 + 0.5, h1 + bh + 0.5);
276 cr->line_to(x2 + 0.5, h1 + h - 1);
277 cr->stroke();
278
279 int x3 = key_to_x(i + 1, w);
280 cr->rectangle(x, h1 + 1, x3 - x + 1, bh);
281 cr->fill();
282 } else if (note == 3 || note == 8) {
283 // C or F: long line to the left
284 cr->move_to(x + 0.5, h1 + 1);
285 cr->line_to(x + 0.5, h1 + h - 1);
286 cr->stroke();
287 }
288
289 if (key_pressed[i]) draw_key(cr, i);
290
291 if (note == 3) draw_digit(cr, i);
292 }
293 }
294
295
296 void RegionChooser::draw_regions(const Cairo::RefPtr<Cairo::Context>& cr,
297 int clip_low, int clip_high) {
298 const int w = get_width() - 1;
299
300 Gdk::Cairo::set_source_rgba(cr, black);
301 gig::Region* next_region;
302 int x3 = -1;
303 for (gig::Region* r = regions.first() ; r ; r = next_region) {
304 next_region = regions.next();
305
306 if (x3 < 0) {
307 x3 = key_to_x(r->KeyRange.low, w);
308 if (x3 >= clip_high) break;
309 }
310 if (!next_region ||
311 r->KeyRange.high + 1 != next_region->KeyRange.low ||
312 r == region || next_region == region) {
313
314 int x2 = key_to_x(r->KeyRange.high + 1, w);
315 if (x2 >= clip_low) {
316 cr->move_to(x3, 0.5);
317 cr->line_to(x2 + 0.5, 0.5);
318 cr->line_to(x2 + 0.5, h1 - 0.5);
319 cr->line_to(x3, h1 - 0.5);
320 cr->stroke();
321
322 Gdk::Cairo::set_source_rgba(cr, region == r ? red : white);
323 cr->rectangle(x3 + 1, 1, x2 - x3 - 1, h1 - 2);
324 cr->fill();
325 Gdk::Cairo::set_source_rgba(cr, black);
326 }
327 x3 = -1;
328 }
329 }
330
331 for (gig::Region* r = regions.first() ; r ; r = regions.next()) {
332 int x = key_to_x(r->KeyRange.low, w);
333
334 if (x < clip_low) continue;
335 if (x >= clip_high) break;
336
337 cr->move_to(x + 0.5, 1);
338 cr->line_to(x + 0.5, h1 - 1);
339 cr->stroke();
340 }
341 }
342
343 bool RegionChooser::is_black_key(int key) {
344 const int note = (key + 3) % 12;
345 return note == 1 || note == 4 || note == 6 || note == 9 || note == 11;
346 }
347
348 void RegionChooser::draw_digit(const Cairo::RefPtr<Cairo::Context>& cr,
349 int key) {
350 const int h = KEYBOARD_HEIGHT;
351 const int w = get_width() - 1;
352 Glib::RefPtr<Pango::Layout> layout =
353 Pango::Layout::create(get_pango_context());
354 char buf[30];
355 sprintf(buf, "<span size=\"8000\">%d</span>", key / 12 - 1);
356 layout->set_markup(buf);
357 Pango::Rectangle rectangle = layout->get_logical_extents();
358 double text_w = double(rectangle.get_width()) / Pango::SCALE;
359 double text_h = double(rectangle.get_height()) / Pango::SCALE;
360 double x = w * (key + 0.75) / 128.0;
361 Gdk::Cairo::set_source_rgba(cr, black);
362 cr->move_to(int(x - text_w / 2 + 1), int(h1 + h - text_h + 0.5));
363 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 16) || GTKMM_MAJOR_VERSION < 2
364 pango_cairo_show_layout(cr->cobj(), layout->gobj());
365 #else
366 layout->show_in_cairo_context(cr);
367 #endif
368 }
369
370 void RegionChooser::draw_key(const Cairo::RefPtr<Cairo::Context>& cr,
371 int key) {
372 const int h = KEYBOARD_HEIGHT;
373 const int w = get_width() - 1;
374 const int bh = int(h * 0.55);
375
376 Gdk::Cairo::set_source_rgba(cr, activeKeyColor);
377
378 int note = (key + 3) % 12;
379 int x = key_to_x(key, w) + 1;
380 int x2 = key_to_x(key + 1.5, w);
381 int x3 = key_to_x(key + 1, w);
382 int x4 = key_to_x(key - 0.5, w);
383 int w1 = x3 - x;
384 switch (note) {
385 case 0: case 5: case 10:
386 cr->rectangle(x, h1 + 1, w1, bh);
387 cr->fill();
388 cr->rectangle(x4 + 1, h1 + bh + 1, x2 - x4 - 1, h - bh - 2);
389 cr->fill();
390 break;
391 case 2: case 7:
392 cr->rectangle(x, h1 + 1, w1, bh);
393 cr->fill();
394 cr->rectangle(x4 + 1, h1 + bh + 1, x3 - x4 - 1, h - bh - 2);
395 cr->fill();
396 break;
397 case 3: case 8:
398 cr->rectangle(x, h1 + 1, w1, bh);
399 cr->fill();
400 cr->rectangle(x, h1 + bh + 1, x2 - x, h - bh - 2);
401 cr->fill();
402 break;
403 default:
404 cr->rectangle(x, h1 + 1, w1, bh - 1);
405 cr->fill();
406 break;
407 }
408 Gdk::Cairo::set_source_rgba(cr, black);
409 }
410
411 void RegionChooser::set_instrument(gig::Instrument* instrument)
412 {
413 this->instrument = instrument;
414 regions.update(instrument);
415 region = regions.first();
416 queue_draw();
417 region_selected();
418 dimensionManager.set_region(region);
419 }
420
421 bool RegionChooser::on_button_release_event(GdkEventButton* event)
422 {
423 const int k = x_to_key(event->x, get_width() - 1);
424
425 // handle-note off on virtual keyboard
426 if (event->type == GDK_BUTTON_RELEASE) {
427 int velocity = (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
428 int(float(event->y - REGION_BLOCK_HEIGHT) / float(KEYBOARD_HEIGHT) * 128.0f) + 1;
429 if (velocity <= 0) velocity = 1;
430 switch (m_VirtKeybModeChoice.get_value()) {
431 case VIRT_KEYBOARD_MODE_CHORD:
432 if (event->y >= REGION_BLOCK_HEIGHT)
433 keyboard_key_released_signal.emit(k, velocity);
434 break;
435 case VIRT_KEYBOARD_MODE_NORMAL:
436 default:
437 if (currentActiveKey >= 0 && currentActiveKey <= 127) {
438 keyboard_key_released_signal.emit(currentActiveKey, velocity);
439 currentActiveKey = -1;
440 }
441 break;
442 }
443 }
444
445 if (resize.active) {
446 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
447 get_window()->pointer_ungrab(event->time);
448 #else
449 Glib::wrap(event->device, true)->ungrab(event->time);
450 #endif
451 resize.active = false;
452
453 if (!is_in_resize_zone(event->x, event->y) && cursor_is_resize) {
454 get_window()->set_cursor();
455 cursor_is_resize = false;
456 }
457 } else if (move.active) {
458 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
459 get_window()->pointer_ungrab(event->time);
460 #else
461 Glib::wrap(event->device, true)->ungrab(event->time);
462 #endif
463 move.active = false;
464
465 if (is_in_resize_zone(event->x, event->y)) {
466 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
467 get_window()->set_cursor(Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW));
468 #else
469 get_window()->set_cursor(Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW));
470 #endif
471 cursor_is_resize = true;
472 }
473 }
474 return true;
475 }
476
477 void RegionChooser::update_after_resize()
478 {
479 if (resize.mode == resize.moving_high_limit) {
480 if (resize.region->KeyRange.high != resize.pos - 1) {
481 instrument_struct_to_be_changed_signal.emit(instrument);
482 resize.region->SetKeyRange(resize.region->KeyRange.low,
483 resize.pos - 1);
484 regions.update(instrument);
485 instrument_changed.emit();
486 instrument_struct_changed_signal.emit(instrument);
487 }
488 } else if (resize.mode == resize.moving_low_limit) {
489 if (resize.region->KeyRange.low != resize.pos) {
490 instrument_struct_to_be_changed_signal.emit(instrument);
491 resize.region->SetKeyRange(resize.pos,
492 resize.region->KeyRange.high);
493 regions.update(instrument);
494 instrument_changed.emit();
495 instrument_struct_changed_signal.emit(instrument);
496 }
497 }
498 }
499
500 void RegionChooser::update_after_move(int pos)
501 {
502 instrument_struct_to_be_changed_signal.emit(instrument);
503 region->SetKeyRange(pos, pos + region->KeyRange.high -
504 region->KeyRange.low);
505 regions.update(instrument);
506 instrument_changed.emit();
507 instrument_struct_changed_signal.emit(instrument);
508 }
509
510 bool RegionChooser::on_button_press_event(GdkEventButton* event)
511 {
512 if (!instrument) return true;
513
514 const int w = get_width() - 1;
515 const int k = x_to_key(event->x, w);
516
517 if (event->type == GDK_BUTTON_PRESS) {
518 if (event->y >= REGION_BLOCK_HEIGHT) {
519 int velocity = (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
520 int(float(event->y - REGION_BLOCK_HEIGHT) / float(KEYBOARD_HEIGHT) * 128.0f) + 1;
521 currentActiveKey = k;
522 keyboard_key_hit_signal.emit(k, velocity);
523 }
524 }
525
526 if (event->y >= REGION_BLOCK_HEIGHT) return true;
527 if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
528 gig::Region* r = get_region(k);
529 if (r) {
530 region = r;
531 queue_draw();
532 region_selected();
533 dimensionManager.set_region(region);
534 popup_menu_inside_region->popup(event->button, event->time);
535 } else {
536 new_region_pos = k;
537 popup_menu_outside_region->popup(event->button, event->time);
538 }
539 } else {
540 if (is_in_resize_zone(event->x, event->y)) {
541 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
542 get_window()->pointer_grab(false,
543 Gdk::BUTTON_RELEASE_MASK |
544 Gdk::POINTER_MOTION_MASK |
545 Gdk::POINTER_MOTION_HINT_MASK,
546 Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW),
547 event->time);
548 #else
549 Glib::wrap(event->device, true)->grab(get_window(),
550 Gdk::OWNERSHIP_NONE,
551 false,
552 Gdk::BUTTON_RELEASE_MASK |
553 Gdk::POINTER_MOTION_MASK |
554 Gdk::POINTER_MOTION_HINT_MASK,
555 Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW),
556 event->time);
557 #endif
558 resize.active = true;
559 } else {
560 gig::Region* r = get_region(k);
561 if (r) {
562 region = r;
563 queue_draw();
564 region_selected();
565 dimensionManager.set_region(region);
566
567 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
568 get_window()->pointer_grab(false,
569 Gdk::BUTTON_RELEASE_MASK |
570 Gdk::POINTER_MOTION_MASK |
571 Gdk::POINTER_MOTION_HINT_MASK,
572 Gdk::Cursor(Gdk::FLEUR),
573 event->time);
574 #else
575 Glib::wrap(event->device, true)->grab(get_window(),
576 Gdk::OWNERSHIP_NONE,
577 false,
578 Gdk::BUTTON_RELEASE_MASK |
579 Gdk::POINTER_MOTION_MASK |
580 Gdk::POINTER_MOTION_HINT_MASK,
581 Gdk::Cursor::create(Gdk::FLEUR),
582 event->time);
583 #endif
584 move.active = true;
585 move.offset = event->x - key_to_x(region->KeyRange.low, w);
586 }
587 }
588 }
589 return true;
590 }
591
592 gig::Region* RegionChooser::get_region(int key)
593 {
594 for (gig::Region* r = regions.first() ; r ; r = regions.next()) {
595 if (key < r->KeyRange.low) return 0;
596 if (key <= r->KeyRange.high) return r;
597 }
598 return 0;
599 }
600
601 void RegionChooser::motion_resize_region(int x, int y)
602 {
603 const int w = get_width() - 1;
604
605 int k = int(double(x) / w * 128.0 + 0.5);
606
607 if (k < resize.min) k = resize.min;
608 else if (k > resize.max) k = resize.max;
609
610 if (k != resize.pos) {
611 if (resize.mode == resize.undecided) {
612 if (k < resize.pos) {
613 // edit high limit of prev_region
614 resize.max = resize.region->KeyRange.low;
615 resize.region = resize.prev_region;
616 resize.mode = resize.moving_high_limit;
617 } else {
618 // edit low limit of region
619 resize.min = resize.prev_region->KeyRange.high + 1;
620 resize.mode = resize.moving_low_limit;
621 }
622 }
623 resize.pos = k;
624
625 int x1, x2;
626 if (resize.mode == resize.moving_high_limit) {
627 if (resize.region->KeyRange.high < resize.pos - 1) {
628 x1 = resize.region->KeyRange.high;
629 x2 = resize.pos - 1;
630 } else {
631 x1 = resize.pos - 1;
632 x2 = resize.region->KeyRange.high;
633 }
634 } else {
635 if (resize.region->KeyRange.low < resize.pos) {
636 x1 = resize.region->KeyRange.low;
637 x2 = resize.pos;
638 } else {
639 x1 = resize.pos;
640 x2 = resize.region->KeyRange.low;
641 }
642 }
643 x1 = key_to_x(x1, w);
644 x2 = key_to_x(x2 + 1, w) + 1;
645 Gdk::Rectangle rect(x1, 0, x2 - x1, h1);
646
647 update_after_resize();
648
649 get_window()->invalidate_rect(rect, false);
650 }
651 }
652
653 void RegionChooser::motion_move_region(int x, int y)
654 {
655 const int w = get_width() - 1;
656
657 int l = int(double(x - move.offset) / w * 128.0 + 0.5);
658
659 if (l == region->KeyRange.low) return;
660 int new_l;
661 int regionsize = region->KeyRange.high - region->KeyRange.low;
662 int a = 0;
663 if (l > region->KeyRange.low) {
664 for (gig::Region* r = regions.first() ; ; r = regions.next()) {
665 if (r != region) {
666 int b = r ? r->KeyRange.low : 128;
667
668 // gap: from a to b (not inclusive b)
669
670 if (region->KeyRange.high >= b) {
671 // not found the current gap yet, just continue
672 } else {
673
674 if (a > l) {
675 // this gap is too far to the right, break
676 break;
677 }
678
679 int newhigh = std::min(l + regionsize, b - 1);
680 int newlo = newhigh - regionsize;
681
682 if (newlo >= a) {
683 // yes it fits - it's a candidate
684 new_l = newlo;
685 }
686 }
687 if (!r) break;
688 a = r->KeyRange.high + 1;
689 }
690 }
691 } else {
692 for (gig::Region* r = regions.first() ; ; r = regions.next()) {
693 if (r != region) {
694 int b = r ? r->KeyRange.low : 128;
695
696 // gap from a to b (not inclusive b)
697
698 if (l + regionsize >= b) {
699 // not found the current gap yet, just continue
700 } else {
701
702 if (a > region->KeyRange.low) {
703 // this gap is too far to the right, break
704 break;
705 }
706
707 int newlo = std::max(l, a);
708 int newhigh = newlo + regionsize;
709
710 if (newhigh < b) {
711 // yes it fits - break as the first one is the best
712 new_l = newlo;
713 break;
714 }
715 }
716 if (!r) break;
717 a = r->KeyRange.high + 1;
718 }
719 }
720 }
721 if (new_l == region->KeyRange.low) return;
722
723 int x1 = key_to_x(std::min(int(region->KeyRange.low), new_l), w);
724 int x2 = key_to_x(std::max(int(region->KeyRange.high),
725 new_l + regionsize) + 1, w) + 1;
726
727 Gdk::Rectangle rect(x1, 0, x2 - x1, h1);
728 update_after_move(new_l);
729
730 get_window()->invalidate_rect(rect, false);
731 }
732
733
734 bool RegionChooser::on_motion_notify_event(GdkEventMotion* event)
735 {
736 Glib::RefPtr<Gdk::Window> window = get_window();
737 int x, y;
738 Gdk::ModifierType state = Gdk::ModifierType(0);
739 window->get_pointer(x, y, state);
740
741 // handle virtual MIDI keyboard
742 if (m_VirtKeybModeChoice.get_value() != VIRT_KEYBOARD_MODE_CHORD &&
743 currentActiveKey > 0 &&
744 event->y >= REGION_BLOCK_HEIGHT &&
745 event->y < REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT)
746 {
747 const int k = x_to_key(event->x, get_width() - 1);
748 if (k != currentActiveKey) {
749 int velocity =
750 (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
751 int(float(event->y - REGION_BLOCK_HEIGHT) /
752 float(KEYBOARD_HEIGHT) * 128.0f) + 1;
753 if (velocity <= 0) velocity = 1;
754 keyboard_key_released_signal.emit(currentActiveKey, velocity);
755 currentActiveKey = k;
756 keyboard_key_hit_signal.emit(k, velocity);
757 }
758 }
759
760 if (resize.active) {
761 motion_resize_region(x, y);
762 } else if (move.active) {
763 motion_move_region(x, y);
764 } else {
765 if (is_in_resize_zone(x, y)) {
766 if (!cursor_is_resize) {
767 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
768 window->set_cursor(Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW));
769 #else
770 window->set_cursor(Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW));
771 #endif
772 cursor_is_resize = true;
773 }
774 } else if (cursor_is_resize) {
775 window->set_cursor();
776 cursor_is_resize = false;
777 }
778 }
779
780 return true;
781 }
782
783 bool RegionChooser::is_in_resize_zone(double x, double y) {
784 const int w = get_width() - 1;
785
786 if (instrument && y >= 0 && y <= h1) {
787 gig::Region* prev_region = 0;
788 gig::Region* next_region;
789 for (gig::Region* r = regions.first(); r ; r = next_region) {
790 next_region = regions.next();
791
792 int lo = key_to_x(r->KeyRange.low, w);
793 if (x <= lo - 2) break;
794 if (x < lo + 2) {
795 resize.region = r;
796 resize.pos = r->KeyRange.low;
797 resize.max = r->KeyRange.high;
798
799 if (prev_region && prev_region->KeyRange.high + 1 == r->KeyRange.low) {
800 // we don't know yet if it's the high limit of
801 // prev_region or the low limit of r that's going
802 // to be edited
803 resize.mode = resize.undecided;
804 resize.min = prev_region->KeyRange.low + 1;
805 resize.prev_region = prev_region;
806 return resize.min != resize.max;
807 }
808
809 // edit low limit
810 resize.mode = resize.moving_low_limit;
811 resize.min = prev_region ? prev_region->KeyRange.high + 1 : 0;
812 return resize.min != resize.max;
813 }
814 if (!next_region || r->KeyRange.high + 1 != next_region->KeyRange.low) {
815 int hi = key_to_x(r->KeyRange.high + 1, w);
816 if (x <= hi - 2) break;
817 if (x < hi + 2) {
818 // edit high limit
819 resize.region = r;
820 resize.pos = r->KeyRange.high + 1;
821 resize.mode = resize.moving_high_limit;
822 resize.min = r->KeyRange.low + 1;
823 resize.max = next_region ? next_region->KeyRange.low : 128;
824 return resize.min != resize.max;
825 }
826 }
827 prev_region = r;
828 }
829 }
830 return false;
831 }
832
833 sigc::signal<void>& RegionChooser::signal_region_selected()
834 {
835 return region_selected;
836 }
837
838 sigc::signal<void>& RegionChooser::signal_instrument_changed()
839 {
840 return instrument_changed;
841 }
842
843 void RegionChooser::show_region_properties()
844 {
845 if (!region) return;
846 Gtk::Dialog dialog(_("Region Properties"), true /*modal*/);
847 // add "Keygroup" checkbox
848 Gtk::CheckButton checkBoxKeygroup(_("Member of a Keygroup (Exclusive Group)"));
849 checkBoxKeygroup.set_active(region->KeyGroup);
850 dialog.get_vbox()->pack_start(checkBoxKeygroup);
851 checkBoxKeygroup.show();
852 // add "Keygroup" spinbox
853 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
854 Gtk::Adjustment adjustment(1, 1, 999);
855 Gtk::SpinButton spinBox(adjustment);
856 #else
857 Gtk::SpinButton spinBox(Gtk::Adjustment::create(1, 1, 999));
858 #endif
859 if (region->KeyGroup) spinBox.set_value(region->KeyGroup);
860 dialog.get_vbox()->pack_start(spinBox);
861 spinBox.show();
862 // add OK and CANCEL buttons to the dialog
863 dialog.add_button(Gtk::Stock::OK, 0);
864 dialog.add_button(Gtk::Stock::CANCEL, 1);
865 dialog.show_all_children();
866 if (!dialog.run()) { // OK selected ...
867 region->KeyGroup =
868 (checkBoxKeygroup.get_active()) ? spinBox.get_value_as_int() : 0;
869 }
870 }
871
872 void RegionChooser::add_region()
873 {
874 instrument_struct_to_be_changed_signal.emit(instrument);
875
876 region = instrument->AddRegion();
877 region->SetKeyRange(new_region_pos, new_region_pos);
878
879 instrument_struct_changed_signal.emit(instrument);
880 regions.update(instrument);
881
882 queue_draw();
883 region_selected();
884 dimensionManager.set_region(region);
885 instrument_changed();
886 }
887
888 void RegionChooser::delete_region()
889 {
890 instrument_struct_to_be_changed_signal.emit(instrument);
891 instrument->DeleteRegion(region);
892 instrument_struct_changed_signal.emit(instrument);
893 regions.update(instrument);
894
895 region = 0;
896 queue_draw();
897 region_selected();
898 dimensionManager.set_region(region);
899 instrument_changed();
900 }
901
902 void RegionChooser::manage_dimensions()
903 {
904 gig::Region* region = get_region();
905 if (!region) return;
906 dimensionManager.show(region);
907 }
908
909 void RegionChooser::on_dimension_manager_changed() {
910 region_selected();
911 instrument_changed();
912 }
913
914 sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_to_be_changed() {
915 return instrument_struct_to_be_changed_signal;
916 }
917
918 sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_changed() {
919 return instrument_struct_changed_signal;
920 }
921
922 sigc::signal<void, gig::Region*>& RegionChooser::signal_region_to_be_changed() {
923 return region_to_be_changed_signal;
924 }
925
926 sigc::signal<void, gig::Region*>& RegionChooser::signal_region_changed_signal() {
927 return region_changed_signal;
928 }
929
930 sigc::signal<void, int/*key*/, int/*velocity*/>& RegionChooser::signal_keyboard_key_hit() {
931 return keyboard_key_hit_signal;
932 }
933
934 sigc::signal<void, int/*key*/, int/*velocity*/>& RegionChooser::signal_keyboard_key_released() {
935 return keyboard_key_released_signal;
936 }

  ViewVC Help
Powered by ViewVC