/[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 1898 - (show annotations) (download)
Sun May 10 09:35:56 2009 UTC (14 years, 10 months ago) by persson
File size: 31345 byte(s)
* Windows: look for translations using base directory of libgigedit
  dll
* virtual keyboard fixes: restore to grey when outside keyboard. Don't
  trigger multiple notes for each key when moving mouse.

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

  ViewVC Help
Powered by ViewVC