/[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 3450 - (hide annotations) (download)
Wed Jan 2 16:39:20 2019 UTC (5 years, 3 months ago) by schoenebeck
File size: 45676 byte(s)
* Fixed compiler errors with Gtk 3.24.x (patch by Miroslav Ć ulc).

1 schoenebeck 1225 /*
2 schoenebeck 3148 * Copyright (C) 2006-2017 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 schoenebeck 3364 #include "compat.h"
21 persson 3202 #include "global.h"
22 schoenebeck 1225 #include "regionchooser.h"
23 persson 2151
24 persson 1623 #include <algorithm>
25 schoenebeck 3148 #include <assert.h>
26 persson 2151
27     #include <cairomm/context.h>
28     #include <gdkmm/general.h>
29 schoenebeck 3364 #if HAS_GDKMM_SEAT
30     # include <gdkmm/seat.h>
31     #endif
32 schoenebeck 1225 #include <gdkmm/cursor.h>
33 schoenebeck 3364 #if HAS_GTKMM_STOCK
34     # include <gtkmm/stock.h>
35     #endif
36 schoenebeck 3106 #include <gdkmm/pixbuf.h>
37 schoenebeck 1225 #include <gtkmm/spinbutton.h>
38     #include <gtkmm/dialog.h>
39    
40 schoenebeck 2773 #include "Settings.h"
41 schoenebeck 3106 #include "gfx/builtinpix.h"
42 schoenebeck 1225
43 schoenebeck 2627 #define REGION_BLOCK_HEIGHT 30
44 persson 2246 #define KEYBOARD_HEIGHT 40
45 schoenebeck 1660
46 schoenebeck 3106 struct RegionFeatures {
47     int sampleRefs;
48     int loops;
49 schoenebeck 3307 int validDimRegs;
50 schoenebeck 3106
51     RegionFeatures() {
52 schoenebeck 3307 sampleRefs = loops = validDimRegs = 0;
53 schoenebeck 3106 }
54     };
55    
56     static RegionFeatures regionFeatures(gig::Region* rgn) {
57     RegionFeatures f;
58     for (int i = 0; i < rgn->DimensionRegions; ++i) {
59     gig::DimensionRegion* dr = rgn->pDimensionRegions[i];
60 schoenebeck 3307 DimensionCase c = dimensionCaseOf(dr);
61     if (!isUsedCase(c, rgn)) continue;
62     f.validDimRegs++;
63 schoenebeck 3106 if (dr->pSample) f.sampleRefs++;
64     // the user doesn't care about loop if there is no valid sample reference
65     if (dr->pSample && dr->SampleLoops) f.loops++;
66     }
67     return f;
68     }
69    
70 persson 1623 void SortedRegions::update(gig::Instrument* instrument) {
71     // Usually, the regions in a gig file are ordered after their key
72     // range, but there are files where they are not. The
73     // RegionChooser code needs a sorted list of regions.
74     regions.clear();
75     if (instrument) {
76 persson 2151 for (gig::Region* r = instrument->GetFirstRegion() ;
77 persson 1623 r ;
78     r = instrument->GetNextRegion()) {
79     regions.push_back(r);
80     }
81     sort(regions.begin(), regions.end(), *this);
82     }
83     }
84    
85     gig::Region* SortedRegions::first() {
86     region_iterator = regions.begin();
87     return region_iterator == regions.end() ? 0 : *region_iterator;
88     }
89    
90     gig::Region* SortedRegions::next() {
91 persson 2841 ++region_iterator;
92 persson 1623 return region_iterator == regions.end() ? 0 : *region_iterator;
93     }
94    
95    
96    
97 schoenebeck 1661 RegionChooser::RegionChooser() :
98 persson 2169 activeKeyColor("red"),
99 schoenebeck 3131 blue("#4796ff"),
100 persson 2169 grey1("grey69"),
101     white("white"),
102     black("black"),
103 schoenebeck 1661 m_VirtKeybModeChoice(_("Virtual Keyboard Mode")),
104 schoenebeck 3148 currentActiveKey(-1),
105     modifyallregions(false)
106 schoenebeck 1225 {
107 persson 2169 set_size_request(500, KEYBOARD_HEIGHT + REGION_BLOCK_HEIGHT);
108 schoenebeck 1225
109 schoenebeck 3106 loadBuiltInPix();
110    
111 schoenebeck 3286 // create blue hatched pattern
112 schoenebeck 3148 {
113 schoenebeck 3286 const int width = blueHatchedPattern->get_width();
114     const int height = blueHatchedPattern->get_height();
115     const int stride = blueHatchedPattern->get_rowstride();
116 schoenebeck 3148
117     // manually convert from RGBA to ARGB
118 schoenebeck 3286 this->blueHatchedPatternARGB = blueHatchedPattern->copy();
119 schoenebeck 3148 const int pixelSize = stride / width;
120     const int totalPixels = width * height;
121     assert(pixelSize == 4);
122 schoenebeck 3286 unsigned char* ptr = this->blueHatchedPatternARGB->get_pixels();
123 schoenebeck 3148 for (int iPixel = 0; iPixel < totalPixels; ++iPixel, ptr += pixelSize) {
124     const unsigned char r = ptr[0];
125     const unsigned char g = ptr[1];
126     const unsigned char b = ptr[2];
127     const unsigned char a = ptr[3];
128     ptr[0] = b;
129     ptr[1] = g;
130     ptr[2] = r;
131     ptr[3] = a;
132     }
133    
134     Cairo::RefPtr<Cairo::ImageSurface> imageSurface = Cairo::ImageSurface::create(
135 schoenebeck 3364 #if HAS_CAIROMM_CPP11_ENUMS
136     this->blueHatchedPatternARGB->get_pixels(), Cairo::Surface::Format::ARGB32, width, height, stride
137     #else
138 schoenebeck 3286 this->blueHatchedPatternARGB->get_pixels(), Cairo::FORMAT_ARGB32, width, height, stride
139 schoenebeck 3364 #endif
140 schoenebeck 3148 );
141 schoenebeck 3286 this->blueHatchedSurfacePattern = Cairo::SurfacePattern::create(imageSurface);
142 schoenebeck 3364 #if HAS_CAIROMM_CPP11_ENUMS
143     this->blueHatchedSurfacePattern->set_extend(Cairo::Pattern::Extend::REPEAT);
144     #else
145 schoenebeck 3286 this->blueHatchedSurfacePattern->set_extend(Cairo::EXTEND_REPEAT);
146 schoenebeck 3364 #endif
147 schoenebeck 3148 }
148    
149 schoenebeck 1225 instrument = 0;
150     region = 0;
151     resize.active = false;
152 persson 1303 move.active = false;
153 schoenebeck 1225 cursor_is_resize = false;
154 schoenebeck 1660 h1 = REGION_BLOCK_HEIGHT;
155 schoenebeck 1225
156 persson 1831 // properties of the virtual keyboard
157 schoenebeck 1661 {
158 persson 2246 const char* choices[] = { _("normal"), _("chord"), 0 };
159 schoenebeck 1661 static const virt_keyboard_mode_t values[] = {
160     VIRT_KEYBOARD_MODE_NORMAL,
161     VIRT_KEYBOARD_MODE_CHORD
162     };
163     m_VirtKeybModeChoice.set_choices(choices, values);
164     m_VirtKeybModeChoice.set_value(VIRT_KEYBOARD_MODE_NORMAL);
165     }
166     m_VirtKeybVelocityLabelDescr.set_text(_("Note-On Velocity:"));
167     m_VirtKeybVelocityLabel.set_text("-");
168     m_VirtKeybOffVelocityLabelDescr.set_text(_("Note-Off Velocity:"));
169     m_VirtKeybOffVelocityLabel.set_text("-");
170     m_VirtKeybPropsBox.pack_start(m_VirtKeybModeChoice.label, Gtk::PACK_SHRINK);
171     m_VirtKeybPropsBox.pack_start(m_VirtKeybModeChoice.widget, Gtk::PACK_SHRINK);
172     m_VirtKeybPropsBox.pack_start(m_VirtKeybVelocityLabelDescr, Gtk::PACK_SHRINK);
173     m_VirtKeybPropsBox.pack_start(m_VirtKeybVelocityLabel, Gtk::PACK_SHRINK);
174     m_VirtKeybPropsBox.pack_start(m_VirtKeybOffVelocityLabelDescr, Gtk::PACK_SHRINK);
175     m_VirtKeybPropsBox.pack_start(m_VirtKeybOffVelocityLabel, Gtk::PACK_SHRINK);
176     m_VirtKeybPropsBox.set_spacing(10);
177     m_VirtKeybPropsBox.show();
178 persson 2246 for (int i = 0 ; i < 128 ; i++) key_pressed[i] = false;
179 schoenebeck 1661
180 schoenebeck 3364 actionGroup = ActionGroup::create();
181     #if USE_GLIB_ACTION
182     actionGroup->add_action(
183     "Properties", sigc::mem_fun(*this, &RegionChooser::show_region_properties)
184     );
185     actionGroup->add_action(
186     "Remove", sigc::mem_fun(*this, &RegionChooser::delete_region)
187     );
188     actionGroup->add_action(
189     "Add", sigc::mem_fun(*this, &RegionChooser::add_region)
190     );
191     actionGroup->add_action(
192     "Dimensions", sigc::mem_fun(*this, &RegionChooser::manage_dimensions)
193     );
194     insert_action_group("PopupMenuInsideRegion", actionGroup);
195     #else
196 schoenebeck 3158 actionGroup->add(Gtk::Action::create("Properties",
197     Gtk::Stock::PROPERTIES),
198 schoenebeck 1225 sigc::mem_fun(*this,
199     &RegionChooser::show_region_properties));
200 schoenebeck 3158 actionGroup->add(Gtk::Action::create("Remove", Gtk::Stock::REMOVE),
201 schoenebeck 1225 sigc::mem_fun(*this, &RegionChooser::delete_region));
202 schoenebeck 3158 actionGroup->add(Gtk::Action::create("Add", Gtk::Stock::ADD),
203 schoenebeck 1225 sigc::mem_fun(*this, &RegionChooser::add_region));
204     actionGroup->add(Gtk::Action::create("Dimensions", _("Dimensions...")),
205     sigc::mem_fun(*this, &RegionChooser::manage_dimensions));
206 schoenebeck 3364 #endif
207 schoenebeck 1225
208 schoenebeck 3364 #if USE_GTKMM_BUILDER
209     uiManager = Gtk::Builder::create();
210     Glib::ustring ui_info =
211     "<interface>"
212     " <menu id='menu-PopupMenuInsideRegion'>"
213     " <section>"
214     " <item>"
215     " <attribute name='label' translatable='yes'>Properties</attribute>"
216     " <attribute name='action'>PopupMenuInsideRegion.Properties</attribute>"
217     " </item>"
218     " <item>"
219     " <attribute name='label' translatable='yes'>Dimensions</attribute>"
220     " <attribute name='action'>PopupMenuInsideRegion.Dimensions</attribute>"
221     " </item>"
222     " <item>"
223     " <attribute name='label' translatable='yes'>Remove</attribute>"
224     " <attribute name='action'>PopupMenuInsideRegion.Remove</attribute>"
225     " </item>"
226     " </section>"
227     " </menu>"
228     " <menu id='menu-PopupMenuOutsideRegion'>"
229     " <section>"
230     " <item>"
231     " <attribute name='label' translatable='yes'>Add</attribute>"
232     " <attribute name='action'>PopupMenuInsideRegion.Add</attribute>"
233     " </item>"
234     " </section>"
235     " </menu>"
236     "</interface>";
237     uiManager->add_from_string(ui_info);
238    
239     popup_menu_inside_region = new Gtk::Menu(
240     Glib::RefPtr<Gio::Menu>::cast_dynamic(
241     uiManager->get_object("menu-PopupMenuInsideRegion")
242     )
243     );
244     popup_menu_outside_region = new Gtk::Menu(
245     Glib::RefPtr<Gio::Menu>::cast_dynamic(
246     uiManager->get_object("menu-PopupMenuOutsideRegion")
247     )
248     );
249     #else
250 schoenebeck 1225 uiManager = Gtk::UIManager::create();
251     uiManager->insert_action_group(actionGroup);
252     Glib::ustring ui_info =
253     "<ui>"
254     " <popup name='PopupMenuInsideRegion'>"
255     " <menuitem action='Properties'/>"
256     " <menuitem action='Dimensions'/>"
257     " <menuitem action='Remove'/>"
258     " </popup>"
259     " <popup name='PopupMenuOutsideRegion'>"
260     " <menuitem action='Add'/>"
261     " </popup>"
262     "</ui>";
263     uiManager->add_ui_from_string(ui_info);
264    
265     popup_menu_inside_region = dynamic_cast<Gtk::Menu*>(
266     uiManager->get_widget("/PopupMenuInsideRegion"));
267     popup_menu_outside_region = dynamic_cast<Gtk::Menu*>(
268     uiManager->get_widget("/PopupMenuOutsideRegion"));
269    
270 schoenebeck 3364 #endif // USE_GTKMM_BUILDER
271    
272 schoenebeck 3450 #if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION > 24)
273 schoenebeck 3364 # warning GTKMM4 event registration code missing for regionchooser!
274     //add_events(Gdk::EventMask::BUTTON_PRESS_MASK);
275     #else
276 schoenebeck 1225 add_events(Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK |
277     Gdk::POINTER_MOTION_MASK | Gdk::POINTER_MOTION_HINT_MASK);
278 schoenebeck 3364 #endif
279 schoenebeck 1225
280 schoenebeck 1322 dimensionManager.region_to_be_changed_signal.connect(
281     region_to_be_changed_signal.make_slot()
282 schoenebeck 1225 );
283 schoenebeck 1322 dimensionManager.region_changed_signal.connect(
284     region_changed_signal.make_slot()
285     );
286     dimensionManager.region_changed_signal.connect(
287     sigc::hide(
288     sigc::mem_fun(*this, &RegionChooser::on_dimension_manager_changed)
289     )
290     );
291 schoenebeck 1660 keyboard_key_hit_signal.connect(
292     sigc::mem_fun(*this, &RegionChooser::on_note_on_event)
293     );
294     keyboard_key_released_signal.connect(
295     sigc::mem_fun(*this, &RegionChooser::on_note_off_event)
296     );
297 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."));
298 schoenebeck 3409
299     Settings::singleton()->showTooltips.get_proxy().signal_changed().connect(
300     sigc::mem_fun(*this, &RegionChooser::on_show_tooltips_changed)
301     );
302    
303     on_show_tooltips_changed();
304 schoenebeck 1225 }
305    
306     RegionChooser::~RegionChooser()
307     {
308     }
309    
310 schoenebeck 3409 void RegionChooser::on_show_tooltips_changed() {
311     const bool b = Settings::singleton()->showTooltips;
312    
313     set_has_tooltip(b);
314     }
315    
316 schoenebeck 3148 void RegionChooser::setModifyAllRegions(bool b) {
317     modifyallregions = b;
318     // redraw required parts
319     queue_draw();
320     }
321    
322 persson 2246 void RegionChooser::invalidate_key(int key) {
323     const int h = KEYBOARD_HEIGHT;
324     const int w = get_width() - 1;
325     int x1 = key_to_x(key - 0.5, w);
326     int x2 = key_to_x(key + 1.5, w);
327    
328     Gdk::Rectangle rect(x1 + 1, h1 + 1, x2 - x1 - 1, h - 2);
329     get_window()->invalidate_rect(rect, false);
330     }
331    
332 schoenebeck 1654 void RegionChooser::on_note_on_event(int key, int velocity) {
333 persson 2246 key_pressed[key] = true;
334     invalidate_key(key);
335 schoenebeck 1661 m_VirtKeybVelocityLabel.set_text(ToString(velocity));
336 schoenebeck 1654 }
337    
338     void RegionChooser::on_note_off_event(int key, int velocity) {
339 persson 2246 key_pressed[key] = false;
340     invalidate_key(key);
341 schoenebeck 1661 m_VirtKeybOffVelocityLabel.set_text(ToString(velocity));
342 schoenebeck 1654 }
343    
344 persson 2246
345 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
346 persson 2246 bool RegionChooser::on_expose_event(GdkEventExpose* e) {
347     double clipx1 = e->area.x;
348     double clipx2 = e->area.x + e->area.width;
349     double clipy1 = e->area.y;
350     double clipy2 = e->area.y + e->area.height;
351    
352     const Cairo::RefPtr<Cairo::Context>& cr =
353     get_window()->create_cairo_context();
354     #if 0
355 persson 2169 }
356     #endif
357 persson 2246 #else
358     bool RegionChooser::on_draw(const Cairo::RefPtr<Cairo::Context>& cr) {
359     double clipx1, clipx2, clipy1, clipy2;
360     cr->get_clip_extents(clipx1, clipy1, clipx2, clipy2);
361     #endif
362 schoenebeck 1225
363 persson 2169 cr->save();
364     cr->set_line_width(1);
365 schoenebeck 1225
366 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
367     const Gdk::Color bg = get_style()->get_bg(Gtk::STATE_NORMAL);
368     #else
369 schoenebeck 3450 #if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION > 24)
370 schoenebeck 3364 GdkRGBA gdkBgRGBA;
371     gtk_style_context_get_background_color(get_style_context()->gobj(), &gdkBgRGBA);
372     const Gdk::RGBA bg = Glib::wrap(&gdkBgRGBA, true);
373     # else
374 persson 2169 const Gdk::RGBA bg = get_style_context()->get_background_color();
375 schoenebeck 3364 # endif
376 persson 2169 #endif
377     Gdk::Cairo::set_source_rgba(cr, bg);
378     cr->paint();
379 schoenebeck 1225
380 persson 2246 if (clipy2 > h1) {
381     draw_keyboard(cr, clipx1, clipx2);
382     }
383    
384     if (clipy1 < h1 && instrument) {
385     draw_regions(cr, clipx1, clipx2);
386     }
387    
388     cr->restore();
389    
390     return true;
391     }
392    
393     void RegionChooser::draw_keyboard(const Cairo::RefPtr<Cairo::Context>& cr,
394     int clip_low, int clip_high) {
395     const int h = KEYBOARD_HEIGHT;
396     const int w = get_width() - 1;
397     const int bh = int(h * 0.55);
398    
399 persson 2169 Gdk::Cairo::set_source_rgba(cr, black);
400     cr->rectangle(0.5, h1 + 0.5, w, h - 1);
401     cr->stroke();
402 schoenebeck 1225
403 persson 2246 int x1 = key_to_x(20.5, w);
404 persson 2169 Gdk::Cairo::set_source_rgba(cr, grey1);
405     cr->rectangle(1, h1 + 1, x1 - 1, h - 2);
406     cr->fill();
407 schoenebeck 1225
408 persson 2246 int x2 = key_to_x(109.5, w);
409 persson 2169 Gdk::Cairo::set_source_rgba(cr, white);
410     cr->rectangle(x1 + 1, h1 + 1, x2 - x1 - 1, h - 2);
411     cr->fill();
412 persson 2151
413 persson 2169 Gdk::Cairo::set_source_rgba(cr, grey1);
414     cr->rectangle(x2 + 1, h1 + 1, w - x2 - 1, h - 2);
415     cr->fill();
416 persson 2151
417 persson 2169 Gdk::Cairo::set_source_rgba(cr, black);
418 persson 2246
419     int clipkey1 = std::max(0, x_to_key_right(clip_low - 1, w));
420     int clipkey2 = std::min(x_to_key_right(clip_high - 1, w) + 1, 128);
421    
422     for (int i = clipkey1 ; i < clipkey2 ; i++) {
423 persson 2169 int note = (i + 3) % 12;
424 persson 2246 int x = key_to_x(i, w);
425 persson 2151
426 persson 2169 if (note == 1 || note == 4 || note == 6 ||
427     note == 9 || note == 11) {
428 persson 2246 // black key: short line in the middle, with a rectangle
429     // on top
430     int x2 = key_to_x(i + 0.5, w);
431 persson 2169 cr->move_to(x2 + 0.5, h1 + bh + 0.5);
432     cr->line_to(x2 + 0.5, h1 + h - 1);
433     cr->stroke();
434 persson 2151
435 persson 2246 int x3 = key_to_x(i + 1, w);
436 persson 2169 cr->rectangle(x, h1 + 1, x3 - x + 1, bh);
437     cr->fill();
438     } else if (note == 3 || note == 8) {
439 persson 2246 // C or F: long line to the left
440 persson 2169 cr->move_to(x + 0.5, h1 + 1);
441     cr->line_to(x + 0.5, h1 + h - 1);
442     cr->stroke();
443 persson 2246 }
444 persson 2151
445 persson 2246 if (key_pressed[i]) draw_key(cr, i);
446    
447     if (note == 3) draw_digit(cr, i);
448 persson 2169 }
449 persson 2246 }
450 schoenebeck 1225
451 persson 2151
452 persson 2246 void RegionChooser::draw_regions(const Cairo::RefPtr<Cairo::Context>& cr,
453     int clip_low, int clip_high) {
454     const int w = get_width() - 1;
455    
456     Gdk::Cairo::set_source_rgba(cr, black);
457     gig::Region* next_region;
458     int x3 = -1;
459     for (gig::Region* r = regions.first() ; r ; r = next_region) {
460     next_region = regions.next();
461    
462     if (x3 < 0) {
463     x3 = key_to_x(r->KeyRange.low, w);
464     if (x3 >= clip_high) break;
465     }
466     if (!next_region ||
467     r->KeyRange.high + 1 != next_region->KeyRange.low ||
468     r == region || next_region == region) {
469    
470     int x2 = key_to_x(r->KeyRange.high + 1, w);
471     if (x2 >= clip_low) {
472 persson 2169 cr->move_to(x3, 0.5);
473     cr->line_to(x2 + 0.5, 0.5);
474     cr->line_to(x2 + 0.5, h1 - 0.5);
475     cr->line_to(x3, h1 - 0.5);
476     cr->stroke();
477 persson 2151
478 schoenebeck 3148 if (region == r)
479     Gdk::Cairo::set_source_rgba(cr, blue);
480     else if (modifyallregions)
481 schoenebeck 3286 cr->set_source(blueHatchedSurfacePattern);
482 schoenebeck 3148 else
483     Gdk::Cairo::set_source_rgba(cr, white);
484    
485 persson 2169 cr->rectangle(x3 + 1, 1, x2 - x3 - 1, h1 - 2);
486     cr->fill();
487     Gdk::Cairo::set_source_rgba(cr, black);
488 persson 2151 }
489 persson 2246 x3 = -1;
490 persson 2169 }
491 persson 2246 }
492 persson 2151
493 persson 2246 for (gig::Region* r = regions.first() ; r ; r = regions.next()) {
494     int x = key_to_x(r->KeyRange.low, w);
495 schoenebeck 3106 int x2 = key_to_x(r->KeyRange.high + 1, w);
496 persson 2151
497 schoenebeck 3106 RegionFeatures features = regionFeatures(r);
498    
499     const bool bShowLoopSymbol = features.loops > 0;
500 schoenebeck 3307 const bool bShowSampleRefSymbol = features.sampleRefs < features.validDimRegs;
501 schoenebeck 3106 if (bShowLoopSymbol || bShowSampleRefSymbol) {
502     const int margin = 2;
503     const int wRgn = x2 - x;
504     //printf("x=%d x2=%d wRgn=%d\n", x, x2, wRgn);
505    
506     cr->save();
507     cr->set_line_width(1);
508     cr->rectangle(x, 1, wRgn, h1 - 1);
509     cr->clip();
510     if (bShowSampleRefSymbol) {
511     const int wPic = 8;
512     const int hPic = 8;
513     Gdk::Cairo::set_source_pixbuf(
514     cr, (features.sampleRefs) ? yellowDot : redDot,
515     x + (wRgn-wPic)/2.f,
516     (bShowLoopSymbol) ? margin : (h1-hPic)/2.f
517     );
518     cr->paint();
519     }
520     if (bShowLoopSymbol) {
521     const int wPic = 12;
522     const int hPic = 14;
523     Gdk::Cairo::set_source_pixbuf(
524 schoenebeck 3307 cr, (features.loops == features.validDimRegs) ? blackLoop : grayLoop,
525 schoenebeck 3106 x + (wRgn-wPic)/2.f,
526     (bShowSampleRefSymbol) ? h1 - hPic - margin : (h1-hPic)/2.f
527     );
528     cr->paint();
529     }
530     cr->restore();
531     }
532     }
533    
534     for (gig::Region* r = regions.first() ; r ; r = regions.next()) {
535     int x = key_to_x(r->KeyRange.low, w);
536    
537 persson 2246 if (x < clip_low) continue;
538     if (x >= clip_high) break;
539    
540     cr->move_to(x + 0.5, 1);
541     cr->line_to(x + 0.5, h1 - 1);
542     cr->stroke();
543 schoenebeck 1225 }
544 schoenebeck 2536
545     // if there is no region yet, show the user some hint text that he may
546     // right click on this area to create a new region
547     if (!regions.first()) {
548     Glib::RefPtr<Pango::Context> context = get_pango_context();
549     Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create(context);
550     layout->set_alignment(Pango::ALIGN_CENTER);
551     layout->set_text(Glib::ustring("*** ") + _("Right click here to create a region.") + " ***");
552     layout->set_width(get_width() * Pango::SCALE);
553 schoenebeck 2627 //layout->set_height(get_height() * Pango::SCALE);
554     layout->set_spacing(10);
555 schoenebeck 3131 Gdk::Cairo::set_source_rgba(cr, blue);
556 schoenebeck 2627 // get the text dimensions
557     int text_width, text_height;
558     layout->get_pixel_size(text_width, text_height);
559     cr->move_to(0, (REGION_BLOCK_HEIGHT - text_height) / 2);
560 schoenebeck 2536 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 16) || GTKMM_MAJOR_VERSION < 2
561     pango_cairo_show_layout(cr->cobj(), layout->gobj());
562     #else
563     layout->show_in_cairo_context(cr);
564     #endif
565     }
566 schoenebeck 1225 }
567    
568 schoenebeck 1654 bool RegionChooser::is_black_key(int key) {
569     const int note = (key + 3) % 12;
570     return note == 1 || note == 4 || note == 6 || note == 9 || note == 11;
571     }
572 schoenebeck 1225
573 persson 2246 void RegionChooser::draw_digit(const Cairo::RefPtr<Cairo::Context>& cr,
574     int key) {
575 schoenebeck 1660 const int h = KEYBOARD_HEIGHT;
576 persson 1658 const int w = get_width() - 1;
577 persson 2246 Glib::RefPtr<Pango::Layout> layout =
578     Pango::Layout::create(get_pango_context());
579 persson 1658 char buf[30];
580     sprintf(buf, "<span size=\"8000\">%d</span>", key / 12 - 1);
581     layout->set_markup(buf);
582     Pango::Rectangle rectangle = layout->get_logical_extents();
583     double text_w = double(rectangle.get_width()) / Pango::SCALE;
584     double text_h = double(rectangle.get_height()) / Pango::SCALE;
585     double x = w * (key + 0.75) / 128.0;
586 persson 2169 Gdk::Cairo::set_source_rgba(cr, black);
587 persson 2151 cr->move_to(int(x - text_w / 2 + 1), int(h1 + h - text_h + 0.5));
588     #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 16) || GTKMM_MAJOR_VERSION < 2
589     pango_cairo_show_layout(cr->cobj(), layout->gobj());
590     #else
591     layout->show_in_cairo_context(cr);
592     #endif
593 persson 1658 }
594    
595 persson 2246 void RegionChooser::draw_key(const Cairo::RefPtr<Cairo::Context>& cr,
596     int key) {
597 schoenebeck 1660 const int h = KEYBOARD_HEIGHT;
598 persson 1658 const int w = get_width() - 1;
599 schoenebeck 1225 const int bh = int(h * 0.55);
600    
601 persson 2246 Gdk::Cairo::set_source_rgba(cr, activeKeyColor);
602 schoenebeck 1225
603 persson 2151 int note = (key + 3) % 12;
604 persson 2246 int x = key_to_x(key, w) + 1;
605     int x2 = key_to_x(key + 1.5, w);
606     int x3 = key_to_x(key + 1, w);
607     int x4 = key_to_x(key - 0.5, w);
608 persson 2151 int w1 = x3 - x;
609     switch (note) {
610     case 0: case 5: case 10:
611     cr->rectangle(x, h1 + 1, w1, bh);
612     cr->fill();
613     cr->rectangle(x4 + 1, h1 + bh + 1, x2 - x4 - 1, h - bh - 2);
614     cr->fill();
615     break;
616     case 2: case 7:
617     cr->rectangle(x, h1 + 1, w1, bh);
618     cr->fill();
619     cr->rectangle(x4 + 1, h1 + bh + 1, x3 - x4 - 1, h - bh - 2);
620     cr->fill();
621     break;
622     case 3: case 8:
623     cr->rectangle(x, h1 + 1, w1, bh);
624     cr->fill();
625     cr->rectangle(x, h1 + bh + 1, x2 - x, h - bh - 2);
626     cr->fill();
627     break;
628     default:
629     cr->rectangle(x, h1 + 1, w1, bh - 1);
630     cr->fill();
631     break;
632 schoenebeck 1225 }
633 persson 2246 Gdk::Cairo::set_source_rgba(cr, black);
634 schoenebeck 1225 }
635    
636     void RegionChooser::set_instrument(gig::Instrument* instrument)
637     {
638     this->instrument = instrument;
639 persson 1623 regions.update(instrument);
640     region = regions.first();
641 schoenebeck 1225 queue_draw();
642 persson 1261 region_selected();
643 persson 1677 dimensionManager.set_region(region);
644 schoenebeck 1225 }
645    
646     bool RegionChooser::on_button_release_event(GdkEventButton* event)
647     {
648 persson 2246 const int k = x_to_key(event->x, get_width() - 1);
649 schoenebeck 1660
650 schoenebeck 1661 // handle-note off on virtual keyboard
651 schoenebeck 1660 if (event->type == GDK_BUTTON_RELEASE) {
652 schoenebeck 1661 int velocity = (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
653     int(float(event->y - REGION_BLOCK_HEIGHT) / float(KEYBOARD_HEIGHT) * 128.0f) + 1;
654     if (velocity <= 0) velocity = 1;
655     switch (m_VirtKeybModeChoice.get_value()) {
656     case VIRT_KEYBOARD_MODE_CHORD:
657     if (event->y >= REGION_BLOCK_HEIGHT)
658     keyboard_key_released_signal.emit(k, velocity);
659     break;
660     case VIRT_KEYBOARD_MODE_NORMAL:
661     default:
662     if (currentActiveKey >= 0 && currentActiveKey <= 127) {
663     keyboard_key_released_signal.emit(currentActiveKey, velocity);
664     currentActiveKey = -1;
665     }
666     break;
667 schoenebeck 1660 }
668     }
669    
670 schoenebeck 1225 if (resize.active) {
671 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
672 schoenebeck 1225 get_window()->pointer_ungrab(event->time);
673 persson 2169 #else
674 schoenebeck 3364 # if GTKMM_MAJOR_VERSION < 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION < 20)
675 persson 2169 Glib::wrap(event->device, true)->ungrab(event->time);
676 schoenebeck 3364 # else
677     gdk_device_ungrab(Glib::wrap(event->device, true)->gobj(), event->time);
678     # endif
679 persson 2169 #endif
680 schoenebeck 1225 resize.active = false;
681    
682     if (!is_in_resize_zone(event->x, event->y) && cursor_is_resize) {
683     get_window()->set_cursor();
684     cursor_is_resize = false;
685     }
686 persson 1262 } else if (move.active) {
687 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
688 persson 1262 get_window()->pointer_ungrab(event->time);
689 persson 2169 #else
690 schoenebeck 3364 # if GTKMM_MAJOR_VERSION < 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION < 20)
691 persson 2169 Glib::wrap(event->device, true)->ungrab(event->time);
692 schoenebeck 3364 # else
693     gdk_device_ungrab(Glib::wrap(event->device, true)->gobj(), event->time);
694     # endif
695 persson 2169 #endif
696 persson 1262 move.active = false;
697    
698     if (is_in_resize_zone(event->x, event->y)) {
699 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
700 persson 1262 get_window()->set_cursor(Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW));
701 persson 2169 #else
702 schoenebeck 3364 get_window()->set_cursor(
703     # if GTKMM_MAJOR_VERSION < 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION < 20)
704     Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW)
705     # else
706     Gdk::Cursor::create(
707     Glib::wrap(event->device, true)->get_seat()->get_display(),
708     Gdk::SB_H_DOUBLE_ARROW
709     )
710     # endif
711     );
712 persson 2169 #endif
713 persson 1262 cursor_is_resize = true;
714     }
715 schoenebeck 1225 }
716     return true;
717     }
718    
719 persson 2246 void RegionChooser::update_after_resize()
720     {
721     if (resize.mode == resize.moving_high_limit) {
722     if (resize.region->KeyRange.high != resize.pos - 1) {
723     instrument_struct_to_be_changed_signal.emit(instrument);
724     resize.region->SetKeyRange(resize.region->KeyRange.low,
725     resize.pos - 1);
726     regions.update(instrument);
727     instrument_changed.emit();
728     instrument_struct_changed_signal.emit(instrument);
729     }
730     } else if (resize.mode == resize.moving_low_limit) {
731     if (resize.region->KeyRange.low != resize.pos) {
732     instrument_struct_to_be_changed_signal.emit(instrument);
733     resize.region->SetKeyRange(resize.pos,
734     resize.region->KeyRange.high);
735     regions.update(instrument);
736     instrument_changed.emit();
737     instrument_struct_changed_signal.emit(instrument);
738     }
739     }
740     }
741    
742     void RegionChooser::update_after_move(int pos)
743     {
744     instrument_struct_to_be_changed_signal.emit(instrument);
745 schoenebeck 2773 const int range = region->KeyRange.high - region->KeyRange.low;
746     const int diff = pos - int(region->KeyRange.low);
747     region->SetKeyRange(pos, pos + range);
748     if (Settings::singleton()->moveRootNoteWithRegionMoved) {
749     for (int i = 0; i < 256; ++i) {
750     gig::DimensionRegion* dimrgn = region->pDimensionRegions[i];
751     if (!dimrgn || !dimrgn->pSample || !dimrgn->PitchTrack) continue;
752     dimrgn->UnityNote += diff;
753     }
754     }
755 persson 2246 regions.update(instrument);
756     instrument_changed.emit();
757     instrument_struct_changed_signal.emit(instrument);
758     }
759    
760 schoenebeck 1225 bool RegionChooser::on_button_press_event(GdkEventButton* event)
761     {
762     if (!instrument) return true;
763    
764 persson 2246 const int w = get_width() - 1;
765     const int k = x_to_key(event->x, w);
766 schoenebeck 1225
767 schoenebeck 1660 if (event->type == GDK_BUTTON_PRESS) {
768     if (event->y >= REGION_BLOCK_HEIGHT) {
769     int velocity = (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
770     int(float(event->y - REGION_BLOCK_HEIGHT) / float(KEYBOARD_HEIGHT) * 128.0f) + 1;
771 schoenebeck 1661 currentActiveKey = k;
772 schoenebeck 1660 keyboard_key_hit_signal.emit(k, velocity);
773     }
774     }
775    
776 schoenebeck 2641 // left mouse button double click
777     if (event->type == GDK_2BUTTON_PRESS && event->button == 1) {
778 schoenebeck 2663 if (event->y < REGION_BLOCK_HEIGHT) {
779     // show dimension manager dialog for this region
780     manage_dimensions();
781     }
782 schoenebeck 2641 }
783    
784 schoenebeck 1660 if (event->y >= REGION_BLOCK_HEIGHT) return true;
785 schoenebeck 1225 if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
786     gig::Region* r = get_region(k);
787     if (r) {
788     region = r;
789     queue_draw();
790 persson 1261 region_selected();
791 persson 1677 dimensionManager.set_region(region);
792 schoenebeck 1225 popup_menu_inside_region->popup(event->button, event->time);
793     } else {
794     new_region_pos = k;
795     popup_menu_outside_region->popup(event->button, event->time);
796     }
797     } else {
798     if (is_in_resize_zone(event->x, event->y)) {
799 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
800 schoenebeck 1225 get_window()->pointer_grab(false,
801     Gdk::BUTTON_RELEASE_MASK |
802     Gdk::POINTER_MOTION_MASK |
803     Gdk::POINTER_MOTION_HINT_MASK,
804 persson 2169 Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW),
805     event->time);
806     #else
807 schoenebeck 3364 # if GTKMM_MAJOR_VERSION < 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION < 20)
808 persson 2169 Glib::wrap(event->device, true)->grab(get_window(),
809     Gdk::OWNERSHIP_NONE,
810     false,
811     Gdk::BUTTON_RELEASE_MASK |
812     Gdk::POINTER_MOTION_MASK |
813     Gdk::POINTER_MOTION_HINT_MASK,
814     Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW),
815     event->time);
816 schoenebeck 3364 # else
817     gdk_device_grab(
818     Glib::wrap(event->device, true)->gobj(),
819     get_window()->gobj(),
820     GDK_OWNERSHIP_NONE,
821     false,
822     GdkEventMask(GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK |
823     GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON1_MOTION_MASK),
824     Gdk::Cursor::create(
825     Glib::wrap(event->device, true)->get_seat()->get_display(),
826     Gdk::SB_H_DOUBLE_ARROW
827     )->gobj(),
828     event->time
829     );
830     # endif
831 persson 2169 #endif
832 schoenebeck 1225 resize.active = true;
833     } else {
834     gig::Region* r = get_region(k);
835     if (r) {
836     region = r;
837     queue_draw();
838 persson 1261 region_selected();
839 persson 1677 dimensionManager.set_region(region);
840 persson 1262
841 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
842 persson 1262 get_window()->pointer_grab(false,
843     Gdk::BUTTON_RELEASE_MASK |
844     Gdk::POINTER_MOTION_MASK |
845     Gdk::POINTER_MOTION_HINT_MASK,
846 persson 2169 Gdk::Cursor(Gdk::FLEUR),
847     event->time);
848     #else
849 schoenebeck 3364 # if GTKMM_MAJOR_VERSION < 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION < 20)
850     Glib::wrap(event->device, true)->grab(get_window(),
851     Gdk::OWNERSHIP_NONE,
852     false,
853     Gdk::BUTTON_RELEASE_MASK |
854     Gdk::POINTER_MOTION_MASK |
855     Gdk::POINTER_MOTION_HINT_MASK,
856     Gdk::Cursor::create(Gdk::FLEUR),
857     event->time);
858     # else
859     gdk_device_grab(
860     Glib::wrap(event->device, true)->gobj(),
861     get_window()->gobj(),
862     GDK_OWNERSHIP_NONE,
863     false,
864     GdkEventMask(GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK |
865     GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON1_MOTION_MASK),
866     Gdk::Cursor::create(
867     Glib::wrap(event->device, true)->get_seat()->get_display(),
868     Gdk::FLEUR
869     )->gobj(),
870     event->time
871     );
872     # endif
873 persson 2169 #endif
874 persson 1262 move.active = true;
875 persson 2246 move.offset = event->x - key_to_x(region->KeyRange.low, w);
876 schoenebeck 1225 }
877     }
878     }
879     return true;
880     }
881    
882     gig::Region* RegionChooser::get_region(int key)
883     {
884 persson 2246 for (gig::Region* r = regions.first() ; r ; r = regions.next()) {
885 schoenebeck 1225 if (key < r->KeyRange.low) return 0;
886 persson 2246 if (key <= r->KeyRange.high) return r;
887 schoenebeck 1225 }
888     return 0;
889     }
890    
891 schoenebeck 2695 void RegionChooser::set_region(gig::Region* region) {
892     this->region = region;
893     queue_draw();
894     region_selected();
895     dimensionManager.set_region(region);
896     }
897    
898 schoenebeck 3123 void RegionChooser::select_next_region() {
899     if (!instrument) return;
900     if (!region) {
901     for (int i = 0; i < 128; ++i) {
902     ::gig::Region* rgn = instrument->GetRegion(i);
903     if (rgn) {
904     set_region(rgn);
905     return;
906     }
907     }
908     } else {
909     bool currentFound = false;
910     for (int i = 0; i < 128; ++i) {
911     ::gig::Region* rgn = instrument->GetRegion(i);
912     if (!rgn) continue;
913     if (currentFound) {
914     if (rgn != region) {
915     set_region(rgn);
916     return;
917     }
918     } else {
919     if (rgn == region) currentFound = true;
920     }
921     }
922     }
923     }
924    
925     void RegionChooser::select_prev_region() {
926     if (!instrument) return;
927     if (!region) {
928     for (int i = 0; i < 128; ++i) {
929     ::gig::Region* rgn = instrument->GetRegion(i);
930     if (rgn) {
931     set_region(rgn);
932     return;
933     }
934     }
935     } else {
936     bool currentFound = false;
937     for (int i = 127; i >= 0; --i) {
938     ::gig::Region* rgn = instrument->GetRegion(i);
939     if (!rgn) continue;
940     if (currentFound) {
941     if (rgn != region) {
942     set_region(rgn);
943     return;
944     }
945     } else {
946     if (rgn == region) currentFound = true;
947     }
948     }
949     }
950     }
951    
952 persson 1262 void RegionChooser::motion_resize_region(int x, int y)
953 schoenebeck 1225 {
954 persson 1623 const int w = get_width() - 1;
955 schoenebeck 1225
956 persson 1262 int k = int(double(x) / w * 128.0 + 0.5);
957 schoenebeck 1225
958 persson 1262 if (k < resize.min) k = resize.min;
959     else if (k > resize.max) k = resize.max;
960    
961     if (k != resize.pos) {
962     if (resize.mode == resize.undecided) {
963     if (k < resize.pos) {
964     // edit high limit of prev_region
965     resize.max = resize.region->KeyRange.low;
966     resize.region = resize.prev_region;
967     resize.mode = resize.moving_high_limit;
968     } else {
969     // edit low limit of region
970     resize.min = resize.prev_region->KeyRange.high + 1;
971     resize.mode = resize.moving_low_limit;
972 schoenebeck 1225 }
973 persson 1262 }
974 persson 2246 resize.pos = k;
975 persson 2151
976 persson 2246 int x1, x2;
977 persson 1262 if (resize.mode == resize.moving_high_limit) {
978 persson 2246 if (resize.region->KeyRange.high < resize.pos - 1) {
979     x1 = resize.region->KeyRange.high;
980     x2 = resize.pos - 1;
981 persson 1262 } else {
982 persson 2246 x1 = resize.pos - 1;
983     x2 = resize.region->KeyRange.high;
984 schoenebeck 1225 }
985 persson 1262 } else {
986 persson 2246 if (resize.region->KeyRange.low < resize.pos) {
987     x1 = resize.region->KeyRange.low;
988     x2 = resize.pos;
989 persson 1262 } else {
990 persson 2246 x1 = resize.pos;
991     x2 = resize.region->KeyRange.low;
992 persson 1262 }
993     }
994 persson 2246 x1 = key_to_x(x1, w);
995     x2 = key_to_x(x2 + 1, w) + 1;
996     Gdk::Rectangle rect(x1, 0, x2 - x1, h1);
997    
998     update_after_resize();
999    
1000 schoenebeck 3106 //get_window()->invalidate_rect(rect, false);
1001     get_window()->invalidate(false); // repaint entire region, otherwise it would create visual artifacts
1002 persson 1262 }
1003     }
1004 schoenebeck 1225
1005 persson 1262 void RegionChooser::motion_move_region(int x, int y)
1006     {
1007 persson 1623 const int w = get_width() - 1;
1008 persson 1262
1009 persson 2246 int l = int(double(x - move.offset) / w * 128.0 + 0.5);
1010    
1011     if (l == region->KeyRange.low) return;
1012     int new_l;
1013     int regionsize = region->KeyRange.high - region->KeyRange.low;
1014 persson 1262 int a = 0;
1015 persson 2246 if (l > region->KeyRange.low) {
1016 persson 1623 for (gig::Region* r = regions.first() ; ; r = regions.next()) {
1017 persson 1262 if (r != region) {
1018     int b = r ? r->KeyRange.low : 128;
1019    
1020     // gap: from a to b (not inclusive b)
1021    
1022 persson 2246 if (region->KeyRange.high >= b) {
1023 persson 1262 // not found the current gap yet, just continue
1024 schoenebeck 1225 } else {
1025 persson 1262
1026 persson 2246 if (a > l) {
1027 persson 1262 // this gap is too far to the right, break
1028     break;
1029     }
1030    
1031 persson 2246 int newhigh = std::min(l + regionsize, b - 1);
1032     int newlo = newhigh - regionsize;
1033 persson 1262
1034     if (newlo >= a) {
1035     // yes it fits - it's a candidate
1036 persson 2246 new_l = newlo;
1037 persson 1262 }
1038 schoenebeck 1225 }
1039 persson 1262 if (!r) break;
1040     a = r->KeyRange.high + 1;
1041     }
1042     }
1043     } else {
1044 persson 1623 for (gig::Region* r = regions.first() ; ; r = regions.next()) {
1045 persson 1262 if (r != region) {
1046     int b = r ? r->KeyRange.low : 128;
1047    
1048     // gap from a to b (not inclusive b)
1049    
1050 persson 2246 if (l + regionsize >= b) {
1051 persson 1262 // not found the current gap yet, just continue
1052 schoenebeck 1225 } else {
1053 persson 1262
1054 persson 2246 if (a > region->KeyRange.low) {
1055 persson 1262 // this gap is too far to the right, break
1056     break;
1057     }
1058    
1059 persson 2246 int newlo = std::max(l, a);
1060     int newhigh = newlo + regionsize;
1061 persson 1262
1062     if (newhigh < b) {
1063     // yes it fits - break as the first one is the best
1064 persson 2246 new_l = newlo;
1065 persson 1262 break;
1066     }
1067 schoenebeck 1225 }
1068 persson 1262 if (!r) break;
1069     a = r->KeyRange.high + 1;
1070 schoenebeck 1225 }
1071     }
1072 persson 1262 }
1073 persson 2246 if (new_l == region->KeyRange.low) return;
1074 persson 1262
1075 persson 2246 int x1 = key_to_x(std::min(int(region->KeyRange.low), new_l), w);
1076     int x2 = key_to_x(std::max(int(region->KeyRange.high),
1077     new_l + regionsize) + 1, w) + 1;
1078 persson 2169
1079 persson 2246 Gdk::Rectangle rect(x1, 0, x2 - x1, h1);
1080     update_after_move(new_l);
1081 persson 1262
1082 persson 2246 get_window()->invalidate_rect(rect, false);
1083 persson 1262 }
1084    
1085    
1086     bool RegionChooser::on_motion_notify_event(GdkEventMotion* event)
1087     {
1088     Glib::RefPtr<Gdk::Window> window = get_window();
1089     int x, y;
1090 schoenebeck 3364 #if HAS_GDKMM_SEAT
1091     x = event->x;
1092     y = event->y;
1093     Gdk::ModifierType state = Gdk::ModifierType(event->state);
1094     #else
1095 persson 1262 Gdk::ModifierType state = Gdk::ModifierType(0);
1096     window->get_pointer(x, y, state);
1097 schoenebeck 3364 #endif
1098 persson 1262
1099 schoenebeck 1661 // handle virtual MIDI keyboard
1100     if (m_VirtKeybModeChoice.get_value() != VIRT_KEYBOARD_MODE_CHORD &&
1101     currentActiveKey > 0 &&
1102     event->y >= REGION_BLOCK_HEIGHT &&
1103     event->y < REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT)
1104     {
1105 persson 2246 const int k = x_to_key(event->x, get_width() - 1);
1106 persson 1898 if (k != currentActiveKey) {
1107     int velocity =
1108     (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
1109     int(float(event->y - REGION_BLOCK_HEIGHT) /
1110     float(KEYBOARD_HEIGHT) * 128.0f) + 1;
1111     if (velocity <= 0) velocity = 1;
1112     keyboard_key_released_signal.emit(currentActiveKey, velocity);
1113     currentActiveKey = k;
1114     keyboard_key_hit_signal.emit(k, velocity);
1115     }
1116 schoenebeck 1661 }
1117    
1118 persson 1262 if (resize.active) {
1119     motion_resize_region(x, y);
1120     } else if (move.active) {
1121     motion_move_region(x, y);
1122     } else {
1123 schoenebeck 1225 if (is_in_resize_zone(x, y)) {
1124     if (!cursor_is_resize) {
1125 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
1126 persson 1262 window->set_cursor(Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW));
1127 persson 2169 #else
1128 schoenebeck 3364 window->set_cursor(
1129     # if GTKMM_MAJOR_VERSION < 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION < 20)
1130     Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW)
1131     # else
1132     Gdk::Cursor::create(
1133     Glib::wrap(event->device, true)->get_seat()->get_display(),
1134     Gdk::SB_H_DOUBLE_ARROW
1135     )
1136     # endif
1137     );
1138 persson 2169 #endif
1139 schoenebeck 1225 cursor_is_resize = true;
1140     }
1141     } else if (cursor_is_resize) {
1142     window->set_cursor();
1143     cursor_is_resize = false;
1144     }
1145     }
1146    
1147     return true;
1148     }
1149    
1150     bool RegionChooser::is_in_resize_zone(double x, double y) {
1151 persson 1623 const int w = get_width() - 1;
1152 schoenebeck 1225
1153     if (instrument && y >= 0 && y <= h1) {
1154     gig::Region* prev_region = 0;
1155     gig::Region* next_region;
1156 persson 1623 for (gig::Region* r = regions.first(); r ; r = next_region) {
1157     next_region = regions.next();
1158 schoenebeck 1225
1159 persson 2246 int lo = key_to_x(r->KeyRange.low, w);
1160 schoenebeck 1225 if (x <= lo - 2) break;
1161     if (x < lo + 2) {
1162     resize.region = r;
1163     resize.pos = r->KeyRange.low;
1164     resize.max = r->KeyRange.high;
1165    
1166     if (prev_region && prev_region->KeyRange.high + 1 == r->KeyRange.low) {
1167     // we don't know yet if it's the high limit of
1168     // prev_region or the low limit of r that's going
1169     // to be edited
1170     resize.mode = resize.undecided;
1171     resize.min = prev_region->KeyRange.low + 1;
1172     resize.prev_region = prev_region;
1173 persson 1623 return resize.min != resize.max;
1174 schoenebeck 1225 }
1175    
1176     // edit low limit
1177     resize.mode = resize.moving_low_limit;
1178     resize.min = prev_region ? prev_region->KeyRange.high + 1 : 0;
1179 persson 1623 return resize.min != resize.max;
1180 schoenebeck 1225 }
1181     if (!next_region || r->KeyRange.high + 1 != next_region->KeyRange.low) {
1182 persson 2246 int hi = key_to_x(r->KeyRange.high + 1, w);
1183 schoenebeck 1225 if (x <= hi - 2) break;
1184     if (x < hi + 2) {
1185     // edit high limit
1186     resize.region = r;
1187     resize.pos = r->KeyRange.high + 1;
1188     resize.mode = resize.moving_high_limit;
1189     resize.min = r->KeyRange.low + 1;
1190     resize.max = next_region ? next_region->KeyRange.low : 128;
1191 persson 1623 return resize.min != resize.max;
1192 schoenebeck 1225 }
1193     }
1194     prev_region = r;
1195     }
1196     }
1197     return false;
1198     }
1199    
1200 schoenebeck 1339 sigc::signal<void>& RegionChooser::signal_region_selected()
1201 schoenebeck 1225 {
1202 persson 1261 return region_selected;
1203 schoenebeck 1225 }
1204    
1205 schoenebeck 1339 sigc::signal<void>& RegionChooser::signal_instrument_changed()
1206 persson 1261 {
1207     return instrument_changed;
1208     }
1209    
1210 schoenebeck 1225 void RegionChooser::show_region_properties()
1211     {
1212     if (!region) return;
1213 persson 1831 Gtk::Dialog dialog(_("Region Properties"), true /*modal*/);
1214 schoenebeck 1225 // add "Keygroup" checkbox
1215 persson 1831 Gtk::CheckButton checkBoxKeygroup(_("Member of a Keygroup (Exclusive Group)"));
1216 schoenebeck 1225 checkBoxKeygroup.set_active(region->KeyGroup);
1217 schoenebeck 3364 #if USE_GTKMM_BOX
1218     dialog.get_content_area()->pack_start(checkBoxKeygroup);
1219     #else
1220 schoenebeck 1225 dialog.get_vbox()->pack_start(checkBoxKeygroup);
1221 schoenebeck 3364 #endif
1222 schoenebeck 1225 checkBoxKeygroup.show();
1223     // add "Keygroup" spinbox
1224 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
1225     Gtk::Adjustment adjustment(1, 1, 999);
1226 schoenebeck 1225 Gtk::SpinButton spinBox(adjustment);
1227 persson 2169 #else
1228     Gtk::SpinButton spinBox(Gtk::Adjustment::create(1, 1, 999));
1229     #endif
1230 schoenebeck 1225 if (region->KeyGroup) spinBox.set_value(region->KeyGroup);
1231 schoenebeck 3364 #if USE_GTKMM_BOX
1232     dialog.get_content_area()->pack_start(spinBox);
1233     #else
1234 schoenebeck 1225 dialog.get_vbox()->pack_start(spinBox);
1235 schoenebeck 3364 #endif
1236 schoenebeck 1225 spinBox.show();
1237     // add OK and CANCEL buttons to the dialog
1238 schoenebeck 3364 #if HAS_GTKMM_STOCK
1239 schoenebeck 3158 dialog.add_button(Gtk::Stock::OK, 0);
1240     dialog.add_button(Gtk::Stock::CANCEL, 1);
1241 schoenebeck 3364 #else
1242     dialog.add_button(_("_OK"), 0);
1243     dialog.add_button(_("_Cancel"), 1);
1244     #endif
1245 schoenebeck 3226 dialog.set_position(Gtk::WIN_POS_MOUSE);
1246 schoenebeck 3364 #if HAS_GTKMM_SHOW_ALL_CHILDREN
1247 schoenebeck 1225 dialog.show_all_children();
1248 schoenebeck 3364 #endif
1249 schoenebeck 1225 if (!dialog.run()) { // OK selected ...
1250     region->KeyGroup =
1251     (checkBoxKeygroup.get_active()) ? spinBox.get_value_as_int() : 0;
1252     }
1253     }
1254    
1255     void RegionChooser::add_region()
1256     {
1257 schoenebeck 1322 instrument_struct_to_be_changed_signal.emit(instrument);
1258    
1259 schoenebeck 1225 region = instrument->AddRegion();
1260 schoenebeck 1336 region->SetKeyRange(new_region_pos, new_region_pos);
1261 schoenebeck 1225
1262 schoenebeck 1322 instrument_struct_changed_signal.emit(instrument);
1263 persson 1623 regions.update(instrument);
1264 schoenebeck 1322
1265 schoenebeck 1225 queue_draw();
1266 persson 1261 region_selected();
1267 persson 1677 dimensionManager.set_region(region);
1268 persson 1261 instrument_changed();
1269 schoenebeck 1225 }
1270    
1271     void RegionChooser::delete_region()
1272     {
1273 schoenebeck 1322 instrument_struct_to_be_changed_signal.emit(instrument);
1274 schoenebeck 1225 instrument->DeleteRegion(region);
1275 schoenebeck 1322 instrument_struct_changed_signal.emit(instrument);
1276 persson 1623 regions.update(instrument);
1277 schoenebeck 1322
1278 schoenebeck 1225 region = 0;
1279     queue_draw();
1280 persson 1261 region_selected();
1281 persson 1677 dimensionManager.set_region(region);
1282 persson 1261 instrument_changed();
1283 schoenebeck 1225 }
1284    
1285     void RegionChooser::manage_dimensions()
1286     {
1287     gig::Region* region = get_region();
1288     if (!region) return;
1289     dimensionManager.show(region);
1290     }
1291    
1292     void RegionChooser::on_dimension_manager_changed() {
1293 persson 1261 region_selected();
1294     instrument_changed();
1295 schoenebeck 1225 }
1296 schoenebeck 1322
1297 schoenebeck 1339 sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_to_be_changed() {
1298 schoenebeck 1322 return instrument_struct_to_be_changed_signal;
1299     }
1300    
1301 schoenebeck 1339 sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_changed() {
1302 schoenebeck 1322 return instrument_struct_changed_signal;
1303     }
1304    
1305 schoenebeck 1339 sigc::signal<void, gig::Region*>& RegionChooser::signal_region_to_be_changed() {
1306 schoenebeck 1322 return region_to_be_changed_signal;
1307     }
1308    
1309 schoenebeck 1339 sigc::signal<void, gig::Region*>& RegionChooser::signal_region_changed_signal() {
1310 schoenebeck 1322 return region_changed_signal;
1311     }
1312 schoenebeck 1660
1313     sigc::signal<void, int/*key*/, int/*velocity*/>& RegionChooser::signal_keyboard_key_hit() {
1314     return keyboard_key_hit_signal;
1315     }
1316    
1317     sigc::signal<void, int/*key*/, int/*velocity*/>& RegionChooser::signal_keyboard_key_released() {
1318     return keyboard_key_released_signal;
1319     }

  ViewVC Help
Powered by ViewVC