/[svn]/gigedit/trunk/src/gigedit/regionchooser.cpp
ViewVC logotype

Annotation of /gigedit/trunk/src/gigedit/regionchooser.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2663 - (hide annotations) (download)
Wed Jul 2 23:53:21 2014 UTC (9 years, 9 months ago) by schoenebeck
File size: 33448 byte(s)
* Fix: double click on the virtual MIDI keyboard opened the
  dimension manager dialog.

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

  ViewVC Help
Powered by ViewVC