/[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 2169 - (show annotations) (download)
Sun Mar 6 07:51:04 2011 UTC (13 years, 1 month ago) by persson
File size: 35276 byte(s)
* ported to gtkmm 3, keeping compatibility with gtkmm 2

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"), NULL };
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
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 }
158
159 RegionChooser::~RegionChooser()
160 {
161 }
162
163 template<class T> inline std::string ToString(T o) {
164 std::stringstream ss;
165 ss << o;
166 return ss.str();
167 }
168
169 void RegionChooser::on_note_on_event(int key, int velocity) {
170 draw_key(key, activeKeyColor);
171 m_VirtKeybVelocityLabel.set_text(ToString(velocity));
172 }
173
174 void RegionChooser::on_note_off_event(int key, int velocity) {
175 if (is_black_key(key)) {
176 draw_key(key, black);
177 } else {
178 draw_key(key, key >= 21 && key <= 108 ? white : grey1);
179 }
180 m_VirtKeybOffVelocityLabel.set_text(ToString(velocity));
181 }
182
183 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
184 bool RegionChooser::on_expose_event(GdkEventExpose* e)
185 {
186 return on_draw(get_window()->create_cairo_context());
187 }
188 #endif
189
190 bool RegionChooser::on_draw(const Cairo::RefPtr<Cairo::Context>& cr)
191 {
192 const int h = KEYBOARD_HEIGHT;
193 const int w = get_width() - 1;
194 const int bh = int(h * 0.55);
195
196 cr->save();
197 cr->set_line_width(1);
198
199 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
200 const Gdk::Color bg = get_style()->get_bg(Gtk::STATE_NORMAL);
201 #else
202 const Gdk::RGBA bg = get_style_context()->get_background_color();
203 #endif
204 Gdk::Cairo::set_source_rgba(cr, bg);
205 cr->paint();
206
207 Gdk::Cairo::set_source_rgba(cr, black);
208 cr->rectangle(0.5, h1 + 0.5, w, h - 1);
209 cr->stroke();
210
211 int x1 = int(w * 20.5 / 128.0 + 0.5);
212 int x2 = int(w * 109.5 / 128.0 + 0.5);
213
214 Gdk::Cairo::set_source_rgba(cr, grey1);
215 cr->rectangle(1, h1 + 1, x1 - 1, h - 2);
216 cr->fill();
217
218 Gdk::Cairo::set_source_rgba(cr, white);
219 cr->rectangle(x1 + 1, h1 + 1, x2 - x1 - 1, h - 2);
220 cr->fill();
221
222 Gdk::Cairo::set_source_rgba(cr, grey1);
223 cr->rectangle(x2 + 1, h1 + 1, w - x2 - 1, h - 2);
224 cr->fill();
225
226 Gdk::Cairo::set_source_rgba(cr, black);
227 for (int i = 0 ; i < 128 ; i++) {
228 int note = (i + 3) % 12;
229 int x = int(w * i / 128.0 + 0.5);
230
231 if (note == 1 || note == 4 || note == 6 ||
232 note == 9 || note == 11) {
233 int x2 = int(w * (i + 0.5) / 128.0 + 0.5);
234 cr->move_to(x2 + 0.5, h1 + bh + 0.5);
235 cr->line_to(x2 + 0.5, h1 + h - 1);
236 cr->stroke();
237
238 int x3 = int(w * (i + 1) / 128.0 + 0.5);
239 cr->rectangle(x, h1 + 1, x3 - x + 1, bh);
240 cr->fill();
241 } else if (note == 3 || note == 8) {
242 cr->move_to(x + 0.5, h1 + 1);
243 cr->line_to(x + 0.5, h1 + h - 1);
244 cr->stroke();
245
246 if (note == 3) draw_digit(i);
247 }
248 }
249
250 if (instrument) {
251 int i = 0;
252 gig::Region* next_region;
253 int x3 = -1;
254 for (gig::Region* r = regions.first() ; r ; r = next_region) {
255
256 if (x3 < 0) x3 = int(w * (r->KeyRange.low) / 128.0 + 0.5);
257 next_region = regions.next();
258 if (!next_region ||
259 r->KeyRange.high + 1 != next_region->KeyRange.low) {
260 int x2 = int(w * (r->KeyRange.high + 1) / 128.0 + 0.5);
261 cr->move_to(x3, 0.5);
262 cr->line_to(x2 + 0.5, 0.5);
263 cr->line_to(x2 + 0.5, h1 - 0.5);
264 cr->line_to(x3, h1 - 0.5);
265 cr->stroke();
266
267 Gdk::Cairo::set_source_rgba(cr, white);
268 cr->rectangle(x3 + 1, 1, x2 - x3 - 1, h1 - 2);
269 cr->fill();
270 Gdk::Cairo::set_source_rgba(cr, black);
271
272 x3 = -1;
273 }
274 i++;
275 }
276
277 for (gig::Region* r = regions.first() ; r ; r = regions.next()) {
278 int x = int(w * (r->KeyRange.low) / 128.0 + 0.5);
279 cr->move_to(x + 0.5, 1);
280 cr->line_to(x + 0.5, h1 - 1);
281 cr->stroke();
282 }
283
284 if (region) {
285 int x1 = int(w * (region->KeyRange.low) / 128.0 + 0.5);
286 int x2 = int(w * (region->KeyRange.high + 1) / 128.0 + 0.5);
287 Gdk::Cairo::set_source_rgba(cr, red);
288 cr->rectangle(x1 + 1, 1, x2 - x1 - 1, h1 - 2);
289 cr->fill();
290 }
291 }
292
293 cr->restore();
294
295 return true;
296 }
297
298
299 bool RegionChooser::is_black_key(int key) {
300 const int note = (key + 3) % 12;
301 return note == 1 || note == 4 || note == 6 || note == 9 || note == 11;
302 }
303
304 void RegionChooser::draw_digit(int key) {
305 const int h = KEYBOARD_HEIGHT;
306 const int w = get_width() - 1;
307 Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create(get_pango_context());
308 char buf[30];
309 sprintf(buf, "<span size=\"8000\">%d</span>", key / 12 - 1);
310 layout->set_markup(buf);
311 Pango::Rectangle rectangle = layout->get_logical_extents();
312 double text_w = double(rectangle.get_width()) / Pango::SCALE;
313 double text_h = double(rectangle.get_height()) / Pango::SCALE;
314 double x = w * (key + 0.75) / 128.0;
315 Cairo::RefPtr<Cairo::Context> cr = get_window()->create_cairo_context();
316 Gdk::Cairo::set_source_rgba(cr, black);
317 cr->move_to(int(x - text_w / 2 + 1), int(h1 + h - text_h + 0.5));
318 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 16) || GTKMM_MAJOR_VERSION < 2
319 pango_cairo_show_layout(cr->cobj(), layout->gobj());
320 #else
321 layout->show_in_cairo_context(cr);
322 #endif
323 }
324
325 void RegionChooser::draw_key(int key, const Gdk::RGBA& color)
326 {
327 const int h = KEYBOARD_HEIGHT;
328 const int w = get_width() - 1;
329 const int bh = int(h * 0.55);
330
331 Cairo::RefPtr<Cairo::Context> cr = get_window()->create_cairo_context();
332 Gdk::Cairo::set_source_rgba(cr, color);
333
334 int note = (key + 3) % 12;
335 int x = int(w * key / 128.0 + 0.5) + 1;
336 int x2 = int(w * (key + 1.5) / 128.0 + 0.5);
337 int x3 = int(w * (key + 1) / 128.0 + 0.5);
338 int x4 = int(w * (key - 0.5) / 128.0 + 0.5);
339 int w1 = x3 - x;
340 switch (note) {
341 case 0: case 5: case 10:
342 cr->rectangle(x, h1 + 1, w1, bh);
343 cr->fill();
344 cr->rectangle(x4 + 1, h1 + bh + 1, x2 - x4 - 1, h - bh - 2);
345 cr->fill();
346 break;
347 case 2: case 7:
348 cr->rectangle(x, h1 + 1, w1, bh);
349 cr->fill();
350 cr->rectangle(x4 + 1, h1 + bh + 1, x3 - x4 - 1, h - bh - 2);
351 cr->fill();
352 break;
353 case 3: case 8:
354 cr->rectangle(x, h1 + 1, w1, bh);
355 cr->fill();
356 cr->rectangle(x, h1 + bh + 1, x2 - x, h - bh - 2);
357 cr->fill();
358 if (note == 3) draw_digit(key);
359 break;
360 default:
361 cr->rectangle(x, h1 + 1, w1, bh - 1);
362 cr->fill();
363 break;
364 }
365 }
366
367 void RegionChooser::set_instrument(gig::Instrument* instrument)
368 {
369 this->instrument = instrument;
370 regions.update(instrument);
371 region = regions.first();
372 queue_draw();
373 region_selected();
374 dimensionManager.set_region(region);
375 }
376
377 bool RegionChooser::on_button_release_event(GdkEventButton* event)
378 {
379 const int k = int(event->x / (get_width() - 1) * 128.0);
380
381 // handle-note off on virtual keyboard
382 if (event->type == GDK_BUTTON_RELEASE) {
383 int velocity = (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
384 int(float(event->y - REGION_BLOCK_HEIGHT) / float(KEYBOARD_HEIGHT) * 128.0f) + 1;
385 if (velocity <= 0) velocity = 1;
386 switch (m_VirtKeybModeChoice.get_value()) {
387 case VIRT_KEYBOARD_MODE_CHORD:
388 if (event->y >= REGION_BLOCK_HEIGHT)
389 keyboard_key_released_signal.emit(k, velocity);
390 break;
391 case VIRT_KEYBOARD_MODE_NORMAL:
392 default:
393 if (currentActiveKey >= 0 && currentActiveKey <= 127) {
394 keyboard_key_released_signal.emit(currentActiveKey, velocity);
395 currentActiveKey = -1;
396 }
397 break;
398 }
399 }
400
401 if (resize.active) {
402 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
403 get_window()->pointer_ungrab(event->time);
404 #else
405 Glib::wrap(event->device, true)->ungrab(event->time);
406 #endif
407 resize.active = false;
408
409 if (resize.mode == resize.moving_high_limit) {
410 if (resize.region->KeyRange.high != resize.pos - 1) {
411 instrument_struct_to_be_changed_signal.emit(instrument);
412 resize.region->SetKeyRange(
413 resize.region->KeyRange.low, // low
414 resize.pos - 1 // high
415 );
416 regions.update(instrument);
417 instrument_changed.emit();
418 instrument_struct_changed_signal.emit(instrument);
419 }
420 } else if (resize.mode == resize.moving_low_limit) {
421 if (resize.region->KeyRange.low != resize.pos) {
422 instrument_struct_to_be_changed_signal.emit(instrument);
423 resize.region->SetKeyRange(
424 resize.pos, // low
425 resize.region->KeyRange.high // high
426 );
427 regions.update(instrument);
428 instrument_changed.emit();
429 instrument_struct_changed_signal.emit(instrument);
430 }
431 }
432
433 if (!is_in_resize_zone(event->x, event->y) && cursor_is_resize) {
434 get_window()->set_cursor();
435 cursor_is_resize = false;
436 }
437 } else if (move.active) {
438 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
439 get_window()->pointer_ungrab(event->time);
440 #else
441 Glib::wrap(event->device, true)->ungrab(event->time);
442 #endif
443 move.active = false;
444
445 if (move.pos) {
446 instrument_struct_to_be_changed_signal.emit(instrument);
447 region->SetKeyRange(
448 region->KeyRange.low + move.pos,
449 region->KeyRange.high + move.pos
450 );
451 regions.update(instrument);
452 instrument_changed.emit();
453 instrument_struct_changed_signal.emit(instrument);
454 }
455
456 if (is_in_resize_zone(event->x, event->y)) {
457 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
458 get_window()->set_cursor(Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW));
459 #else
460 get_window()->set_cursor(Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW));
461 #endif
462 cursor_is_resize = true;
463 }
464 }
465 return true;
466 }
467
468 bool RegionChooser::on_button_press_event(GdkEventButton* event)
469 {
470 if (!instrument) return true;
471
472 const int k = int(event->x / (get_width() - 1) * 128.0);
473
474 if (event->type == GDK_BUTTON_PRESS) {
475 if (event->y >= REGION_BLOCK_HEIGHT) {
476 int velocity = (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
477 int(float(event->y - REGION_BLOCK_HEIGHT) / float(KEYBOARD_HEIGHT) * 128.0f) + 1;
478 currentActiveKey = k;
479 keyboard_key_hit_signal.emit(k, velocity);
480 }
481 }
482
483 if (event->y >= REGION_BLOCK_HEIGHT) return true;
484 if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
485 gig::Region* r = get_region(k);
486 if (r) {
487 region = r;
488 queue_draw();
489 region_selected();
490 dimensionManager.set_region(region);
491 popup_menu_inside_region->popup(event->button, event->time);
492 } else {
493 new_region_pos = k;
494 popup_menu_outside_region->popup(event->button, event->time);
495 }
496 } else {
497 if (is_in_resize_zone(event->x, event->y)) {
498 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
499 get_window()->pointer_grab(false,
500 Gdk::BUTTON_RELEASE_MASK |
501 Gdk::POINTER_MOTION_MASK |
502 Gdk::POINTER_MOTION_HINT_MASK,
503 Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW),
504 event->time);
505 #else
506 Glib::wrap(event->device, true)->grab(get_window(),
507 Gdk::OWNERSHIP_NONE,
508 false,
509 Gdk::BUTTON_RELEASE_MASK |
510 Gdk::POINTER_MOTION_MASK |
511 Gdk::POINTER_MOTION_HINT_MASK,
512 Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW),
513 event->time);
514 #endif
515 resize.active = true;
516 } else {
517 gig::Region* r = get_region(k);
518 if (r) {
519 region = r;
520 queue_draw();
521 region_selected();
522 dimensionManager.set_region(region);
523
524 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
525 get_window()->pointer_grab(false,
526 Gdk::BUTTON_RELEASE_MASK |
527 Gdk::POINTER_MOTION_MASK |
528 Gdk::POINTER_MOTION_HINT_MASK,
529 Gdk::Cursor(Gdk::FLEUR),
530 event->time);
531 #else
532 Glib::wrap(event->device, true)->grab(get_window(),
533 Gdk::OWNERSHIP_NONE,
534 false,
535 Gdk::BUTTON_RELEASE_MASK |
536 Gdk::POINTER_MOTION_MASK |
537 Gdk::POINTER_MOTION_HINT_MASK,
538 Gdk::Cursor::create(Gdk::FLEUR),
539 event->time);
540 #endif
541 move.active = true;
542 move.from_x = event->x;
543 move.pos = 0;
544 }
545 }
546 }
547 return true;
548 }
549
550 gig::Region* RegionChooser::get_region(int key)
551 {
552 gig::Region* prev_region = 0;
553 gig::Region* next_region;
554 for (gig::Region* r = regions.first() ; r ; r = next_region) {
555 next_region = regions.next();
556
557 if (key < r->KeyRange.low) return 0;
558 if (key <= r->KeyRange.high) {
559 move.touch_left = prev_region && prev_region->KeyRange.high + 1 == r->KeyRange.low;
560 move.touch_right = next_region && r->KeyRange.high + 1 == next_region->KeyRange.low;
561 return r;
562 }
563 prev_region = r;
564 }
565 return 0;
566 }
567
568 void RegionChooser::motion_resize_region(int x, int y)
569 {
570 const int w = get_width() - 1;
571
572 int k = int(double(x) / w * 128.0 + 0.5);
573
574 if (k < resize.min) k = resize.min;
575 else if (k > resize.max) k = resize.max;
576
577 if (k != resize.pos) {
578 Cairo::RefPtr<Cairo::Context> cr = get_window()->create_cairo_context();
579 cr->set_line_width(1);
580
581 if (resize.mode == resize.undecided) {
582 if (k < resize.pos) {
583 // edit high limit of prev_region
584 resize.max = resize.region->KeyRange.low;
585 resize.region = resize.prev_region;
586 resize.mode = resize.moving_high_limit;
587 } else {
588 // edit low limit of region
589 resize.min = resize.prev_region->KeyRange.high + 1;
590 resize.mode = resize.moving_low_limit;
591 }
592 }
593 const Gdk::RGBA white = region == resize.region ? red : this->white;
594 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
595 const Gdk::Color bg = get_style()->get_bg(Gtk::STATE_NORMAL);
596 #else
597 const Gdk::RGBA bg = get_style_context()->get_background_color();
598 #endif
599
600 int prevx = int(w * resize.pos / 128.0 + 0.5);
601 x = int(w * k / 128.0 + 0.5);
602
603 if (resize.mode == resize.moving_high_limit) {
604 if (k > resize.pos) {
605 Gdk::Cairo::set_source_rgba(cr, white);
606 cr->rectangle(prevx, 1, x - prevx, h1 - 2);
607 cr->fill();
608
609 Gdk::Cairo::set_source_rgba(cr, black);
610 cr->move_to(prevx, 0.5);
611 cr->line_to(x + 1, 0.5);
612 cr->move_to(prevx, h1 - 0.5);
613 cr->line_to(x + 1, h1 - 0.5);
614 cr->stroke();
615 } else {
616 int xx = (resize.pos == resize.max &&
617 resize.max != 128) ? 1 : 0;
618 Gdk::Cairo::set_source_rgba(cr, bg);
619 cr->rectangle(x + 1, 0, prevx - x - xx, h1);
620 cr->fill();
621 }
622 } else {
623 if (k < resize.pos) {
624 Gdk::Cairo::set_source_rgba(cr, white);
625 cr->rectangle(x + 1, 1, prevx - x, h1 - 2);
626 cr->fill();
627
628 Gdk::Cairo::set_source_rgba(cr, black);
629 cr->move_to(x, 0.5);
630 cr->line_to(prevx, 0.5);
631 cr->move_to(x, h1 - 0.5);
632 cr->line_to(prevx, h1 - 0.5);
633 cr->stroke();
634 } else {
635 int xx = (resize.pos == resize.min &&
636 resize.min != 0) ? 1 : 0;
637 Gdk::Cairo::set_source_rgba(cr, bg);
638 cr->rectangle(prevx + xx, 0, x - prevx - xx, h1);
639 cr->fill();
640 }
641 }
642 Gdk::Cairo::set_source_rgba(cr, black);
643 cr->move_to(x + 0.5, 1);
644 cr->line_to(x + 0.5, h1 - 1);
645 cr->stroke();
646 resize.pos = k;
647 }
648 }
649
650 void RegionChooser::motion_move_region(int x, int y)
651 {
652 const int w = get_width() - 1;
653 Cairo::RefPtr<Cairo::Context> cr = get_window()->create_cairo_context();
654 cr->set_line_width(1);
655
656 int k = int(double(x - move.from_x) / w * 128.0 + 0.5);
657 if (k == move.pos) return;
658 int new_k;
659 bool new_touch_left;
660 bool new_touch_right;
661 int a = 0;
662 if (k > move.pos) {
663 for (gig::Region* r = regions.first() ; ; r = regions.next()) {
664 if (r != region) {
665 int b = r ? r->KeyRange.low : 128;
666
667 // gap: from a to b (not inclusive b)
668
669 if (region->KeyRange.high + move.pos >= b) {
670 // not found the current gap yet, just continue
671 } else {
672
673 if (a > region->KeyRange.low + k) {
674 // this gap is too far to the right, break
675 break;
676 }
677
678 int newhigh = std::min(region->KeyRange.high + k, b - 1);
679 int newlo = newhigh - (region->KeyRange.high - region->KeyRange.low);
680
681 if (newlo >= a) {
682 // yes it fits - it's a candidate
683 new_k = newlo - region->KeyRange.low;
684 new_touch_left = a > 0 && a == newlo;
685 new_touch_right = b < 128 && newhigh + 1 == b;
686 }
687 }
688 if (!r) break;
689 a = r->KeyRange.high + 1;
690 }
691 }
692 } else {
693 for (gig::Region* r = regions.first() ; ; r = regions.next()) {
694 if (r != region) {
695 int b = r ? r->KeyRange.low : 128;
696
697 // gap from a to b (not inclusive b)
698
699 if (region->KeyRange.high + k >= b) {
700 // not found the current gap yet, just continue
701 } else {
702
703 if (a > region->KeyRange.low + move.pos) {
704 // this gap is too far to the right, break
705 break;
706 }
707
708 int newlo = std::max(region->KeyRange.low + k, a);
709 int newhigh = newlo + (region->KeyRange.high - region->KeyRange.low);
710
711 if (newhigh < b) {
712 // yes it fits - break as the first one is the best
713 new_k = newlo - region->KeyRange.low;
714 new_touch_left = a > 0 && a == newlo;
715 new_touch_right = b < 128 && newhigh + 1 == b;
716 break;
717 }
718 }
719 if (!r) break;
720 a = r->KeyRange.high + 1;
721 }
722 }
723 }
724 k = new_k;
725 if (k == move.pos) return;
726
727 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
728 const Gdk::Color bg = get_style()->get_bg(Gtk::STATE_NORMAL);
729 #else
730 const Gdk::RGBA bg = get_style_context()->get_background_color();
731 #endif
732
733 int prevx = int(w * (move.pos + region->KeyRange.low) / 128.0 + 0.5);
734 x = int(w * (k + region->KeyRange.low) / 128.0 + 0.5);
735 int prevx2 = int(w * (move.pos + region->KeyRange.high + 1) / 128.0 + 0.5);
736 int x2 = int(w * (k + region->KeyRange.high + 1) / 128.0 + 0.5);
737
738 if (!new_touch_left) {
739 Gdk::Cairo::set_source_rgba(cr, black);
740 cr->move_to(x + 0.5, 1);
741 cr->line_to(x + 0.5, h1 - 1);
742 cr->stroke();
743 }
744 if (!new_touch_right) {
745 Gdk::Cairo::set_source_rgba(cr, black);
746 cr->move_to(x2 + 0.5, 1);
747 cr->line_to(x2 + 0.5, h1 - 1);
748 cr->stroke();
749 }
750
751 if (k > move.pos) {
752 Gdk::Cairo::set_source_rgba(cr, bg);
753 cr->rectangle(prevx + (move.touch_left ? 1 : 0), 0,
754 std::min(x, prevx2 + 1 - (move.touch_right ? 1 : 0)) -
755 (prevx + (move.touch_left ? 1 : 0)), h1);
756 cr->fill();
757
758 Gdk::Cairo::set_source_rgba(cr, black);
759 cr->move_to(std::max(x, prevx2 + 1), 0.5);
760 cr->line_to(x2 + 1, 0.5);
761 cr->move_to(std::max(x, prevx2 + 1), h1 - 0.5);
762 cr->line_to(x2 + 1, h1 - 0.5);
763 cr->stroke();
764
765 Gdk::Cairo::set_source_rgba(cr, red);
766 cr->rectangle(std::max(x + 1, prevx2), 1,
767 x2 - std::max(x + 1, prevx2), h1 - 2);
768 cr->fill();
769 } else {
770 Gdk::Cairo::set_source_rgba(cr, bg);
771 cr->rectangle(std::max(x2 + 1, prevx + (move.touch_left ? 1 : 0)), 0,
772 prevx2 + 1 - (move.touch_right ? 1 : 0) -
773 std::max(x2 + 1, prevx + (move.touch_left ? 1 : 0)), h1);
774 cr->fill();
775
776 Gdk::Cairo::set_source_rgba(cr, black);
777 cr->move_to(x, 0.5);
778 cr->line_to(std::min(x2, prevx - 1) + 1, 0.5);
779 cr->move_to(x, h1 - 0.5);
780 cr->line_to(std::min(x2, prevx - 1) + 1, h1 - 0.5);
781 cr->stroke();
782
783 Gdk::Cairo::set_source_rgba(cr, red);
784 cr->rectangle(x + 1, 1, std::min(x2 - 1, prevx) - x, h1 - 2);
785 cr->fill();
786 }
787
788 move.pos = k;
789 move.touch_left = new_touch_left;
790 move.touch_right = new_touch_right;
791 }
792
793
794 bool RegionChooser::on_motion_notify_event(GdkEventMotion* event)
795 {
796 Glib::RefPtr<Gdk::Window> window = get_window();
797 int x, y;
798 Gdk::ModifierType state = Gdk::ModifierType(0);
799 window->get_pointer(x, y, state);
800
801 // handle virtual MIDI keyboard
802 if (m_VirtKeybModeChoice.get_value() != VIRT_KEYBOARD_MODE_CHORD &&
803 currentActiveKey > 0 &&
804 event->y >= REGION_BLOCK_HEIGHT &&
805 event->y < REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT)
806 {
807 const int k = int(event->x / (get_width() - 1) * 128.0);
808 if (k != currentActiveKey) {
809 int velocity =
810 (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
811 int(float(event->y - REGION_BLOCK_HEIGHT) /
812 float(KEYBOARD_HEIGHT) * 128.0f) + 1;
813 if (velocity <= 0) velocity = 1;
814 keyboard_key_released_signal.emit(currentActiveKey, velocity);
815 currentActiveKey = k;
816 keyboard_key_hit_signal.emit(k, velocity);
817 }
818 }
819
820 if (resize.active) {
821 motion_resize_region(x, y);
822 } else if (move.active) {
823 motion_move_region(x, y);
824 } else {
825 if (is_in_resize_zone(x, y)) {
826 if (!cursor_is_resize) {
827 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
828 window->set_cursor(Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW));
829 #else
830 window->set_cursor(Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW));
831 #endif
832 cursor_is_resize = true;
833 }
834 } else if (cursor_is_resize) {
835 window->set_cursor();
836 cursor_is_resize = false;
837 }
838 }
839
840 return true;
841 }
842
843 bool RegionChooser::is_in_resize_zone(double x, double y) {
844 const int w = get_width() - 1;
845
846 if (instrument && y >= 0 && y <= h1) {
847 gig::Region* prev_region = 0;
848 gig::Region* next_region;
849 for (gig::Region* r = regions.first(); r ; r = next_region) {
850 next_region = regions.next();
851
852 int lo = int(w * (r->KeyRange.low) / 128.0 + 0.5);
853 if (x <= lo - 2) break;
854 if (x < lo + 2) {
855 resize.region = r;
856 resize.pos = r->KeyRange.low;
857 resize.max = r->KeyRange.high;
858
859 if (prev_region && prev_region->KeyRange.high + 1 == r->KeyRange.low) {
860 // we don't know yet if it's the high limit of
861 // prev_region or the low limit of r that's going
862 // to be edited
863 resize.mode = resize.undecided;
864 resize.min = prev_region->KeyRange.low + 1;
865 resize.prev_region = prev_region;
866 return resize.min != resize.max;
867 }
868
869 // edit low limit
870 resize.mode = resize.moving_low_limit;
871 resize.min = prev_region ? prev_region->KeyRange.high + 1 : 0;
872 return resize.min != resize.max;
873 }
874 if (!next_region || r->KeyRange.high + 1 != next_region->KeyRange.low) {
875 int hi = int(w * (r->KeyRange.high + 1) / 128.0 + 0.5);
876 if (x <= hi - 2) break;
877 if (x < hi + 2) {
878 // edit high limit
879 resize.region = r;
880 resize.pos = r->KeyRange.high + 1;
881 resize.mode = resize.moving_high_limit;
882 resize.min = r->KeyRange.low + 1;
883 resize.max = next_region ? next_region->KeyRange.low : 128;
884 return resize.min != resize.max;
885 }
886 }
887 prev_region = r;
888 }
889 }
890 return false;
891 }
892
893 sigc::signal<void>& RegionChooser::signal_region_selected()
894 {
895 return region_selected;
896 }
897
898 sigc::signal<void>& RegionChooser::signal_instrument_changed()
899 {
900 return instrument_changed;
901 }
902
903 void RegionChooser::show_region_properties()
904 {
905 if (!region) return;
906 Gtk::Dialog dialog(_("Region Properties"), true /*modal*/);
907 // add "Keygroup" checkbox
908 Gtk::CheckButton checkBoxKeygroup(_("Member of a Keygroup (Exclusive Group)"));
909 checkBoxKeygroup.set_active(region->KeyGroup);
910 dialog.get_vbox()->pack_start(checkBoxKeygroup);
911 checkBoxKeygroup.show();
912 // add "Keygroup" spinbox
913 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
914 Gtk::Adjustment adjustment(1, 1, 999);
915 Gtk::SpinButton spinBox(adjustment);
916 #else
917 Gtk::SpinButton spinBox(Gtk::Adjustment::create(1, 1, 999));
918 #endif
919 if (region->KeyGroup) spinBox.set_value(region->KeyGroup);
920 dialog.get_vbox()->pack_start(spinBox);
921 spinBox.show();
922 // add OK and CANCEL buttons to the dialog
923 dialog.add_button(Gtk::Stock::OK, 0);
924 dialog.add_button(Gtk::Stock::CANCEL, 1);
925 dialog.show_all_children();
926 if (!dialog.run()) { // OK selected ...
927 region->KeyGroup =
928 (checkBoxKeygroup.get_active()) ? spinBox.get_value_as_int() : 0;
929 }
930 }
931
932 void RegionChooser::add_region()
933 {
934 instrument_struct_to_be_changed_signal.emit(instrument);
935
936 region = instrument->AddRegion();
937 region->SetKeyRange(new_region_pos, new_region_pos);
938
939 instrument_struct_changed_signal.emit(instrument);
940 regions.update(instrument);
941
942 queue_draw();
943 region_selected();
944 dimensionManager.set_region(region);
945 instrument_changed();
946 }
947
948 void RegionChooser::delete_region()
949 {
950 instrument_struct_to_be_changed_signal.emit(instrument);
951 instrument->DeleteRegion(region);
952 instrument_struct_changed_signal.emit(instrument);
953 regions.update(instrument);
954
955 region = 0;
956 queue_draw();
957 region_selected();
958 dimensionManager.set_region(region);
959 instrument_changed();
960 }
961
962 void RegionChooser::manage_dimensions()
963 {
964 gig::Region* region = get_region();
965 if (!region) return;
966 dimensionManager.show(region);
967 }
968
969 void RegionChooser::on_dimension_manager_changed() {
970 region_selected();
971 instrument_changed();
972 }
973
974 sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_to_be_changed() {
975 return instrument_struct_to_be_changed_signal;
976 }
977
978 sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_changed() {
979 return instrument_struct_changed_signal;
980 }
981
982 sigc::signal<void, gig::Region*>& RegionChooser::signal_region_to_be_changed() {
983 return region_to_be_changed_signal;
984 }
985
986 sigc::signal<void, gig::Region*>& RegionChooser::signal_region_changed_signal() {
987 return region_changed_signal;
988 }
989
990 sigc::signal<void, int/*key*/, int/*velocity*/>& RegionChooser::signal_keyboard_key_hit() {
991 return keyboard_key_hit_signal;
992 }
993
994 sigc::signal<void, int/*key*/, int/*velocity*/>& RegionChooser::signal_keyboard_key_released() {
995 return keyboard_key_released_signal;
996 }

  ViewVC Help
Powered by ViewVC