/[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 2627 - (hide annotations) (download)
Thu Jun 12 16:12:55 2014 UTC (9 years, 9 months ago) by schoenebeck
File size: 33187 byte(s)
* Increased region selector height by 50%.
* Increased dimension region selector height by 20%.
* Some minor fixes.
* Updated "about" dialog: not so immature anymore ;-).
* Configure script: require at least LinuxSampler v1.0.0.

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     if (event->y >= REGION_BLOCK_HEIGHT) return true;
542 schoenebeck 1225 if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
543     gig::Region* r = get_region(k);
544     if (r) {
545     region = r;
546     queue_draw();
547 persson 1261 region_selected();
548 persson 1677 dimensionManager.set_region(region);
549 schoenebeck 1225 popup_menu_inside_region->popup(event->button, event->time);
550     } else {
551     new_region_pos = k;
552     popup_menu_outside_region->popup(event->button, event->time);
553     }
554     } else {
555     if (is_in_resize_zone(event->x, event->y)) {
556 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
557 schoenebeck 1225 get_window()->pointer_grab(false,
558     Gdk::BUTTON_RELEASE_MASK |
559     Gdk::POINTER_MOTION_MASK |
560     Gdk::POINTER_MOTION_HINT_MASK,
561 persson 2169 Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW),
562     event->time);
563     #else
564     Glib::wrap(event->device, true)->grab(get_window(),
565     Gdk::OWNERSHIP_NONE,
566     false,
567     Gdk::BUTTON_RELEASE_MASK |
568     Gdk::POINTER_MOTION_MASK |
569     Gdk::POINTER_MOTION_HINT_MASK,
570     Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW),
571     event->time);
572     #endif
573 schoenebeck 1225 resize.active = true;
574     } else {
575     gig::Region* r = get_region(k);
576     if (r) {
577     region = r;
578     queue_draw();
579 persson 1261 region_selected();
580 persson 1677 dimensionManager.set_region(region);
581 persson 1262
582 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
583 persson 1262 get_window()->pointer_grab(false,
584     Gdk::BUTTON_RELEASE_MASK |
585     Gdk::POINTER_MOTION_MASK |
586     Gdk::POINTER_MOTION_HINT_MASK,
587 persson 2169 Gdk::Cursor(Gdk::FLEUR),
588     event->time);
589     #else
590     Glib::wrap(event->device, true)->grab(get_window(),
591     Gdk::OWNERSHIP_NONE,
592     false,
593     Gdk::BUTTON_RELEASE_MASK |
594     Gdk::POINTER_MOTION_MASK |
595     Gdk::POINTER_MOTION_HINT_MASK,
596     Gdk::Cursor::create(Gdk::FLEUR),
597     event->time);
598     #endif
599 persson 1262 move.active = true;
600 persson 2246 move.offset = event->x - key_to_x(region->KeyRange.low, w);
601 schoenebeck 1225 }
602     }
603     }
604     return true;
605     }
606    
607     gig::Region* RegionChooser::get_region(int key)
608     {
609 persson 2246 for (gig::Region* r = regions.first() ; r ; r = regions.next()) {
610 schoenebeck 1225 if (key < r->KeyRange.low) return 0;
611 persson 2246 if (key <= r->KeyRange.high) return r;
612 schoenebeck 1225 }
613     return 0;
614     }
615    
616 persson 1262 void RegionChooser::motion_resize_region(int x, int y)
617 schoenebeck 1225 {
618 persson 1623 const int w = get_width() - 1;
619 schoenebeck 1225
620 persson 1262 int k = int(double(x) / w * 128.0 + 0.5);
621 schoenebeck 1225
622 persson 1262 if (k < resize.min) k = resize.min;
623     else if (k > resize.max) k = resize.max;
624    
625     if (k != resize.pos) {
626     if (resize.mode == resize.undecided) {
627     if (k < resize.pos) {
628     // edit high limit of prev_region
629     resize.max = resize.region->KeyRange.low;
630     resize.region = resize.prev_region;
631     resize.mode = resize.moving_high_limit;
632     } else {
633     // edit low limit of region
634     resize.min = resize.prev_region->KeyRange.high + 1;
635     resize.mode = resize.moving_low_limit;
636 schoenebeck 1225 }
637 persson 1262 }
638 persson 2246 resize.pos = k;
639 persson 2151
640 persson 2246 int x1, x2;
641 persson 1262 if (resize.mode == resize.moving_high_limit) {
642 persson 2246 if (resize.region->KeyRange.high < resize.pos - 1) {
643     x1 = resize.region->KeyRange.high;
644     x2 = resize.pos - 1;
645 persson 1262 } else {
646 persson 2246 x1 = resize.pos - 1;
647     x2 = resize.region->KeyRange.high;
648 schoenebeck 1225 }
649 persson 1262 } else {
650 persson 2246 if (resize.region->KeyRange.low < resize.pos) {
651     x1 = resize.region->KeyRange.low;
652     x2 = resize.pos;
653 persson 1262 } else {
654 persson 2246 x1 = resize.pos;
655     x2 = resize.region->KeyRange.low;
656 persson 1262 }
657     }
658 persson 2246 x1 = key_to_x(x1, w);
659     x2 = key_to_x(x2 + 1, w) + 1;
660     Gdk::Rectangle rect(x1, 0, x2 - x1, h1);
661    
662     update_after_resize();
663    
664     get_window()->invalidate_rect(rect, false);
665 persson 1262 }
666     }
667 schoenebeck 1225
668 persson 1262 void RegionChooser::motion_move_region(int x, int y)
669     {
670 persson 1623 const int w = get_width() - 1;
671 persson 1262
672 persson 2246 int l = int(double(x - move.offset) / w * 128.0 + 0.5);
673    
674     if (l == region->KeyRange.low) return;
675     int new_l;
676     int regionsize = region->KeyRange.high - region->KeyRange.low;
677 persson 1262 int a = 0;
678 persson 2246 if (l > region->KeyRange.low) {
679 persson 1623 for (gig::Region* r = regions.first() ; ; r = regions.next()) {
680 persson 1262 if (r != region) {
681     int b = r ? r->KeyRange.low : 128;
682    
683     // gap: from a to b (not inclusive b)
684    
685 persson 2246 if (region->KeyRange.high >= b) {
686 persson 1262 // not found the current gap yet, just continue
687 schoenebeck 1225 } else {
688 persson 1262
689 persson 2246 if (a > l) {
690 persson 1262 // this gap is too far to the right, break
691     break;
692     }
693    
694 persson 2246 int newhigh = std::min(l + regionsize, b - 1);
695     int newlo = newhigh - regionsize;
696 persson 1262
697     if (newlo >= a) {
698     // yes it fits - it's a candidate
699 persson 2246 new_l = newlo;
700 persson 1262 }
701 schoenebeck 1225 }
702 persson 1262 if (!r) break;
703     a = r->KeyRange.high + 1;
704     }
705     }
706     } else {
707 persson 1623 for (gig::Region* r = regions.first() ; ; r = regions.next()) {
708 persson 1262 if (r != region) {
709     int b = r ? r->KeyRange.low : 128;
710    
711     // gap from a to b (not inclusive b)
712    
713 persson 2246 if (l + regionsize >= b) {
714 persson 1262 // not found the current gap yet, just continue
715 schoenebeck 1225 } else {
716 persson 1262
717 persson 2246 if (a > region->KeyRange.low) {
718 persson 1262 // this gap is too far to the right, break
719     break;
720     }
721    
722 persson 2246 int newlo = std::max(l, a);
723     int newhigh = newlo + regionsize;
724 persson 1262
725     if (newhigh < b) {
726     // yes it fits - break as the first one is the best
727 persson 2246 new_l = newlo;
728 persson 1262 break;
729     }
730 schoenebeck 1225 }
731 persson 1262 if (!r) break;
732     a = r->KeyRange.high + 1;
733 schoenebeck 1225 }
734     }
735 persson 1262 }
736 persson 2246 if (new_l == region->KeyRange.low) return;
737 persson 1262
738 persson 2246 int x1 = key_to_x(std::min(int(region->KeyRange.low), new_l), w);
739     int x2 = key_to_x(std::max(int(region->KeyRange.high),
740     new_l + regionsize) + 1, w) + 1;
741 persson 2169
742 persson 2246 Gdk::Rectangle rect(x1, 0, x2 - x1, h1);
743     update_after_move(new_l);
744 persson 1262
745 persson 2246 get_window()->invalidate_rect(rect, false);
746 persson 1262 }
747    
748    
749     bool RegionChooser::on_motion_notify_event(GdkEventMotion* event)
750     {
751     Glib::RefPtr<Gdk::Window> window = get_window();
752     int x, y;
753     Gdk::ModifierType state = Gdk::ModifierType(0);
754     window->get_pointer(x, y, state);
755    
756 schoenebeck 1661 // handle virtual MIDI keyboard
757     if (m_VirtKeybModeChoice.get_value() != VIRT_KEYBOARD_MODE_CHORD &&
758     currentActiveKey > 0 &&
759     event->y >= REGION_BLOCK_HEIGHT &&
760     event->y < REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT)
761     {
762 persson 2246 const int k = x_to_key(event->x, get_width() - 1);
763 persson 1898 if (k != currentActiveKey) {
764     int velocity =
765     (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
766     int(float(event->y - REGION_BLOCK_HEIGHT) /
767     float(KEYBOARD_HEIGHT) * 128.0f) + 1;
768     if (velocity <= 0) velocity = 1;
769     keyboard_key_released_signal.emit(currentActiveKey, velocity);
770     currentActiveKey = k;
771     keyboard_key_hit_signal.emit(k, velocity);
772     }
773 schoenebeck 1661 }
774    
775 persson 1262 if (resize.active) {
776     motion_resize_region(x, y);
777     } else if (move.active) {
778     motion_move_region(x, y);
779     } else {
780 schoenebeck 1225 if (is_in_resize_zone(x, y)) {
781     if (!cursor_is_resize) {
782 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
783 persson 1262 window->set_cursor(Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW));
784 persson 2169 #else
785     window->set_cursor(Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW));
786     #endif
787 schoenebeck 1225 cursor_is_resize = true;
788     }
789     } else if (cursor_is_resize) {
790     window->set_cursor();
791     cursor_is_resize = false;
792     }
793     }
794    
795     return true;
796     }
797    
798     bool RegionChooser::is_in_resize_zone(double x, double y) {
799 persson 1623 const int w = get_width() - 1;
800 schoenebeck 1225
801     if (instrument && y >= 0 && y <= h1) {
802     gig::Region* prev_region = 0;
803     gig::Region* next_region;
804 persson 1623 for (gig::Region* r = regions.first(); r ; r = next_region) {
805     next_region = regions.next();
806 schoenebeck 1225
807 persson 2246 int lo = key_to_x(r->KeyRange.low, w);
808 schoenebeck 1225 if (x <= lo - 2) break;
809     if (x < lo + 2) {
810     resize.region = r;
811     resize.pos = r->KeyRange.low;
812     resize.max = r->KeyRange.high;
813    
814     if (prev_region && prev_region->KeyRange.high + 1 == r->KeyRange.low) {
815     // we don't know yet if it's the high limit of
816     // prev_region or the low limit of r that's going
817     // to be edited
818     resize.mode = resize.undecided;
819     resize.min = prev_region->KeyRange.low + 1;
820     resize.prev_region = prev_region;
821 persson 1623 return resize.min != resize.max;
822 schoenebeck 1225 }
823    
824     // edit low limit
825     resize.mode = resize.moving_low_limit;
826     resize.min = prev_region ? prev_region->KeyRange.high + 1 : 0;
827 persson 1623 return resize.min != resize.max;
828 schoenebeck 1225 }
829     if (!next_region || r->KeyRange.high + 1 != next_region->KeyRange.low) {
830 persson 2246 int hi = key_to_x(r->KeyRange.high + 1, w);
831 schoenebeck 1225 if (x <= hi - 2) break;
832     if (x < hi + 2) {
833     // edit high limit
834     resize.region = r;
835     resize.pos = r->KeyRange.high + 1;
836     resize.mode = resize.moving_high_limit;
837     resize.min = r->KeyRange.low + 1;
838     resize.max = next_region ? next_region->KeyRange.low : 128;
839 persson 1623 return resize.min != resize.max;
840 schoenebeck 1225 }
841     }
842     prev_region = r;
843     }
844     }
845     return false;
846     }
847    
848 schoenebeck 1339 sigc::signal<void>& RegionChooser::signal_region_selected()
849 schoenebeck 1225 {
850 persson 1261 return region_selected;
851 schoenebeck 1225 }
852    
853 schoenebeck 1339 sigc::signal<void>& RegionChooser::signal_instrument_changed()
854 persson 1261 {
855     return instrument_changed;
856     }
857    
858 schoenebeck 1225 void RegionChooser::show_region_properties()
859     {
860     if (!region) return;
861 persson 1831 Gtk::Dialog dialog(_("Region Properties"), true /*modal*/);
862 schoenebeck 1225 // add "Keygroup" checkbox
863 persson 1831 Gtk::CheckButton checkBoxKeygroup(_("Member of a Keygroup (Exclusive Group)"));
864 schoenebeck 1225 checkBoxKeygroup.set_active(region->KeyGroup);
865     dialog.get_vbox()->pack_start(checkBoxKeygroup);
866     checkBoxKeygroup.show();
867     // add "Keygroup" spinbox
868 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
869     Gtk::Adjustment adjustment(1, 1, 999);
870 schoenebeck 1225 Gtk::SpinButton spinBox(adjustment);
871 persson 2169 #else
872     Gtk::SpinButton spinBox(Gtk::Adjustment::create(1, 1, 999));
873     #endif
874 schoenebeck 1225 if (region->KeyGroup) spinBox.set_value(region->KeyGroup);
875     dialog.get_vbox()->pack_start(spinBox);
876     spinBox.show();
877     // add OK and CANCEL buttons to the dialog
878     dialog.add_button(Gtk::Stock::OK, 0);
879     dialog.add_button(Gtk::Stock::CANCEL, 1);
880     dialog.show_all_children();
881     if (!dialog.run()) { // OK selected ...
882     region->KeyGroup =
883     (checkBoxKeygroup.get_active()) ? spinBox.get_value_as_int() : 0;
884     }
885     }
886    
887     void RegionChooser::add_region()
888     {
889 schoenebeck 1322 instrument_struct_to_be_changed_signal.emit(instrument);
890    
891 schoenebeck 1225 region = instrument->AddRegion();
892 schoenebeck 1336 region->SetKeyRange(new_region_pos, new_region_pos);
893 schoenebeck 1225
894 schoenebeck 1322 instrument_struct_changed_signal.emit(instrument);
895 persson 1623 regions.update(instrument);
896 schoenebeck 1322
897 schoenebeck 1225 queue_draw();
898 persson 1261 region_selected();
899 persson 1677 dimensionManager.set_region(region);
900 persson 1261 instrument_changed();
901 schoenebeck 1225 }
902    
903     void RegionChooser::delete_region()
904     {
905 schoenebeck 1322 instrument_struct_to_be_changed_signal.emit(instrument);
906 schoenebeck 1225 instrument->DeleteRegion(region);
907 schoenebeck 1322 instrument_struct_changed_signal.emit(instrument);
908 persson 1623 regions.update(instrument);
909 schoenebeck 1322
910 schoenebeck 1225 region = 0;
911     queue_draw();
912 persson 1261 region_selected();
913 persson 1677 dimensionManager.set_region(region);
914 persson 1261 instrument_changed();
915 schoenebeck 1225 }
916    
917     void RegionChooser::manage_dimensions()
918     {
919     gig::Region* region = get_region();
920     if (!region) return;
921     dimensionManager.show(region);
922     }
923    
924     void RegionChooser::on_dimension_manager_changed() {
925 persson 1261 region_selected();
926     instrument_changed();
927 schoenebeck 1225 }
928 schoenebeck 1322
929 schoenebeck 1339 sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_to_be_changed() {
930 schoenebeck 1322 return instrument_struct_to_be_changed_signal;
931     }
932    
933 schoenebeck 1339 sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_changed() {
934 schoenebeck 1322 return instrument_struct_changed_signal;
935     }
936    
937 schoenebeck 1339 sigc::signal<void, gig::Region*>& RegionChooser::signal_region_to_be_changed() {
938 schoenebeck 1322 return region_to_be_changed_signal;
939     }
940    
941 schoenebeck 1339 sigc::signal<void, gig::Region*>& RegionChooser::signal_region_changed_signal() {
942 schoenebeck 1322 return region_changed_signal;
943     }
944 schoenebeck 1660
945     sigc::signal<void, int/*key*/, int/*velocity*/>& RegionChooser::signal_keyboard_key_hit() {
946     return keyboard_key_hit_signal;
947     }
948    
949     sigc::signal<void, int/*key*/, int/*velocity*/>& RegionChooser::signal_keyboard_key_released() {
950     return keyboard_key_released_signal;
951     }

  ViewVC Help
Powered by ViewVC