/[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 2841 - (hide annotations) (download)
Sun Aug 30 10:00:49 2015 UTC (8 years, 7 months ago) by persson
File size: 33941 byte(s)
* allow building with G_DISABLE_DEPRECATED
* fixed building without liblinuxsampler on Mac
* fixed some compiler and cppcheck warnings

1 schoenebeck 1225 /*
2 schoenebeck 2695 * Copyright (C) 2006-2015 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 2773 #include "Settings.h"
33 schoenebeck 1225
34 schoenebeck 2627 #define REGION_BLOCK_HEIGHT 30
35 persson 2246 #define KEYBOARD_HEIGHT 40
36 schoenebeck 1660
37 persson 1623 void SortedRegions::update(gig::Instrument* instrument) {
38     // Usually, the regions in a gig file are ordered after their key
39     // range, but there are files where they are not. The
40     // RegionChooser code needs a sorted list of regions.
41     regions.clear();
42     if (instrument) {
43 persson 2151 for (gig::Region* r = instrument->GetFirstRegion() ;
44 persson 1623 r ;
45     r = instrument->GetNextRegion()) {
46     regions.push_back(r);
47     }
48     sort(regions.begin(), regions.end(), *this);
49     }
50     }
51    
52     gig::Region* SortedRegions::first() {
53     region_iterator = regions.begin();
54     return region_iterator == regions.end() ? 0 : *region_iterator;
55     }
56    
57     gig::Region* SortedRegions::next() {
58 persson 2841 ++region_iterator;
59 persson 1623 return region_iterator == regions.end() ? 0 : *region_iterator;
60     }
61    
62    
63    
64 schoenebeck 1661 RegionChooser::RegionChooser() :
65 persson 2169 activeKeyColor("red"),
66     red("#8070ff"),
67     grey1("grey69"),
68     white("white"),
69     black("black"),
70 schoenebeck 1661 m_VirtKeybModeChoice(_("Virtual Keyboard Mode")),
71     currentActiveKey(-1)
72 schoenebeck 1225 {
73 persson 2169 set_size_request(500, KEYBOARD_HEIGHT + REGION_BLOCK_HEIGHT);
74 schoenebeck 1225
75     instrument = 0;
76     region = 0;
77     resize.active = false;
78 persson 1303 move.active = false;
79 schoenebeck 1225 cursor_is_resize = false;
80 schoenebeck 1660 h1 = REGION_BLOCK_HEIGHT;
81 schoenebeck 1225
82 persson 1831 // properties of the virtual keyboard
83 schoenebeck 1661 {
84 persson 2246 const char* choices[] = { _("normal"), _("chord"), 0 };
85 schoenebeck 1661 static const virt_keyboard_mode_t values[] = {
86     VIRT_KEYBOARD_MODE_NORMAL,
87     VIRT_KEYBOARD_MODE_CHORD
88     };
89     m_VirtKeybModeChoice.set_choices(choices, values);
90     m_VirtKeybModeChoice.set_value(VIRT_KEYBOARD_MODE_NORMAL);
91     }
92     m_VirtKeybVelocityLabelDescr.set_text(_("Note-On Velocity:"));
93     m_VirtKeybVelocityLabel.set_text("-");
94     m_VirtKeybOffVelocityLabelDescr.set_text(_("Note-Off Velocity:"));
95     m_VirtKeybOffVelocityLabel.set_text("-");
96     m_VirtKeybPropsBox.pack_start(m_VirtKeybModeChoice.label, Gtk::PACK_SHRINK);
97     m_VirtKeybPropsBox.pack_start(m_VirtKeybModeChoice.widget, Gtk::PACK_SHRINK);
98     m_VirtKeybPropsBox.pack_start(m_VirtKeybVelocityLabelDescr, Gtk::PACK_SHRINK);
99     m_VirtKeybPropsBox.pack_start(m_VirtKeybVelocityLabel, Gtk::PACK_SHRINK);
100     m_VirtKeybPropsBox.pack_start(m_VirtKeybOffVelocityLabelDescr, Gtk::PACK_SHRINK);
101     m_VirtKeybPropsBox.pack_start(m_VirtKeybOffVelocityLabel, Gtk::PACK_SHRINK);
102     m_VirtKeybPropsBox.set_spacing(10);
103     m_VirtKeybPropsBox.show();
104 persson 2246 for (int i = 0 ; i < 128 ; i++) key_pressed[i] = false;
105 schoenebeck 1661
106 schoenebeck 1225 actionGroup = Gtk::ActionGroup::create();
107     actionGroup->add(Gtk::Action::create("Properties",
108     Gtk::Stock::PROPERTIES),
109     sigc::mem_fun(*this,
110     &RegionChooser::show_region_properties));
111     actionGroup->add(Gtk::Action::create("Remove", Gtk::Stock::REMOVE),
112     sigc::mem_fun(*this, &RegionChooser::delete_region));
113     actionGroup->add(Gtk::Action::create("Add", Gtk::Stock::ADD),
114     sigc::mem_fun(*this, &RegionChooser::add_region));
115     actionGroup->add(Gtk::Action::create("Dimensions", _("Dimensions...")),
116     sigc::mem_fun(*this, &RegionChooser::manage_dimensions));
117    
118     uiManager = Gtk::UIManager::create();
119     uiManager->insert_action_group(actionGroup);
120     Glib::ustring ui_info =
121     "<ui>"
122     " <popup name='PopupMenuInsideRegion'>"
123     " <menuitem action='Properties'/>"
124     " <menuitem action='Dimensions'/>"
125     " <menuitem action='Remove'/>"
126     " </popup>"
127     " <popup name='PopupMenuOutsideRegion'>"
128     " <menuitem action='Add'/>"
129     " </popup>"
130     "</ui>";
131     uiManager->add_ui_from_string(ui_info);
132    
133     popup_menu_inside_region = dynamic_cast<Gtk::Menu*>(
134     uiManager->get_widget("/PopupMenuInsideRegion"));
135     popup_menu_outside_region = dynamic_cast<Gtk::Menu*>(
136     uiManager->get_widget("/PopupMenuOutsideRegion"));
137    
138     add_events(Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK |
139     Gdk::POINTER_MOTION_MASK | Gdk::POINTER_MOTION_HINT_MASK);
140    
141 schoenebeck 1322 dimensionManager.region_to_be_changed_signal.connect(
142     region_to_be_changed_signal.make_slot()
143 schoenebeck 1225 );
144 schoenebeck 1322 dimensionManager.region_changed_signal.connect(
145     region_changed_signal.make_slot()
146     );
147     dimensionManager.region_changed_signal.connect(
148     sigc::hide(
149     sigc::mem_fun(*this, &RegionChooser::on_dimension_manager_changed)
150     )
151     );
152 schoenebeck 1660 keyboard_key_hit_signal.connect(
153     sigc::mem_fun(*this, &RegionChooser::on_note_on_event)
154     );
155     keyboard_key_released_signal.connect(
156     sigc::mem_fun(*this, &RegionChooser::on_note_off_event)
157     );
158 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."));
159 schoenebeck 1225 }
160    
161     RegionChooser::~RegionChooser()
162     {
163     }
164    
165 persson 2246 void RegionChooser::invalidate_key(int key) {
166     const int h = KEYBOARD_HEIGHT;
167     const int w = get_width() - 1;
168     int x1 = key_to_x(key - 0.5, w);
169     int x2 = key_to_x(key + 1.5, w);
170    
171     Gdk::Rectangle rect(x1 + 1, h1 + 1, x2 - x1 - 1, h - 2);
172     get_window()->invalidate_rect(rect, false);
173     }
174    
175 schoenebeck 1654 void RegionChooser::on_note_on_event(int key, int velocity) {
176 persson 2246 key_pressed[key] = true;
177     invalidate_key(key);
178 schoenebeck 1661 m_VirtKeybVelocityLabel.set_text(ToString(velocity));
179 schoenebeck 1654 }
180    
181     void RegionChooser::on_note_off_event(int key, int velocity) {
182 persson 2246 key_pressed[key] = false;
183     invalidate_key(key);
184 schoenebeck 1661 m_VirtKeybOffVelocityLabel.set_text(ToString(velocity));
185 schoenebeck 1654 }
186    
187 persson 2246
188 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
189 persson 2246 bool RegionChooser::on_expose_event(GdkEventExpose* e) {
190     double clipx1 = e->area.x;
191     double clipx2 = e->area.x + e->area.width;
192     double clipy1 = e->area.y;
193     double clipy2 = e->area.y + e->area.height;
194    
195     const Cairo::RefPtr<Cairo::Context>& cr =
196     get_window()->create_cairo_context();
197     #if 0
198 persson 2169 }
199     #endif
200 persson 2246 #else
201     bool RegionChooser::on_draw(const Cairo::RefPtr<Cairo::Context>& cr) {
202     double clipx1, clipx2, clipy1, clipy2;
203     cr->get_clip_extents(clipx1, clipy1, clipx2, clipy2);
204     #endif
205 schoenebeck 1225
206 persson 2169 cr->save();
207     cr->set_line_width(1);
208 schoenebeck 1225
209 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
210     const Gdk::Color bg = get_style()->get_bg(Gtk::STATE_NORMAL);
211     #else
212     const Gdk::RGBA bg = get_style_context()->get_background_color();
213     #endif
214     Gdk::Cairo::set_source_rgba(cr, bg);
215     cr->paint();
216 schoenebeck 1225
217 persson 2246 if (clipy2 > h1) {
218     draw_keyboard(cr, clipx1, clipx2);
219     }
220    
221     if (clipy1 < h1 && instrument) {
222     draw_regions(cr, clipx1, clipx2);
223     }
224    
225     cr->restore();
226    
227     return true;
228     }
229    
230     void RegionChooser::draw_keyboard(const Cairo::RefPtr<Cairo::Context>& cr,
231     int clip_low, int clip_high) {
232     const int h = KEYBOARD_HEIGHT;
233     const int w = get_width() - 1;
234     const int bh = int(h * 0.55);
235    
236 persson 2169 Gdk::Cairo::set_source_rgba(cr, black);
237     cr->rectangle(0.5, h1 + 0.5, w, h - 1);
238     cr->stroke();
239 schoenebeck 1225
240 persson 2246 int x1 = key_to_x(20.5, w);
241 persson 2169 Gdk::Cairo::set_source_rgba(cr, grey1);
242     cr->rectangle(1, h1 + 1, x1 - 1, h - 2);
243     cr->fill();
244 schoenebeck 1225
245 persson 2246 int x2 = key_to_x(109.5, w);
246 persson 2169 Gdk::Cairo::set_source_rgba(cr, white);
247     cr->rectangle(x1 + 1, h1 + 1, x2 - x1 - 1, h - 2);
248     cr->fill();
249 persson 2151
250 persson 2169 Gdk::Cairo::set_source_rgba(cr, grey1);
251     cr->rectangle(x2 + 1, h1 + 1, w - x2 - 1, h - 2);
252     cr->fill();
253 persson 2151
254 persson 2169 Gdk::Cairo::set_source_rgba(cr, black);
255 persson 2246
256     int clipkey1 = std::max(0, x_to_key_right(clip_low - 1, w));
257     int clipkey2 = std::min(x_to_key_right(clip_high - 1, w) + 1, 128);
258    
259     for (int i = clipkey1 ; i < clipkey2 ; i++) {
260 persson 2169 int note = (i + 3) % 12;
261 persson 2246 int x = key_to_x(i, w);
262 persson 2151
263 persson 2169 if (note == 1 || note == 4 || note == 6 ||
264     note == 9 || note == 11) {
265 persson 2246 // black key: short line in the middle, with a rectangle
266     // on top
267     int x2 = key_to_x(i + 0.5, w);
268 persson 2169 cr->move_to(x2 + 0.5, h1 + bh + 0.5);
269     cr->line_to(x2 + 0.5, h1 + h - 1);
270     cr->stroke();
271 persson 2151
272 persson 2246 int x3 = key_to_x(i + 1, w);
273 persson 2169 cr->rectangle(x, h1 + 1, x3 - x + 1, bh);
274     cr->fill();
275     } else if (note == 3 || note == 8) {
276 persson 2246 // C or F: long line to the left
277 persson 2169 cr->move_to(x + 0.5, h1 + 1);
278     cr->line_to(x + 0.5, h1 + h - 1);
279     cr->stroke();
280 persson 2246 }
281 persson 2151
282 persson 2246 if (key_pressed[i]) draw_key(cr, i);
283    
284     if (note == 3) draw_digit(cr, i);
285 persson 2169 }
286 persson 2246 }
287 schoenebeck 1225
288 persson 2151
289 persson 2246 void RegionChooser::draw_regions(const Cairo::RefPtr<Cairo::Context>& cr,
290     int clip_low, int clip_high) {
291     const int w = get_width() - 1;
292    
293     Gdk::Cairo::set_source_rgba(cr, black);
294     gig::Region* next_region;
295     int x3 = -1;
296     for (gig::Region* r = regions.first() ; r ; r = next_region) {
297     next_region = regions.next();
298    
299     if (x3 < 0) {
300     x3 = key_to_x(r->KeyRange.low, w);
301     if (x3 >= clip_high) break;
302     }
303     if (!next_region ||
304     r->KeyRange.high + 1 != next_region->KeyRange.low ||
305     r == region || next_region == region) {
306    
307     int x2 = key_to_x(r->KeyRange.high + 1, w);
308     if (x2 >= clip_low) {
309 persson 2169 cr->move_to(x3, 0.5);
310     cr->line_to(x2 + 0.5, 0.5);
311     cr->line_to(x2 + 0.5, h1 - 0.5);
312     cr->line_to(x3, h1 - 0.5);
313     cr->stroke();
314 persson 2151
315 persson 2246 Gdk::Cairo::set_source_rgba(cr, region == r ? red : white);
316 persson 2169 cr->rectangle(x3 + 1, 1, x2 - x3 - 1, h1 - 2);
317     cr->fill();
318     Gdk::Cairo::set_source_rgba(cr, black);
319 persson 2151 }
320 persson 2246 x3 = -1;
321 persson 2169 }
322 persson 2246 }
323 persson 2151
324 persson 2246 for (gig::Region* r = regions.first() ; r ; r = regions.next()) {
325     int x = key_to_x(r->KeyRange.low, w);
326 persson 2151
327 persson 2246 if (x < clip_low) continue;
328     if (x >= clip_high) break;
329    
330     cr->move_to(x + 0.5, 1);
331     cr->line_to(x + 0.5, h1 - 1);
332     cr->stroke();
333 schoenebeck 1225 }
334 schoenebeck 2536
335     // if there is no region yet, show the user some hint text that he may
336     // right click on this area to create a new region
337     if (!regions.first()) {
338     Glib::RefPtr<Pango::Context> context = get_pango_context();
339     Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create(context);
340     layout->set_alignment(Pango::ALIGN_CENTER);
341     layout->set_text(Glib::ustring("*** ") + _("Right click here to create a region.") + " ***");
342     layout->set_width(get_width() * Pango::SCALE);
343 schoenebeck 2627 //layout->set_height(get_height() * Pango::SCALE);
344     layout->set_spacing(10);
345     Gdk::Cairo::set_source_rgba(cr, red);
346     // get the text dimensions
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 schoenebeck 2773 const int range = region->KeyRange.high - region->KeyRange.low;
519     const int diff = pos - int(region->KeyRange.low);
520     region->SetKeyRange(pos, pos + range);
521     if (Settings::singleton()->moveRootNoteWithRegionMoved) {
522     for (int i = 0; i < 256; ++i) {
523     gig::DimensionRegion* dimrgn = region->pDimensionRegions[i];
524     if (!dimrgn || !dimrgn->pSample || !dimrgn->PitchTrack) continue;
525     dimrgn->UnityNote += diff;
526     }
527     }
528 persson 2246 regions.update(instrument);
529     instrument_changed.emit();
530     instrument_struct_changed_signal.emit(instrument);
531     }
532    
533 schoenebeck 1225 bool RegionChooser::on_button_press_event(GdkEventButton* event)
534     {
535     if (!instrument) return true;
536    
537 persson 2246 const int w = get_width() - 1;
538     const int k = x_to_key(event->x, w);
539 schoenebeck 1225
540 schoenebeck 1660 if (event->type == GDK_BUTTON_PRESS) {
541     if (event->y >= REGION_BLOCK_HEIGHT) {
542     int velocity = (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
543     int(float(event->y - REGION_BLOCK_HEIGHT) / float(KEYBOARD_HEIGHT) * 128.0f) + 1;
544 schoenebeck 1661 currentActiveKey = k;
545 schoenebeck 1660 keyboard_key_hit_signal.emit(k, velocity);
546     }
547     }
548    
549 schoenebeck 2641 // left mouse button double click
550     if (event->type == GDK_2BUTTON_PRESS && event->button == 1) {
551 schoenebeck 2663 if (event->y < REGION_BLOCK_HEIGHT) {
552     // show dimension manager dialog for this region
553     manage_dimensions();
554     }
555 schoenebeck 2641 }
556    
557 schoenebeck 1660 if (event->y >= REGION_BLOCK_HEIGHT) return true;
558 schoenebeck 1225 if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
559     gig::Region* r = get_region(k);
560     if (r) {
561     region = r;
562     queue_draw();
563 persson 1261 region_selected();
564 persson 1677 dimensionManager.set_region(region);
565 schoenebeck 1225 popup_menu_inside_region->popup(event->button, event->time);
566     } else {
567     new_region_pos = k;
568     popup_menu_outside_region->popup(event->button, event->time);
569     }
570     } else {
571     if (is_in_resize_zone(event->x, event->y)) {
572 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
573 schoenebeck 1225 get_window()->pointer_grab(false,
574     Gdk::BUTTON_RELEASE_MASK |
575     Gdk::POINTER_MOTION_MASK |
576     Gdk::POINTER_MOTION_HINT_MASK,
577 persson 2169 Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW),
578     event->time);
579     #else
580     Glib::wrap(event->device, true)->grab(get_window(),
581     Gdk::OWNERSHIP_NONE,
582     false,
583     Gdk::BUTTON_RELEASE_MASK |
584     Gdk::POINTER_MOTION_MASK |
585     Gdk::POINTER_MOTION_HINT_MASK,
586     Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW),
587     event->time);
588     #endif
589 schoenebeck 1225 resize.active = true;
590     } else {
591     gig::Region* r = get_region(k);
592     if (r) {
593     region = r;
594     queue_draw();
595 persson 1261 region_selected();
596 persson 1677 dimensionManager.set_region(region);
597 persson 1262
598 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
599 persson 1262 get_window()->pointer_grab(false,
600     Gdk::BUTTON_RELEASE_MASK |
601     Gdk::POINTER_MOTION_MASK |
602     Gdk::POINTER_MOTION_HINT_MASK,
603 persson 2169 Gdk::Cursor(Gdk::FLEUR),
604     event->time);
605     #else
606     Glib::wrap(event->device, true)->grab(get_window(),
607     Gdk::OWNERSHIP_NONE,
608     false,
609     Gdk::BUTTON_RELEASE_MASK |
610     Gdk::POINTER_MOTION_MASK |
611     Gdk::POINTER_MOTION_HINT_MASK,
612     Gdk::Cursor::create(Gdk::FLEUR),
613     event->time);
614     #endif
615 persson 1262 move.active = true;
616 persson 2246 move.offset = event->x - key_to_x(region->KeyRange.low, w);
617 schoenebeck 1225 }
618     }
619     }
620     return true;
621     }
622    
623     gig::Region* RegionChooser::get_region(int key)
624     {
625 persson 2246 for (gig::Region* r = regions.first() ; r ; r = regions.next()) {
626 schoenebeck 1225 if (key < r->KeyRange.low) return 0;
627 persson 2246 if (key <= r->KeyRange.high) return r;
628 schoenebeck 1225 }
629     return 0;
630     }
631    
632 schoenebeck 2695 void RegionChooser::set_region(gig::Region* region) {
633     this->region = region;
634     queue_draw();
635     region_selected();
636     dimensionManager.set_region(region);
637     }
638    
639 persson 1262 void RegionChooser::motion_resize_region(int x, int y)
640 schoenebeck 1225 {
641 persson 1623 const int w = get_width() - 1;
642 schoenebeck 1225
643 persson 1262 int k = int(double(x) / w * 128.0 + 0.5);
644 schoenebeck 1225
645 persson 1262 if (k < resize.min) k = resize.min;
646     else if (k > resize.max) k = resize.max;
647    
648     if (k != resize.pos) {
649     if (resize.mode == resize.undecided) {
650     if (k < resize.pos) {
651     // edit high limit of prev_region
652     resize.max = resize.region->KeyRange.low;
653     resize.region = resize.prev_region;
654     resize.mode = resize.moving_high_limit;
655     } else {
656     // edit low limit of region
657     resize.min = resize.prev_region->KeyRange.high + 1;
658     resize.mode = resize.moving_low_limit;
659 schoenebeck 1225 }
660 persson 1262 }
661 persson 2246 resize.pos = k;
662 persson 2151
663 persson 2246 int x1, x2;
664 persson 1262 if (resize.mode == resize.moving_high_limit) {
665 persson 2246 if (resize.region->KeyRange.high < resize.pos - 1) {
666     x1 = resize.region->KeyRange.high;
667     x2 = resize.pos - 1;
668 persson 1262 } else {
669 persson 2246 x1 = resize.pos - 1;
670     x2 = resize.region->KeyRange.high;
671 schoenebeck 1225 }
672 persson 1262 } else {
673 persson 2246 if (resize.region->KeyRange.low < resize.pos) {
674     x1 = resize.region->KeyRange.low;
675     x2 = resize.pos;
676 persson 1262 } else {
677 persson 2246 x1 = resize.pos;
678     x2 = resize.region->KeyRange.low;
679 persson 1262 }
680     }
681 persson 2246 x1 = key_to_x(x1, w);
682     x2 = key_to_x(x2 + 1, w) + 1;
683     Gdk::Rectangle rect(x1, 0, x2 - x1, h1);
684    
685     update_after_resize();
686    
687     get_window()->invalidate_rect(rect, false);
688 persson 1262 }
689     }
690 schoenebeck 1225
691 persson 1262 void RegionChooser::motion_move_region(int x, int y)
692     {
693 persson 1623 const int w = get_width() - 1;
694 persson 1262
695 persson 2246 int l = int(double(x - move.offset) / w * 128.0 + 0.5);
696    
697     if (l == region->KeyRange.low) return;
698     int new_l;
699     int regionsize = region->KeyRange.high - region->KeyRange.low;
700 persson 1262 int a = 0;
701 persson 2246 if (l > region->KeyRange.low) {
702 persson 1623 for (gig::Region* r = regions.first() ; ; r = regions.next()) {
703 persson 1262 if (r != region) {
704     int b = r ? r->KeyRange.low : 128;
705    
706     // gap: from a to b (not inclusive b)
707    
708 persson 2246 if (region->KeyRange.high >= b) {
709 persson 1262 // not found the current gap yet, just continue
710 schoenebeck 1225 } else {
711 persson 1262
712 persson 2246 if (a > l) {
713 persson 1262 // this gap is too far to the right, break
714     break;
715     }
716    
717 persson 2246 int newhigh = std::min(l + regionsize, b - 1);
718     int newlo = newhigh - regionsize;
719 persson 1262
720     if (newlo >= a) {
721     // yes it fits - it's a candidate
722 persson 2246 new_l = newlo;
723 persson 1262 }
724 schoenebeck 1225 }
725 persson 1262 if (!r) break;
726     a = r->KeyRange.high + 1;
727     }
728     }
729     } else {
730 persson 1623 for (gig::Region* r = regions.first() ; ; r = regions.next()) {
731 persson 1262 if (r != region) {
732     int b = r ? r->KeyRange.low : 128;
733    
734     // gap from a to b (not inclusive b)
735    
736 persson 2246 if (l + regionsize >= b) {
737 persson 1262 // not found the current gap yet, just continue
738 schoenebeck 1225 } else {
739 persson 1262
740 persson 2246 if (a > region->KeyRange.low) {
741 persson 1262 // this gap is too far to the right, break
742     break;
743     }
744    
745 persson 2246 int newlo = std::max(l, a);
746     int newhigh = newlo + regionsize;
747 persson 1262
748     if (newhigh < b) {
749     // yes it fits - break as the first one is the best
750 persson 2246 new_l = newlo;
751 persson 1262 break;
752     }
753 schoenebeck 1225 }
754 persson 1262 if (!r) break;
755     a = r->KeyRange.high + 1;
756 schoenebeck 1225 }
757     }
758 persson 1262 }
759 persson 2246 if (new_l == region->KeyRange.low) return;
760 persson 1262
761 persson 2246 int x1 = key_to_x(std::min(int(region->KeyRange.low), new_l), w);
762     int x2 = key_to_x(std::max(int(region->KeyRange.high),
763     new_l + regionsize) + 1, w) + 1;
764 persson 2169
765 persson 2246 Gdk::Rectangle rect(x1, 0, x2 - x1, h1);
766     update_after_move(new_l);
767 persson 1262
768 persson 2246 get_window()->invalidate_rect(rect, false);
769 persson 1262 }
770    
771    
772     bool RegionChooser::on_motion_notify_event(GdkEventMotion* event)
773     {
774     Glib::RefPtr<Gdk::Window> window = get_window();
775     int x, y;
776     Gdk::ModifierType state = Gdk::ModifierType(0);
777     window->get_pointer(x, y, state);
778    
779 schoenebeck 1661 // handle virtual MIDI keyboard
780     if (m_VirtKeybModeChoice.get_value() != VIRT_KEYBOARD_MODE_CHORD &&
781     currentActiveKey > 0 &&
782     event->y >= REGION_BLOCK_HEIGHT &&
783     event->y < REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT)
784     {
785 persson 2246 const int k = x_to_key(event->x, get_width() - 1);
786 persson 1898 if (k != currentActiveKey) {
787     int velocity =
788     (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
789     int(float(event->y - REGION_BLOCK_HEIGHT) /
790     float(KEYBOARD_HEIGHT) * 128.0f) + 1;
791     if (velocity <= 0) velocity = 1;
792     keyboard_key_released_signal.emit(currentActiveKey, velocity);
793     currentActiveKey = k;
794     keyboard_key_hit_signal.emit(k, velocity);
795     }
796 schoenebeck 1661 }
797    
798 persson 1262 if (resize.active) {
799     motion_resize_region(x, y);
800     } else if (move.active) {
801     motion_move_region(x, y);
802     } else {
803 schoenebeck 1225 if (is_in_resize_zone(x, y)) {
804     if (!cursor_is_resize) {
805 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
806 persson 1262 window->set_cursor(Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW));
807 persson 2169 #else
808     window->set_cursor(Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW));
809     #endif
810 schoenebeck 1225 cursor_is_resize = true;
811     }
812     } else if (cursor_is_resize) {
813     window->set_cursor();
814     cursor_is_resize = false;
815     }
816     }
817    
818     return true;
819     }
820    
821     bool RegionChooser::is_in_resize_zone(double x, double y) {
822 persson 1623 const int w = get_width() - 1;
823 schoenebeck 1225
824     if (instrument && y >= 0 && y <= h1) {
825     gig::Region* prev_region = 0;
826     gig::Region* next_region;
827 persson 1623 for (gig::Region* r = regions.first(); r ; r = next_region) {
828     next_region = regions.next();
829 schoenebeck 1225
830 persson 2246 int lo = key_to_x(r->KeyRange.low, w);
831 schoenebeck 1225 if (x <= lo - 2) break;
832     if (x < lo + 2) {
833     resize.region = r;
834     resize.pos = r->KeyRange.low;
835     resize.max = r->KeyRange.high;
836    
837     if (prev_region && prev_region->KeyRange.high + 1 == r->KeyRange.low) {
838     // we don't know yet if it's the high limit of
839     // prev_region or the low limit of r that's going
840     // to be edited
841     resize.mode = resize.undecided;
842     resize.min = prev_region->KeyRange.low + 1;
843     resize.prev_region = prev_region;
844 persson 1623 return resize.min != resize.max;
845 schoenebeck 1225 }
846    
847     // edit low limit
848     resize.mode = resize.moving_low_limit;
849     resize.min = prev_region ? prev_region->KeyRange.high + 1 : 0;
850 persson 1623 return resize.min != resize.max;
851 schoenebeck 1225 }
852     if (!next_region || r->KeyRange.high + 1 != next_region->KeyRange.low) {
853 persson 2246 int hi = key_to_x(r->KeyRange.high + 1, w);
854 schoenebeck 1225 if (x <= hi - 2) break;
855     if (x < hi + 2) {
856     // edit high limit
857     resize.region = r;
858     resize.pos = r->KeyRange.high + 1;
859     resize.mode = resize.moving_high_limit;
860     resize.min = r->KeyRange.low + 1;
861     resize.max = next_region ? next_region->KeyRange.low : 128;
862 persson 1623 return resize.min != resize.max;
863 schoenebeck 1225 }
864     }
865     prev_region = r;
866     }
867     }
868     return false;
869     }
870    
871 schoenebeck 1339 sigc::signal<void>& RegionChooser::signal_region_selected()
872 schoenebeck 1225 {
873 persson 1261 return region_selected;
874 schoenebeck 1225 }
875    
876 schoenebeck 1339 sigc::signal<void>& RegionChooser::signal_instrument_changed()
877 persson 1261 {
878     return instrument_changed;
879     }
880    
881 schoenebeck 1225 void RegionChooser::show_region_properties()
882     {
883     if (!region) return;
884 persson 1831 Gtk::Dialog dialog(_("Region Properties"), true /*modal*/);
885 schoenebeck 1225 // add "Keygroup" checkbox
886 persson 1831 Gtk::CheckButton checkBoxKeygroup(_("Member of a Keygroup (Exclusive Group)"));
887 schoenebeck 1225 checkBoxKeygroup.set_active(region->KeyGroup);
888     dialog.get_vbox()->pack_start(checkBoxKeygroup);
889     checkBoxKeygroup.show();
890     // add "Keygroup" spinbox
891 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
892     Gtk::Adjustment adjustment(1, 1, 999);
893 schoenebeck 1225 Gtk::SpinButton spinBox(adjustment);
894 persson 2169 #else
895     Gtk::SpinButton spinBox(Gtk::Adjustment::create(1, 1, 999));
896     #endif
897 schoenebeck 1225 if (region->KeyGroup) spinBox.set_value(region->KeyGroup);
898     dialog.get_vbox()->pack_start(spinBox);
899     spinBox.show();
900     // add OK and CANCEL buttons to the dialog
901     dialog.add_button(Gtk::Stock::OK, 0);
902     dialog.add_button(Gtk::Stock::CANCEL, 1);
903     dialog.show_all_children();
904     if (!dialog.run()) { // OK selected ...
905     region->KeyGroup =
906     (checkBoxKeygroup.get_active()) ? spinBox.get_value_as_int() : 0;
907     }
908     }
909    
910     void RegionChooser::add_region()
911     {
912 schoenebeck 1322 instrument_struct_to_be_changed_signal.emit(instrument);
913    
914 schoenebeck 1225 region = instrument->AddRegion();
915 schoenebeck 1336 region->SetKeyRange(new_region_pos, new_region_pos);
916 schoenebeck 1225
917 schoenebeck 1322 instrument_struct_changed_signal.emit(instrument);
918 persson 1623 regions.update(instrument);
919 schoenebeck 1322
920 schoenebeck 1225 queue_draw();
921 persson 1261 region_selected();
922 persson 1677 dimensionManager.set_region(region);
923 persson 1261 instrument_changed();
924 schoenebeck 1225 }
925    
926     void RegionChooser::delete_region()
927     {
928 schoenebeck 1322 instrument_struct_to_be_changed_signal.emit(instrument);
929 schoenebeck 1225 instrument->DeleteRegion(region);
930 schoenebeck 1322 instrument_struct_changed_signal.emit(instrument);
931 persson 1623 regions.update(instrument);
932 schoenebeck 1322
933 schoenebeck 1225 region = 0;
934     queue_draw();
935 persson 1261 region_selected();
936 persson 1677 dimensionManager.set_region(region);
937 persson 1261 instrument_changed();
938 schoenebeck 1225 }
939    
940     void RegionChooser::manage_dimensions()
941     {
942     gig::Region* region = get_region();
943     if (!region) return;
944     dimensionManager.show(region);
945     }
946    
947     void RegionChooser::on_dimension_manager_changed() {
948 persson 1261 region_selected();
949     instrument_changed();
950 schoenebeck 1225 }
951 schoenebeck 1322
952 schoenebeck 1339 sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_to_be_changed() {
953 schoenebeck 1322 return instrument_struct_to_be_changed_signal;
954     }
955    
956 schoenebeck 1339 sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_changed() {
957 schoenebeck 1322 return instrument_struct_changed_signal;
958     }
959    
960 schoenebeck 1339 sigc::signal<void, gig::Region*>& RegionChooser::signal_region_to_be_changed() {
961 schoenebeck 1322 return region_to_be_changed_signal;
962     }
963    
964 schoenebeck 1339 sigc::signal<void, gig::Region*>& RegionChooser::signal_region_changed_signal() {
965 schoenebeck 1322 return region_changed_signal;
966     }
967 schoenebeck 1660
968     sigc::signal<void, int/*key*/, int/*velocity*/>& RegionChooser::signal_keyboard_key_hit() {
969     return keyboard_key_hit_signal;
970     }
971    
972     sigc::signal<void, int/*key*/, int/*velocity*/>& RegionChooser::signal_keyboard_key_released() {
973     return keyboard_key_released_signal;
974     }

  ViewVC Help
Powered by ViewVC