/[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 3460 - (hide annotations) (download)
Sat Feb 2 07:48:50 2019 UTC (5 years, 1 month ago) by persson
File size: 45340 byte(s)
* Use GDK Seat API if available to grab pointer
* Improve version checks for pangomm
* Fix the instrument list tooltip handling so it doesn't generate GTK
  assertion error messages
* Use English quotation marks in tooltips instead of German
* Use multiple columns in controller value popups, as tall popup menus
  work really badly in some GTK environments

1 schoenebeck 1225 /*
2 persson 3460 * Copyright (C) 2006-2019 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 persson 3460 Glib::wrap(event->device, true)->get_seat()->ungrab();
678 schoenebeck 3364 # 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 persson 3460 Glib::wrap(event->device, true)->get_seat()->ungrab();
694 schoenebeck 3364 # 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 persson 3460 Glib::wrap(event->device, true)->get_seat()->grab(
818     get_window(),
819     Gdk::SeatCapabilities::SEAT_CAPABILITY_ALL_POINTING,
820 schoenebeck 3364 false,
821     Gdk::Cursor::create(
822     Glib::wrap(event->device, true)->get_seat()->get_display(),
823     Gdk::SB_H_DOUBLE_ARROW
824 persson 3460 ),
825     reinterpret_cast<GdkEvent*>(event)
826 schoenebeck 3364 );
827     # endif
828 persson 2169 #endif
829 schoenebeck 1225 resize.active = true;
830     } else {
831     gig::Region* r = get_region(k);
832     if (r) {
833     region = r;
834     queue_draw();
835 persson 1261 region_selected();
836 persson 1677 dimensionManager.set_region(region);
837 persson 1262
838 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
839 persson 1262 get_window()->pointer_grab(false,
840     Gdk::BUTTON_RELEASE_MASK |
841     Gdk::POINTER_MOTION_MASK |
842     Gdk::POINTER_MOTION_HINT_MASK,
843 persson 2169 Gdk::Cursor(Gdk::FLEUR),
844     event->time);
845     #else
846 schoenebeck 3364 # if GTKMM_MAJOR_VERSION < 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION < 20)
847     Glib::wrap(event->device, true)->grab(get_window(),
848     Gdk::OWNERSHIP_NONE,
849     false,
850     Gdk::BUTTON_RELEASE_MASK |
851     Gdk::POINTER_MOTION_MASK |
852     Gdk::POINTER_MOTION_HINT_MASK,
853     Gdk::Cursor::create(Gdk::FLEUR),
854     event->time);
855     # else
856 persson 3460 Glib::wrap(event->device, true)->get_seat()->grab(
857     get_window(),
858     Gdk::SeatCapabilities::SEAT_CAPABILITY_ALL_POINTING,
859 schoenebeck 3364 false,
860     Gdk::Cursor::create(
861     Glib::wrap(event->device, true)->get_seat()->get_display(),
862     Gdk::FLEUR
863 persson 3460 ),
864     reinterpret_cast<GdkEvent*>(event)
865 schoenebeck 3364 );
866     # endif
867 persson 2169 #endif
868 persson 1262 move.active = true;
869 persson 2246 move.offset = event->x - key_to_x(region->KeyRange.low, w);
870 schoenebeck 1225 }
871     }
872     }
873     return true;
874     }
875    
876     gig::Region* RegionChooser::get_region(int key)
877     {
878 persson 2246 for (gig::Region* r = regions.first() ; r ; r = regions.next()) {
879 schoenebeck 1225 if (key < r->KeyRange.low) return 0;
880 persson 2246 if (key <= r->KeyRange.high) return r;
881 schoenebeck 1225 }
882     return 0;
883     }
884    
885 schoenebeck 2695 void RegionChooser::set_region(gig::Region* region) {
886     this->region = region;
887     queue_draw();
888     region_selected();
889     dimensionManager.set_region(region);
890     }
891    
892 schoenebeck 3123 void RegionChooser::select_next_region() {
893     if (!instrument) return;
894     if (!region) {
895     for (int i = 0; i < 128; ++i) {
896     ::gig::Region* rgn = instrument->GetRegion(i);
897     if (rgn) {
898     set_region(rgn);
899     return;
900     }
901     }
902     } else {
903     bool currentFound = false;
904     for (int i = 0; i < 128; ++i) {
905     ::gig::Region* rgn = instrument->GetRegion(i);
906     if (!rgn) continue;
907     if (currentFound) {
908     if (rgn != region) {
909     set_region(rgn);
910     return;
911     }
912     } else {
913     if (rgn == region) currentFound = true;
914     }
915     }
916     }
917     }
918    
919     void RegionChooser::select_prev_region() {
920     if (!instrument) return;
921     if (!region) {
922     for (int i = 0; i < 128; ++i) {
923     ::gig::Region* rgn = instrument->GetRegion(i);
924     if (rgn) {
925     set_region(rgn);
926     return;
927     }
928     }
929     } else {
930     bool currentFound = false;
931     for (int i = 127; i >= 0; --i) {
932     ::gig::Region* rgn = instrument->GetRegion(i);
933     if (!rgn) continue;
934     if (currentFound) {
935     if (rgn != region) {
936     set_region(rgn);
937     return;
938     }
939     } else {
940     if (rgn == region) currentFound = true;
941     }
942     }
943     }
944     }
945    
946 persson 1262 void RegionChooser::motion_resize_region(int x, int y)
947 schoenebeck 1225 {
948 persson 1623 const int w = get_width() - 1;
949 schoenebeck 1225
950 persson 1262 int k = int(double(x) / w * 128.0 + 0.5);
951 schoenebeck 1225
952 persson 1262 if (k < resize.min) k = resize.min;
953     else if (k > resize.max) k = resize.max;
954    
955     if (k != resize.pos) {
956     if (resize.mode == resize.undecided) {
957     if (k < resize.pos) {
958     // edit high limit of prev_region
959     resize.max = resize.region->KeyRange.low;
960     resize.region = resize.prev_region;
961     resize.mode = resize.moving_high_limit;
962     } else {
963     // edit low limit of region
964     resize.min = resize.prev_region->KeyRange.high + 1;
965     resize.mode = resize.moving_low_limit;
966 schoenebeck 1225 }
967 persson 1262 }
968 persson 2246 resize.pos = k;
969 persson 2151
970 persson 2246 int x1, x2;
971 persson 1262 if (resize.mode == resize.moving_high_limit) {
972 persson 2246 if (resize.region->KeyRange.high < resize.pos - 1) {
973     x1 = resize.region->KeyRange.high;
974     x2 = resize.pos - 1;
975 persson 1262 } else {
976 persson 2246 x1 = resize.pos - 1;
977     x2 = resize.region->KeyRange.high;
978 schoenebeck 1225 }
979 persson 1262 } else {
980 persson 2246 if (resize.region->KeyRange.low < resize.pos) {
981     x1 = resize.region->KeyRange.low;
982     x2 = resize.pos;
983 persson 1262 } else {
984 persson 2246 x1 = resize.pos;
985     x2 = resize.region->KeyRange.low;
986 persson 1262 }
987     }
988 persson 2246 x1 = key_to_x(x1, w);
989     x2 = key_to_x(x2 + 1, w) + 1;
990     Gdk::Rectangle rect(x1, 0, x2 - x1, h1);
991    
992     update_after_resize();
993    
994 schoenebeck 3106 //get_window()->invalidate_rect(rect, false);
995     get_window()->invalidate(false); // repaint entire region, otherwise it would create visual artifacts
996 persson 1262 }
997     }
998 schoenebeck 1225
999 persson 1262 void RegionChooser::motion_move_region(int x, int y)
1000     {
1001 persson 1623 const int w = get_width() - 1;
1002 persson 1262
1003 persson 2246 int l = int(double(x - move.offset) / w * 128.0 + 0.5);
1004    
1005     if (l == region->KeyRange.low) return;
1006     int new_l;
1007     int regionsize = region->KeyRange.high - region->KeyRange.low;
1008 persson 1262 int a = 0;
1009 persson 2246 if (l > region->KeyRange.low) {
1010 persson 1623 for (gig::Region* r = regions.first() ; ; r = regions.next()) {
1011 persson 1262 if (r != region) {
1012     int b = r ? r->KeyRange.low : 128;
1013    
1014     // gap: from a to b (not inclusive b)
1015    
1016 persson 2246 if (region->KeyRange.high >= b) {
1017 persson 1262 // not found the current gap yet, just continue
1018 schoenebeck 1225 } else {
1019 persson 1262
1020 persson 2246 if (a > l) {
1021 persson 1262 // this gap is too far to the right, break
1022     break;
1023     }
1024    
1025 persson 2246 int newhigh = std::min(l + regionsize, b - 1);
1026     int newlo = newhigh - regionsize;
1027 persson 1262
1028     if (newlo >= a) {
1029     // yes it fits - it's a candidate
1030 persson 2246 new_l = newlo;
1031 persson 1262 }
1032 schoenebeck 1225 }
1033 persson 1262 if (!r) break;
1034     a = r->KeyRange.high + 1;
1035     }
1036     }
1037     } else {
1038 persson 1623 for (gig::Region* r = regions.first() ; ; r = regions.next()) {
1039 persson 1262 if (r != region) {
1040     int b = r ? r->KeyRange.low : 128;
1041    
1042     // gap from a to b (not inclusive b)
1043    
1044 persson 2246 if (l + regionsize >= b) {
1045 persson 1262 // not found the current gap yet, just continue
1046 schoenebeck 1225 } else {
1047 persson 1262
1048 persson 2246 if (a > region->KeyRange.low) {
1049 persson 1262 // this gap is too far to the right, break
1050     break;
1051     }
1052    
1053 persson 2246 int newlo = std::max(l, a);
1054     int newhigh = newlo + regionsize;
1055 persson 1262
1056     if (newhigh < b) {
1057     // yes it fits - break as the first one is the best
1058 persson 2246 new_l = newlo;
1059 persson 1262 break;
1060     }
1061 schoenebeck 1225 }
1062 persson 1262 if (!r) break;
1063     a = r->KeyRange.high + 1;
1064 schoenebeck 1225 }
1065     }
1066 persson 1262 }
1067 persson 2246 if (new_l == region->KeyRange.low) return;
1068 persson 1262
1069 persson 2246 int x1 = key_to_x(std::min(int(region->KeyRange.low), new_l), w);
1070     int x2 = key_to_x(std::max(int(region->KeyRange.high),
1071     new_l + regionsize) + 1, w) + 1;
1072 persson 2169
1073 persson 2246 Gdk::Rectangle rect(x1, 0, x2 - x1, h1);
1074     update_after_move(new_l);
1075 persson 1262
1076 persson 2246 get_window()->invalidate_rect(rect, false);
1077 persson 1262 }
1078    
1079    
1080     bool RegionChooser::on_motion_notify_event(GdkEventMotion* event)
1081     {
1082     Glib::RefPtr<Gdk::Window> window = get_window();
1083     int x, y;
1084 schoenebeck 3364 #if HAS_GDKMM_SEAT
1085     x = event->x;
1086     y = event->y;
1087     Gdk::ModifierType state = Gdk::ModifierType(event->state);
1088     #else
1089 persson 1262 Gdk::ModifierType state = Gdk::ModifierType(0);
1090     window->get_pointer(x, y, state);
1091 schoenebeck 3364 #endif
1092 persson 1262
1093 schoenebeck 1661 // handle virtual MIDI keyboard
1094     if (m_VirtKeybModeChoice.get_value() != VIRT_KEYBOARD_MODE_CHORD &&
1095     currentActiveKey > 0 &&
1096     event->y >= REGION_BLOCK_HEIGHT &&
1097     event->y < REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT)
1098     {
1099 persson 2246 const int k = x_to_key(event->x, get_width() - 1);
1100 persson 1898 if (k != currentActiveKey) {
1101     int velocity =
1102     (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
1103     int(float(event->y - REGION_BLOCK_HEIGHT) /
1104     float(KEYBOARD_HEIGHT) * 128.0f) + 1;
1105     if (velocity <= 0) velocity = 1;
1106     keyboard_key_released_signal.emit(currentActiveKey, velocity);
1107     currentActiveKey = k;
1108     keyboard_key_hit_signal.emit(k, velocity);
1109     }
1110 schoenebeck 1661 }
1111    
1112 persson 1262 if (resize.active) {
1113     motion_resize_region(x, y);
1114     } else if (move.active) {
1115     motion_move_region(x, y);
1116     } else {
1117 schoenebeck 1225 if (is_in_resize_zone(x, y)) {
1118     if (!cursor_is_resize) {
1119 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
1120 persson 1262 window->set_cursor(Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW));
1121 persson 2169 #else
1122 schoenebeck 3364 window->set_cursor(
1123     # if GTKMM_MAJOR_VERSION < 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION < 20)
1124     Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW)
1125     # else
1126     Gdk::Cursor::create(
1127     Glib::wrap(event->device, true)->get_seat()->get_display(),
1128     Gdk::SB_H_DOUBLE_ARROW
1129     )
1130     # endif
1131     );
1132 persson 2169 #endif
1133 schoenebeck 1225 cursor_is_resize = true;
1134     }
1135     } else if (cursor_is_resize) {
1136     window->set_cursor();
1137     cursor_is_resize = false;
1138     }
1139     }
1140    
1141     return true;
1142     }
1143    
1144     bool RegionChooser::is_in_resize_zone(double x, double y) {
1145 persson 1623 const int w = get_width() - 1;
1146 schoenebeck 1225
1147     if (instrument && y >= 0 && y <= h1) {
1148     gig::Region* prev_region = 0;
1149     gig::Region* next_region;
1150 persson 1623 for (gig::Region* r = regions.first(); r ; r = next_region) {
1151     next_region = regions.next();
1152 schoenebeck 1225
1153 persson 2246 int lo = key_to_x(r->KeyRange.low, w);
1154 schoenebeck 1225 if (x <= lo - 2) break;
1155     if (x < lo + 2) {
1156     resize.region = r;
1157     resize.pos = r->KeyRange.low;
1158     resize.max = r->KeyRange.high;
1159    
1160     if (prev_region && prev_region->KeyRange.high + 1 == r->KeyRange.low) {
1161     // we don't know yet if it's the high limit of
1162     // prev_region or the low limit of r that's going
1163     // to be edited
1164     resize.mode = resize.undecided;
1165     resize.min = prev_region->KeyRange.low + 1;
1166     resize.prev_region = prev_region;
1167 persson 1623 return resize.min != resize.max;
1168 schoenebeck 1225 }
1169    
1170     // edit low limit
1171     resize.mode = resize.moving_low_limit;
1172     resize.min = prev_region ? prev_region->KeyRange.high + 1 : 0;
1173 persson 1623 return resize.min != resize.max;
1174 schoenebeck 1225 }
1175     if (!next_region || r->KeyRange.high + 1 != next_region->KeyRange.low) {
1176 persson 2246 int hi = key_to_x(r->KeyRange.high + 1, w);
1177 schoenebeck 1225 if (x <= hi - 2) break;
1178     if (x < hi + 2) {
1179     // edit high limit
1180     resize.region = r;
1181     resize.pos = r->KeyRange.high + 1;
1182     resize.mode = resize.moving_high_limit;
1183     resize.min = r->KeyRange.low + 1;
1184     resize.max = next_region ? next_region->KeyRange.low : 128;
1185 persson 1623 return resize.min != resize.max;
1186 schoenebeck 1225 }
1187     }
1188     prev_region = r;
1189     }
1190     }
1191     return false;
1192     }
1193    
1194 schoenebeck 1339 sigc::signal<void>& RegionChooser::signal_region_selected()
1195 schoenebeck 1225 {
1196 persson 1261 return region_selected;
1197 schoenebeck 1225 }
1198    
1199 schoenebeck 1339 sigc::signal<void>& RegionChooser::signal_instrument_changed()
1200 persson 1261 {
1201     return instrument_changed;
1202     }
1203    
1204 schoenebeck 1225 void RegionChooser::show_region_properties()
1205     {
1206     if (!region) return;
1207 persson 1831 Gtk::Dialog dialog(_("Region Properties"), true /*modal*/);
1208 schoenebeck 1225 // add "Keygroup" checkbox
1209 persson 1831 Gtk::CheckButton checkBoxKeygroup(_("Member of a Keygroup (Exclusive Group)"));
1210 schoenebeck 1225 checkBoxKeygroup.set_active(region->KeyGroup);
1211 schoenebeck 3364 #if USE_GTKMM_BOX
1212     dialog.get_content_area()->pack_start(checkBoxKeygroup);
1213     #else
1214 schoenebeck 1225 dialog.get_vbox()->pack_start(checkBoxKeygroup);
1215 schoenebeck 3364 #endif
1216 schoenebeck 1225 checkBoxKeygroup.show();
1217     // add "Keygroup" spinbox
1218 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
1219     Gtk::Adjustment adjustment(1, 1, 999);
1220 schoenebeck 1225 Gtk::SpinButton spinBox(adjustment);
1221 persson 2169 #else
1222     Gtk::SpinButton spinBox(Gtk::Adjustment::create(1, 1, 999));
1223     #endif
1224 schoenebeck 1225 if (region->KeyGroup) spinBox.set_value(region->KeyGroup);
1225 schoenebeck 3364 #if USE_GTKMM_BOX
1226     dialog.get_content_area()->pack_start(spinBox);
1227     #else
1228 schoenebeck 1225 dialog.get_vbox()->pack_start(spinBox);
1229 schoenebeck 3364 #endif
1230 schoenebeck 1225 spinBox.show();
1231     // add OK and CANCEL buttons to the dialog
1232 schoenebeck 3364 #if HAS_GTKMM_STOCK
1233 schoenebeck 3158 dialog.add_button(Gtk::Stock::OK, 0);
1234     dialog.add_button(Gtk::Stock::CANCEL, 1);
1235 schoenebeck 3364 #else
1236     dialog.add_button(_("_OK"), 0);
1237     dialog.add_button(_("_Cancel"), 1);
1238     #endif
1239 schoenebeck 3226 dialog.set_position(Gtk::WIN_POS_MOUSE);
1240 schoenebeck 3364 #if HAS_GTKMM_SHOW_ALL_CHILDREN
1241 schoenebeck 1225 dialog.show_all_children();
1242 schoenebeck 3364 #endif
1243 schoenebeck 1225 if (!dialog.run()) { // OK selected ...
1244     region->KeyGroup =
1245     (checkBoxKeygroup.get_active()) ? spinBox.get_value_as_int() : 0;
1246     }
1247     }
1248    
1249     void RegionChooser::add_region()
1250     {
1251 schoenebeck 1322 instrument_struct_to_be_changed_signal.emit(instrument);
1252    
1253 schoenebeck 1225 region = instrument->AddRegion();
1254 schoenebeck 1336 region->SetKeyRange(new_region_pos, new_region_pos);
1255 schoenebeck 1225
1256 schoenebeck 1322 instrument_struct_changed_signal.emit(instrument);
1257 persson 1623 regions.update(instrument);
1258 schoenebeck 1322
1259 schoenebeck 1225 queue_draw();
1260 persson 1261 region_selected();
1261 persson 1677 dimensionManager.set_region(region);
1262 persson 1261 instrument_changed();
1263 schoenebeck 1225 }
1264    
1265     void RegionChooser::delete_region()
1266     {
1267 schoenebeck 1322 instrument_struct_to_be_changed_signal.emit(instrument);
1268 schoenebeck 1225 instrument->DeleteRegion(region);
1269 schoenebeck 1322 instrument_struct_changed_signal.emit(instrument);
1270 persson 1623 regions.update(instrument);
1271 schoenebeck 1322
1272 schoenebeck 1225 region = 0;
1273     queue_draw();
1274 persson 1261 region_selected();
1275 persson 1677 dimensionManager.set_region(region);
1276 persson 1261 instrument_changed();
1277 schoenebeck 1225 }
1278    
1279     void RegionChooser::manage_dimensions()
1280     {
1281     gig::Region* region = get_region();
1282     if (!region) return;
1283     dimensionManager.show(region);
1284     }
1285    
1286     void RegionChooser::on_dimension_manager_changed() {
1287 persson 1261 region_selected();
1288     instrument_changed();
1289 schoenebeck 1225 }
1290 schoenebeck 1322
1291 schoenebeck 1339 sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_to_be_changed() {
1292 schoenebeck 1322 return instrument_struct_to_be_changed_signal;
1293     }
1294    
1295 schoenebeck 1339 sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_changed() {
1296 schoenebeck 1322 return instrument_struct_changed_signal;
1297     }
1298    
1299 schoenebeck 1339 sigc::signal<void, gig::Region*>& RegionChooser::signal_region_to_be_changed() {
1300 schoenebeck 1322 return region_to_be_changed_signal;
1301     }
1302    
1303 schoenebeck 1339 sigc::signal<void, gig::Region*>& RegionChooser::signal_region_changed_signal() {
1304 schoenebeck 1322 return region_changed_signal;
1305     }
1306 schoenebeck 1660
1307     sigc::signal<void, int/*key*/, int/*velocity*/>& RegionChooser::signal_keyboard_key_hit() {
1308     return keyboard_key_hit_signal;
1309     }
1310    
1311     sigc::signal<void, int/*key*/, int/*velocity*/>& RegionChooser::signal_keyboard_key_released() {
1312     return keyboard_key_released_signal;
1313     }

  ViewVC Help
Powered by ViewVC