/[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 2151 - (show annotations) (download)
Sun Nov 21 12:38:41 2010 UTC (13 years, 3 months ago) by persson
File size: 33886 byte(s)
* use Cairo instead of deprecated gdk drawing primitives
* avoid deprecated gtk methods when using newer gtk versions
* raised minimum supported gtkmm version to 2.8

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

  ViewVC Help
Powered by ViewVC