/[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 3364 - (hide annotations) (download)
Tue Nov 14 18:07:25 2017 UTC (6 years, 5 months ago) by schoenebeck
File size: 45353 byte(s)
* Added experimental support for upcoming GTK(MM)4
  (for now up to GTKMM 3.91.2 while still preserving backward compatibility
  down to GTKMM 2).
* Re-merged r2845 to compile now with and without Gtk "Stock ID" API
  (see also r3158).

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     #if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION > 22)
273     # 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 1225 }
299    
300     RegionChooser::~RegionChooser()
301     {
302     }
303    
304 schoenebeck 3148 void RegionChooser::setModifyAllRegions(bool b) {
305     modifyallregions = b;
306     // redraw required parts
307     queue_draw();
308     }
309    
310 persson 2246 void RegionChooser::invalidate_key(int key) {
311     const int h = KEYBOARD_HEIGHT;
312     const int w = get_width() - 1;
313     int x1 = key_to_x(key - 0.5, w);
314     int x2 = key_to_x(key + 1.5, w);
315    
316     Gdk::Rectangle rect(x1 + 1, h1 + 1, x2 - x1 - 1, h - 2);
317     get_window()->invalidate_rect(rect, false);
318     }
319    
320 schoenebeck 1654 void RegionChooser::on_note_on_event(int key, int velocity) {
321 persson 2246 key_pressed[key] = true;
322     invalidate_key(key);
323 schoenebeck 1661 m_VirtKeybVelocityLabel.set_text(ToString(velocity));
324 schoenebeck 1654 }
325    
326     void RegionChooser::on_note_off_event(int key, int velocity) {
327 persson 2246 key_pressed[key] = false;
328     invalidate_key(key);
329 schoenebeck 1661 m_VirtKeybOffVelocityLabel.set_text(ToString(velocity));
330 schoenebeck 1654 }
331    
332 persson 2246
333 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
334 persson 2246 bool RegionChooser::on_expose_event(GdkEventExpose* e) {
335     double clipx1 = e->area.x;
336     double clipx2 = e->area.x + e->area.width;
337     double clipy1 = e->area.y;
338     double clipy2 = e->area.y + e->area.height;
339    
340     const Cairo::RefPtr<Cairo::Context>& cr =
341     get_window()->create_cairo_context();
342     #if 0
343 persson 2169 }
344     #endif
345 persson 2246 #else
346     bool RegionChooser::on_draw(const Cairo::RefPtr<Cairo::Context>& cr) {
347     double clipx1, clipx2, clipy1, clipy2;
348     cr->get_clip_extents(clipx1, clipy1, clipx2, clipy2);
349     #endif
350 schoenebeck 1225
351 persson 2169 cr->save();
352     cr->set_line_width(1);
353 schoenebeck 1225
354 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
355     const Gdk::Color bg = get_style()->get_bg(Gtk::STATE_NORMAL);
356     #else
357 schoenebeck 3364 #if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION > 22)
358     GdkRGBA gdkBgRGBA;
359     gtk_style_context_get_background_color(get_style_context()->gobj(), &gdkBgRGBA);
360     const Gdk::RGBA bg = Glib::wrap(&gdkBgRGBA, true);
361     # else
362 persson 2169 const Gdk::RGBA bg = get_style_context()->get_background_color();
363 schoenebeck 3364 # endif
364 persson 2169 #endif
365     Gdk::Cairo::set_source_rgba(cr, bg);
366     cr->paint();
367 schoenebeck 1225
368 persson 2246 if (clipy2 > h1) {
369     draw_keyboard(cr, clipx1, clipx2);
370     }
371    
372     if (clipy1 < h1 && instrument) {
373     draw_regions(cr, clipx1, clipx2);
374     }
375    
376     cr->restore();
377    
378     return true;
379     }
380    
381     void RegionChooser::draw_keyboard(const Cairo::RefPtr<Cairo::Context>& cr,
382     int clip_low, int clip_high) {
383     const int h = KEYBOARD_HEIGHT;
384     const int w = get_width() - 1;
385     const int bh = int(h * 0.55);
386    
387 persson 2169 Gdk::Cairo::set_source_rgba(cr, black);
388     cr->rectangle(0.5, h1 + 0.5, w, h - 1);
389     cr->stroke();
390 schoenebeck 1225
391 persson 2246 int x1 = key_to_x(20.5, w);
392 persson 2169 Gdk::Cairo::set_source_rgba(cr, grey1);
393     cr->rectangle(1, h1 + 1, x1 - 1, h - 2);
394     cr->fill();
395 schoenebeck 1225
396 persson 2246 int x2 = key_to_x(109.5, w);
397 persson 2169 Gdk::Cairo::set_source_rgba(cr, white);
398     cr->rectangle(x1 + 1, h1 + 1, x2 - x1 - 1, h - 2);
399     cr->fill();
400 persson 2151
401 persson 2169 Gdk::Cairo::set_source_rgba(cr, grey1);
402     cr->rectangle(x2 + 1, h1 + 1, w - x2 - 1, h - 2);
403     cr->fill();
404 persson 2151
405 persson 2169 Gdk::Cairo::set_source_rgba(cr, black);
406 persson 2246
407     int clipkey1 = std::max(0, x_to_key_right(clip_low - 1, w));
408     int clipkey2 = std::min(x_to_key_right(clip_high - 1, w) + 1, 128);
409    
410     for (int i = clipkey1 ; i < clipkey2 ; i++) {
411 persson 2169 int note = (i + 3) % 12;
412 persson 2246 int x = key_to_x(i, w);
413 persson 2151
414 persson 2169 if (note == 1 || note == 4 || note == 6 ||
415     note == 9 || note == 11) {
416 persson 2246 // black key: short line in the middle, with a rectangle
417     // on top
418     int x2 = key_to_x(i + 0.5, w);
419 persson 2169 cr->move_to(x2 + 0.5, h1 + bh + 0.5);
420     cr->line_to(x2 + 0.5, h1 + h - 1);
421     cr->stroke();
422 persson 2151
423 persson 2246 int x3 = key_to_x(i + 1, w);
424 persson 2169 cr->rectangle(x, h1 + 1, x3 - x + 1, bh);
425     cr->fill();
426     } else if (note == 3 || note == 8) {
427 persson 2246 // C or F: long line to the left
428 persson 2169 cr->move_to(x + 0.5, h1 + 1);
429     cr->line_to(x + 0.5, h1 + h - 1);
430     cr->stroke();
431 persson 2246 }
432 persson 2151
433 persson 2246 if (key_pressed[i]) draw_key(cr, i);
434    
435     if (note == 3) draw_digit(cr, i);
436 persson 2169 }
437 persson 2246 }
438 schoenebeck 1225
439 persson 2151
440 persson 2246 void RegionChooser::draw_regions(const Cairo::RefPtr<Cairo::Context>& cr,
441     int clip_low, int clip_high) {
442     const int w = get_width() - 1;
443    
444     Gdk::Cairo::set_source_rgba(cr, black);
445     gig::Region* next_region;
446     int x3 = -1;
447     for (gig::Region* r = regions.first() ; r ; r = next_region) {
448     next_region = regions.next();
449    
450     if (x3 < 0) {
451     x3 = key_to_x(r->KeyRange.low, w);
452     if (x3 >= clip_high) break;
453     }
454     if (!next_region ||
455     r->KeyRange.high + 1 != next_region->KeyRange.low ||
456     r == region || next_region == region) {
457    
458     int x2 = key_to_x(r->KeyRange.high + 1, w);
459     if (x2 >= clip_low) {
460 persson 2169 cr->move_to(x3, 0.5);
461     cr->line_to(x2 + 0.5, 0.5);
462     cr->line_to(x2 + 0.5, h1 - 0.5);
463     cr->line_to(x3, h1 - 0.5);
464     cr->stroke();
465 persson 2151
466 schoenebeck 3148 if (region == r)
467     Gdk::Cairo::set_source_rgba(cr, blue);
468     else if (modifyallregions)
469 schoenebeck 3286 cr->set_source(blueHatchedSurfacePattern);
470 schoenebeck 3148 else
471     Gdk::Cairo::set_source_rgba(cr, white);
472    
473 persson 2169 cr->rectangle(x3 + 1, 1, x2 - x3 - 1, h1 - 2);
474     cr->fill();
475     Gdk::Cairo::set_source_rgba(cr, black);
476 persson 2151 }
477 persson 2246 x3 = -1;
478 persson 2169 }
479 persson 2246 }
480 persson 2151
481 persson 2246 for (gig::Region* r = regions.first() ; r ; r = regions.next()) {
482     int x = key_to_x(r->KeyRange.low, w);
483 schoenebeck 3106 int x2 = key_to_x(r->KeyRange.high + 1, w);
484 persson 2151
485 schoenebeck 3106 RegionFeatures features = regionFeatures(r);
486    
487     const bool bShowLoopSymbol = features.loops > 0;
488 schoenebeck 3307 const bool bShowSampleRefSymbol = features.sampleRefs < features.validDimRegs;
489 schoenebeck 3106 if (bShowLoopSymbol || bShowSampleRefSymbol) {
490     const int margin = 2;
491     const int wRgn = x2 - x;
492     //printf("x=%d x2=%d wRgn=%d\n", x, x2, wRgn);
493    
494     cr->save();
495     cr->set_line_width(1);
496     cr->rectangle(x, 1, wRgn, h1 - 1);
497     cr->clip();
498     if (bShowSampleRefSymbol) {
499     const int wPic = 8;
500     const int hPic = 8;
501     Gdk::Cairo::set_source_pixbuf(
502     cr, (features.sampleRefs) ? yellowDot : redDot,
503     x + (wRgn-wPic)/2.f,
504     (bShowLoopSymbol) ? margin : (h1-hPic)/2.f
505     );
506     cr->paint();
507     }
508     if (bShowLoopSymbol) {
509     const int wPic = 12;
510     const int hPic = 14;
511     Gdk::Cairo::set_source_pixbuf(
512 schoenebeck 3307 cr, (features.loops == features.validDimRegs) ? blackLoop : grayLoop,
513 schoenebeck 3106 x + (wRgn-wPic)/2.f,
514     (bShowSampleRefSymbol) ? h1 - hPic - margin : (h1-hPic)/2.f
515     );
516     cr->paint();
517     }
518     cr->restore();
519     }
520     }
521    
522     for (gig::Region* r = regions.first() ; r ; r = regions.next()) {
523     int x = key_to_x(r->KeyRange.low, w);
524    
525 persson 2246 if (x < clip_low) continue;
526     if (x >= clip_high) break;
527    
528     cr->move_to(x + 0.5, 1);
529     cr->line_to(x + 0.5, h1 - 1);
530     cr->stroke();
531 schoenebeck 1225 }
532 schoenebeck 2536
533     // if there is no region yet, show the user some hint text that he may
534     // right click on this area to create a new region
535     if (!regions.first()) {
536     Glib::RefPtr<Pango::Context> context = get_pango_context();
537     Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create(context);
538     layout->set_alignment(Pango::ALIGN_CENTER);
539     layout->set_text(Glib::ustring("*** ") + _("Right click here to create a region.") + " ***");
540     layout->set_width(get_width() * Pango::SCALE);
541 schoenebeck 2627 //layout->set_height(get_height() * Pango::SCALE);
542     layout->set_spacing(10);
543 schoenebeck 3131 Gdk::Cairo::set_source_rgba(cr, blue);
544 schoenebeck 2627 // get the text dimensions
545     int text_width, text_height;
546     layout->get_pixel_size(text_width, text_height);
547     cr->move_to(0, (REGION_BLOCK_HEIGHT - text_height) / 2);
548 schoenebeck 2536 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 16) || GTKMM_MAJOR_VERSION < 2
549     pango_cairo_show_layout(cr->cobj(), layout->gobj());
550     #else
551     layout->show_in_cairo_context(cr);
552     #endif
553     }
554 schoenebeck 1225 }
555    
556 schoenebeck 1654 bool RegionChooser::is_black_key(int key) {
557     const int note = (key + 3) % 12;
558     return note == 1 || note == 4 || note == 6 || note == 9 || note == 11;
559     }
560 schoenebeck 1225
561 persson 2246 void RegionChooser::draw_digit(const Cairo::RefPtr<Cairo::Context>& cr,
562     int key) {
563 schoenebeck 1660 const int h = KEYBOARD_HEIGHT;
564 persson 1658 const int w = get_width() - 1;
565 persson 2246 Glib::RefPtr<Pango::Layout> layout =
566     Pango::Layout::create(get_pango_context());
567 persson 1658 char buf[30];
568     sprintf(buf, "<span size=\"8000\">%d</span>", key / 12 - 1);
569     layout->set_markup(buf);
570     Pango::Rectangle rectangle = layout->get_logical_extents();
571     double text_w = double(rectangle.get_width()) / Pango::SCALE;
572     double text_h = double(rectangle.get_height()) / Pango::SCALE;
573     double x = w * (key + 0.75) / 128.0;
574 persson 2169 Gdk::Cairo::set_source_rgba(cr, black);
575 persson 2151 cr->move_to(int(x - text_w / 2 + 1), int(h1 + h - text_h + 0.5));
576     #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 16) || GTKMM_MAJOR_VERSION < 2
577     pango_cairo_show_layout(cr->cobj(), layout->gobj());
578     #else
579     layout->show_in_cairo_context(cr);
580     #endif
581 persson 1658 }
582    
583 persson 2246 void RegionChooser::draw_key(const Cairo::RefPtr<Cairo::Context>& cr,
584     int key) {
585 schoenebeck 1660 const int h = KEYBOARD_HEIGHT;
586 persson 1658 const int w = get_width() - 1;
587 schoenebeck 1225 const int bh = int(h * 0.55);
588    
589 persson 2246 Gdk::Cairo::set_source_rgba(cr, activeKeyColor);
590 schoenebeck 1225
591 persson 2151 int note = (key + 3) % 12;
592 persson 2246 int x = key_to_x(key, w) + 1;
593     int x2 = key_to_x(key + 1.5, w);
594     int x3 = key_to_x(key + 1, w);
595     int x4 = key_to_x(key - 0.5, w);
596 persson 2151 int w1 = x3 - x;
597     switch (note) {
598     case 0: case 5: case 10:
599     cr->rectangle(x, h1 + 1, w1, bh);
600     cr->fill();
601     cr->rectangle(x4 + 1, h1 + bh + 1, x2 - x4 - 1, h - bh - 2);
602     cr->fill();
603     break;
604     case 2: case 7:
605     cr->rectangle(x, h1 + 1, w1, bh);
606     cr->fill();
607     cr->rectangle(x4 + 1, h1 + bh + 1, x3 - x4 - 1, h - bh - 2);
608     cr->fill();
609     break;
610     case 3: case 8:
611     cr->rectangle(x, h1 + 1, w1, bh);
612     cr->fill();
613     cr->rectangle(x, h1 + bh + 1, x2 - x, h - bh - 2);
614     cr->fill();
615     break;
616     default:
617     cr->rectangle(x, h1 + 1, w1, bh - 1);
618     cr->fill();
619     break;
620 schoenebeck 1225 }
621 persson 2246 Gdk::Cairo::set_source_rgba(cr, black);
622 schoenebeck 1225 }
623    
624     void RegionChooser::set_instrument(gig::Instrument* instrument)
625     {
626     this->instrument = instrument;
627 persson 1623 regions.update(instrument);
628     region = regions.first();
629 schoenebeck 1225 queue_draw();
630 persson 1261 region_selected();
631 persson 1677 dimensionManager.set_region(region);
632 schoenebeck 1225 }
633    
634     bool RegionChooser::on_button_release_event(GdkEventButton* event)
635     {
636 persson 2246 const int k = x_to_key(event->x, get_width() - 1);
637 schoenebeck 1660
638 schoenebeck 1661 // handle-note off on virtual keyboard
639 schoenebeck 1660 if (event->type == GDK_BUTTON_RELEASE) {
640 schoenebeck 1661 int velocity = (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
641     int(float(event->y - REGION_BLOCK_HEIGHT) / float(KEYBOARD_HEIGHT) * 128.0f) + 1;
642     if (velocity <= 0) velocity = 1;
643     switch (m_VirtKeybModeChoice.get_value()) {
644     case VIRT_KEYBOARD_MODE_CHORD:
645     if (event->y >= REGION_BLOCK_HEIGHT)
646     keyboard_key_released_signal.emit(k, velocity);
647     break;
648     case VIRT_KEYBOARD_MODE_NORMAL:
649     default:
650     if (currentActiveKey >= 0 && currentActiveKey <= 127) {
651     keyboard_key_released_signal.emit(currentActiveKey, velocity);
652     currentActiveKey = -1;
653     }
654     break;
655 schoenebeck 1660 }
656     }
657    
658 schoenebeck 1225 if (resize.active) {
659 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
660 schoenebeck 1225 get_window()->pointer_ungrab(event->time);
661 persson 2169 #else
662 schoenebeck 3364 # if GTKMM_MAJOR_VERSION < 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION < 20)
663 persson 2169 Glib::wrap(event->device, true)->ungrab(event->time);
664 schoenebeck 3364 # else
665     gdk_device_ungrab(Glib::wrap(event->device, true)->gobj(), event->time);
666     # endif
667 persson 2169 #endif
668 schoenebeck 1225 resize.active = false;
669    
670     if (!is_in_resize_zone(event->x, event->y) && cursor_is_resize) {
671     get_window()->set_cursor();
672     cursor_is_resize = false;
673     }
674 persson 1262 } else if (move.active) {
675 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
676 persson 1262 get_window()->pointer_ungrab(event->time);
677 persson 2169 #else
678 schoenebeck 3364 # if GTKMM_MAJOR_VERSION < 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION < 20)
679 persson 2169 Glib::wrap(event->device, true)->ungrab(event->time);
680 schoenebeck 3364 # else
681     gdk_device_ungrab(Glib::wrap(event->device, true)->gobj(), event->time);
682     # endif
683 persson 2169 #endif
684 persson 1262 move.active = false;
685    
686     if (is_in_resize_zone(event->x, event->y)) {
687 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
688 persson 1262 get_window()->set_cursor(Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW));
689 persson 2169 #else
690 schoenebeck 3364 get_window()->set_cursor(
691     # if GTKMM_MAJOR_VERSION < 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION < 20)
692     Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW)
693     # else
694     Gdk::Cursor::create(
695     Glib::wrap(event->device, true)->get_seat()->get_display(),
696     Gdk::SB_H_DOUBLE_ARROW
697     )
698     # endif
699     );
700 persson 2169 #endif
701 persson 1262 cursor_is_resize = true;
702     }
703 schoenebeck 1225 }
704     return true;
705     }
706    
707 persson 2246 void RegionChooser::update_after_resize()
708     {
709     if (resize.mode == resize.moving_high_limit) {
710     if (resize.region->KeyRange.high != resize.pos - 1) {
711     instrument_struct_to_be_changed_signal.emit(instrument);
712     resize.region->SetKeyRange(resize.region->KeyRange.low,
713     resize.pos - 1);
714     regions.update(instrument);
715     instrument_changed.emit();
716     instrument_struct_changed_signal.emit(instrument);
717     }
718     } else if (resize.mode == resize.moving_low_limit) {
719     if (resize.region->KeyRange.low != resize.pos) {
720     instrument_struct_to_be_changed_signal.emit(instrument);
721     resize.region->SetKeyRange(resize.pos,
722     resize.region->KeyRange.high);
723     regions.update(instrument);
724     instrument_changed.emit();
725     instrument_struct_changed_signal.emit(instrument);
726     }
727     }
728     }
729    
730     void RegionChooser::update_after_move(int pos)
731     {
732     instrument_struct_to_be_changed_signal.emit(instrument);
733 schoenebeck 2773 const int range = region->KeyRange.high - region->KeyRange.low;
734     const int diff = pos - int(region->KeyRange.low);
735     region->SetKeyRange(pos, pos + range);
736     if (Settings::singleton()->moveRootNoteWithRegionMoved) {
737     for (int i = 0; i < 256; ++i) {
738     gig::DimensionRegion* dimrgn = region->pDimensionRegions[i];
739     if (!dimrgn || !dimrgn->pSample || !dimrgn->PitchTrack) continue;
740     dimrgn->UnityNote += diff;
741     }
742     }
743 persson 2246 regions.update(instrument);
744     instrument_changed.emit();
745     instrument_struct_changed_signal.emit(instrument);
746     }
747    
748 schoenebeck 1225 bool RegionChooser::on_button_press_event(GdkEventButton* event)
749     {
750     if (!instrument) return true;
751    
752 persson 2246 const int w = get_width() - 1;
753     const int k = x_to_key(event->x, w);
754 schoenebeck 1225
755 schoenebeck 1660 if (event->type == GDK_BUTTON_PRESS) {
756     if (event->y >= REGION_BLOCK_HEIGHT) {
757     int velocity = (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
758     int(float(event->y - REGION_BLOCK_HEIGHT) / float(KEYBOARD_HEIGHT) * 128.0f) + 1;
759 schoenebeck 1661 currentActiveKey = k;
760 schoenebeck 1660 keyboard_key_hit_signal.emit(k, velocity);
761     }
762     }
763    
764 schoenebeck 2641 // left mouse button double click
765     if (event->type == GDK_2BUTTON_PRESS && event->button == 1) {
766 schoenebeck 2663 if (event->y < REGION_BLOCK_HEIGHT) {
767     // show dimension manager dialog for this region
768     manage_dimensions();
769     }
770 schoenebeck 2641 }
771    
772 schoenebeck 1660 if (event->y >= REGION_BLOCK_HEIGHT) return true;
773 schoenebeck 1225 if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
774     gig::Region* r = get_region(k);
775     if (r) {
776     region = r;
777     queue_draw();
778 persson 1261 region_selected();
779 persson 1677 dimensionManager.set_region(region);
780 schoenebeck 1225 popup_menu_inside_region->popup(event->button, event->time);
781     } else {
782     new_region_pos = k;
783     popup_menu_outside_region->popup(event->button, event->time);
784     }
785     } else {
786     if (is_in_resize_zone(event->x, event->y)) {
787 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
788 schoenebeck 1225 get_window()->pointer_grab(false,
789     Gdk::BUTTON_RELEASE_MASK |
790     Gdk::POINTER_MOTION_MASK |
791     Gdk::POINTER_MOTION_HINT_MASK,
792 persson 2169 Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW),
793     event->time);
794     #else
795 schoenebeck 3364 # if GTKMM_MAJOR_VERSION < 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION < 20)
796 persson 2169 Glib::wrap(event->device, true)->grab(get_window(),
797     Gdk::OWNERSHIP_NONE,
798     false,
799     Gdk::BUTTON_RELEASE_MASK |
800     Gdk::POINTER_MOTION_MASK |
801     Gdk::POINTER_MOTION_HINT_MASK,
802     Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW),
803     event->time);
804 schoenebeck 3364 # else
805     gdk_device_grab(
806     Glib::wrap(event->device, true)->gobj(),
807     get_window()->gobj(),
808     GDK_OWNERSHIP_NONE,
809     false,
810     GdkEventMask(GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK |
811     GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON1_MOTION_MASK),
812     Gdk::Cursor::create(
813     Glib::wrap(event->device, true)->get_seat()->get_display(),
814     Gdk::SB_H_DOUBLE_ARROW
815     )->gobj(),
816     event->time
817     );
818     # endif
819 persson 2169 #endif
820 schoenebeck 1225 resize.active = true;
821     } else {
822     gig::Region* r = get_region(k);
823     if (r) {
824     region = r;
825     queue_draw();
826 persson 1261 region_selected();
827 persson 1677 dimensionManager.set_region(region);
828 persson 1262
829 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
830 persson 1262 get_window()->pointer_grab(false,
831     Gdk::BUTTON_RELEASE_MASK |
832     Gdk::POINTER_MOTION_MASK |
833     Gdk::POINTER_MOTION_HINT_MASK,
834 persson 2169 Gdk::Cursor(Gdk::FLEUR),
835     event->time);
836     #else
837 schoenebeck 3364 # if GTKMM_MAJOR_VERSION < 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION < 20)
838     Glib::wrap(event->device, true)->grab(get_window(),
839     Gdk::OWNERSHIP_NONE,
840     false,
841     Gdk::BUTTON_RELEASE_MASK |
842     Gdk::POINTER_MOTION_MASK |
843     Gdk::POINTER_MOTION_HINT_MASK,
844     Gdk::Cursor::create(Gdk::FLEUR),
845     event->time);
846     # else
847     gdk_device_grab(
848     Glib::wrap(event->device, true)->gobj(),
849     get_window()->gobj(),
850     GDK_OWNERSHIP_NONE,
851     false,
852     GdkEventMask(GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK |
853     GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON1_MOTION_MASK),
854     Gdk::Cursor::create(
855     Glib::wrap(event->device, true)->get_seat()->get_display(),
856     Gdk::FLEUR
857     )->gobj(),
858     event->time
859     );
860     # endif
861 persson 2169 #endif
862 persson 1262 move.active = true;
863 persson 2246 move.offset = event->x - key_to_x(region->KeyRange.low, w);
864 schoenebeck 1225 }
865     }
866     }
867     return true;
868     }
869    
870     gig::Region* RegionChooser::get_region(int key)
871     {
872 persson 2246 for (gig::Region* r = regions.first() ; r ; r = regions.next()) {
873 schoenebeck 1225 if (key < r->KeyRange.low) return 0;
874 persson 2246 if (key <= r->KeyRange.high) return r;
875 schoenebeck 1225 }
876     return 0;
877     }
878    
879 schoenebeck 2695 void RegionChooser::set_region(gig::Region* region) {
880     this->region = region;
881     queue_draw();
882     region_selected();
883     dimensionManager.set_region(region);
884     }
885    
886 schoenebeck 3123 void RegionChooser::select_next_region() {
887     if (!instrument) return;
888     if (!region) {
889     for (int i = 0; i < 128; ++i) {
890     ::gig::Region* rgn = instrument->GetRegion(i);
891     if (rgn) {
892     set_region(rgn);
893     return;
894     }
895     }
896     } else {
897     bool currentFound = false;
898     for (int i = 0; i < 128; ++i) {
899     ::gig::Region* rgn = instrument->GetRegion(i);
900     if (!rgn) continue;
901     if (currentFound) {
902     if (rgn != region) {
903     set_region(rgn);
904     return;
905     }
906     } else {
907     if (rgn == region) currentFound = true;
908     }
909     }
910     }
911     }
912    
913     void RegionChooser::select_prev_region() {
914     if (!instrument) return;
915     if (!region) {
916     for (int i = 0; i < 128; ++i) {
917     ::gig::Region* rgn = instrument->GetRegion(i);
918     if (rgn) {
919     set_region(rgn);
920     return;
921     }
922     }
923     } else {
924     bool currentFound = false;
925     for (int i = 127; i >= 0; --i) {
926     ::gig::Region* rgn = instrument->GetRegion(i);
927     if (!rgn) continue;
928     if (currentFound) {
929     if (rgn != region) {
930     set_region(rgn);
931     return;
932     }
933     } else {
934     if (rgn == region) currentFound = true;
935     }
936     }
937     }
938     }
939    
940 persson 1262 void RegionChooser::motion_resize_region(int x, int y)
941 schoenebeck 1225 {
942 persson 1623 const int w = get_width() - 1;
943 schoenebeck 1225
944 persson 1262 int k = int(double(x) / w * 128.0 + 0.5);
945 schoenebeck 1225
946 persson 1262 if (k < resize.min) k = resize.min;
947     else if (k > resize.max) k = resize.max;
948    
949     if (k != resize.pos) {
950     if (resize.mode == resize.undecided) {
951     if (k < resize.pos) {
952     // edit high limit of prev_region
953     resize.max = resize.region->KeyRange.low;
954     resize.region = resize.prev_region;
955     resize.mode = resize.moving_high_limit;
956     } else {
957     // edit low limit of region
958     resize.min = resize.prev_region->KeyRange.high + 1;
959     resize.mode = resize.moving_low_limit;
960 schoenebeck 1225 }
961 persson 1262 }
962 persson 2246 resize.pos = k;
963 persson 2151
964 persson 2246 int x1, x2;
965 persson 1262 if (resize.mode == resize.moving_high_limit) {
966 persson 2246 if (resize.region->KeyRange.high < resize.pos - 1) {
967     x1 = resize.region->KeyRange.high;
968     x2 = resize.pos - 1;
969 persson 1262 } else {
970 persson 2246 x1 = resize.pos - 1;
971     x2 = resize.region->KeyRange.high;
972 schoenebeck 1225 }
973 persson 1262 } else {
974 persson 2246 if (resize.region->KeyRange.low < resize.pos) {
975     x1 = resize.region->KeyRange.low;
976     x2 = resize.pos;
977 persson 1262 } else {
978 persson 2246 x1 = resize.pos;
979     x2 = resize.region->KeyRange.low;
980 persson 1262 }
981     }
982 persson 2246 x1 = key_to_x(x1, w);
983     x2 = key_to_x(x2 + 1, w) + 1;
984     Gdk::Rectangle rect(x1, 0, x2 - x1, h1);
985    
986     update_after_resize();
987    
988 schoenebeck 3106 //get_window()->invalidate_rect(rect, false);
989     get_window()->invalidate(false); // repaint entire region, otherwise it would create visual artifacts
990 persson 1262 }
991     }
992 schoenebeck 1225
993 persson 1262 void RegionChooser::motion_move_region(int x, int y)
994     {
995 persson 1623 const int w = get_width() - 1;
996 persson 1262
997 persson 2246 int l = int(double(x - move.offset) / w * 128.0 + 0.5);
998    
999     if (l == region->KeyRange.low) return;
1000     int new_l;
1001     int regionsize = region->KeyRange.high - region->KeyRange.low;
1002 persson 1262 int a = 0;
1003 persson 2246 if (l > region->KeyRange.low) {
1004 persson 1623 for (gig::Region* r = regions.first() ; ; r = regions.next()) {
1005 persson 1262 if (r != region) {
1006     int b = r ? r->KeyRange.low : 128;
1007    
1008     // gap: from a to b (not inclusive b)
1009    
1010 persson 2246 if (region->KeyRange.high >= b) {
1011 persson 1262 // not found the current gap yet, just continue
1012 schoenebeck 1225 } else {
1013 persson 1262
1014 persson 2246 if (a > l) {
1015 persson 1262 // this gap is too far to the right, break
1016     break;
1017     }
1018    
1019 persson 2246 int newhigh = std::min(l + regionsize, b - 1);
1020     int newlo = newhigh - regionsize;
1021 persson 1262
1022     if (newlo >= a) {
1023     // yes it fits - it's a candidate
1024 persson 2246 new_l = newlo;
1025 persson 1262 }
1026 schoenebeck 1225 }
1027 persson 1262 if (!r) break;
1028     a = r->KeyRange.high + 1;
1029     }
1030     }
1031     } else {
1032 persson 1623 for (gig::Region* r = regions.first() ; ; r = regions.next()) {
1033 persson 1262 if (r != region) {
1034     int b = r ? r->KeyRange.low : 128;
1035    
1036     // gap from a to b (not inclusive b)
1037    
1038 persson 2246 if (l + regionsize >= b) {
1039 persson 1262 // not found the current gap yet, just continue
1040 schoenebeck 1225 } else {
1041 persson 1262
1042 persson 2246 if (a > region->KeyRange.low) {
1043 persson 1262 // this gap is too far to the right, break
1044     break;
1045     }
1046    
1047 persson 2246 int newlo = std::max(l, a);
1048     int newhigh = newlo + regionsize;
1049 persson 1262
1050     if (newhigh < b) {
1051     // yes it fits - break as the first one is the best
1052 persson 2246 new_l = newlo;
1053 persson 1262 break;
1054     }
1055 schoenebeck 1225 }
1056 persson 1262 if (!r) break;
1057     a = r->KeyRange.high + 1;
1058 schoenebeck 1225 }
1059     }
1060 persson 1262 }
1061 persson 2246 if (new_l == region->KeyRange.low) return;
1062 persson 1262
1063 persson 2246 int x1 = key_to_x(std::min(int(region->KeyRange.low), new_l), w);
1064     int x2 = key_to_x(std::max(int(region->KeyRange.high),
1065     new_l + regionsize) + 1, w) + 1;
1066 persson 2169
1067 persson 2246 Gdk::Rectangle rect(x1, 0, x2 - x1, h1);
1068     update_after_move(new_l);
1069 persson 1262
1070 persson 2246 get_window()->invalidate_rect(rect, false);
1071 persson 1262 }
1072    
1073    
1074     bool RegionChooser::on_motion_notify_event(GdkEventMotion* event)
1075     {
1076     Glib::RefPtr<Gdk::Window> window = get_window();
1077     int x, y;
1078 schoenebeck 3364 #if HAS_GDKMM_SEAT
1079     x = event->x;
1080     y = event->y;
1081     Gdk::ModifierType state = Gdk::ModifierType(event->state);
1082     #else
1083 persson 1262 Gdk::ModifierType state = Gdk::ModifierType(0);
1084     window->get_pointer(x, y, state);
1085 schoenebeck 3364 #endif
1086 persson 1262
1087 schoenebeck 1661 // handle virtual MIDI keyboard
1088     if (m_VirtKeybModeChoice.get_value() != VIRT_KEYBOARD_MODE_CHORD &&
1089     currentActiveKey > 0 &&
1090     event->y >= REGION_BLOCK_HEIGHT &&
1091     event->y < REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT)
1092     {
1093 persson 2246 const int k = x_to_key(event->x, get_width() - 1);
1094 persson 1898 if (k != currentActiveKey) {
1095     int velocity =
1096     (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
1097     int(float(event->y - REGION_BLOCK_HEIGHT) /
1098     float(KEYBOARD_HEIGHT) * 128.0f) + 1;
1099     if (velocity <= 0) velocity = 1;
1100     keyboard_key_released_signal.emit(currentActiveKey, velocity);
1101     currentActiveKey = k;
1102     keyboard_key_hit_signal.emit(k, velocity);
1103     }
1104 schoenebeck 1661 }
1105    
1106 persson 1262 if (resize.active) {
1107     motion_resize_region(x, y);
1108     } else if (move.active) {
1109     motion_move_region(x, y);
1110     } else {
1111 schoenebeck 1225 if (is_in_resize_zone(x, y)) {
1112     if (!cursor_is_resize) {
1113 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
1114 persson 1262 window->set_cursor(Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW));
1115 persson 2169 #else
1116 schoenebeck 3364 window->set_cursor(
1117     # if GTKMM_MAJOR_VERSION < 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION < 20)
1118     Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW)
1119     # else
1120     Gdk::Cursor::create(
1121     Glib::wrap(event->device, true)->get_seat()->get_display(),
1122     Gdk::SB_H_DOUBLE_ARROW
1123     )
1124     # endif
1125     );
1126 persson 2169 #endif
1127 schoenebeck 1225 cursor_is_resize = true;
1128     }
1129     } else if (cursor_is_resize) {
1130     window->set_cursor();
1131     cursor_is_resize = false;
1132     }
1133     }
1134    
1135     return true;
1136     }
1137    
1138     bool RegionChooser::is_in_resize_zone(double x, double y) {
1139 persson 1623 const int w = get_width() - 1;
1140 schoenebeck 1225
1141     if (instrument && y >= 0 && y <= h1) {
1142     gig::Region* prev_region = 0;
1143     gig::Region* next_region;
1144 persson 1623 for (gig::Region* r = regions.first(); r ; r = next_region) {
1145     next_region = regions.next();
1146 schoenebeck 1225
1147 persson 2246 int lo = key_to_x(r->KeyRange.low, w);
1148 schoenebeck 1225 if (x <= lo - 2) break;
1149     if (x < lo + 2) {
1150     resize.region = r;
1151     resize.pos = r->KeyRange.low;
1152     resize.max = r->KeyRange.high;
1153    
1154     if (prev_region && prev_region->KeyRange.high + 1 == r->KeyRange.low) {
1155     // we don't know yet if it's the high limit of
1156     // prev_region or the low limit of r that's going
1157     // to be edited
1158     resize.mode = resize.undecided;
1159     resize.min = prev_region->KeyRange.low + 1;
1160     resize.prev_region = prev_region;
1161 persson 1623 return resize.min != resize.max;
1162 schoenebeck 1225 }
1163    
1164     // edit low limit
1165     resize.mode = resize.moving_low_limit;
1166     resize.min = prev_region ? prev_region->KeyRange.high + 1 : 0;
1167 persson 1623 return resize.min != resize.max;
1168 schoenebeck 1225 }
1169     if (!next_region || r->KeyRange.high + 1 != next_region->KeyRange.low) {
1170 persson 2246 int hi = key_to_x(r->KeyRange.high + 1, w);
1171 schoenebeck 1225 if (x <= hi - 2) break;
1172     if (x < hi + 2) {
1173     // edit high limit
1174     resize.region = r;
1175     resize.pos = r->KeyRange.high + 1;
1176     resize.mode = resize.moving_high_limit;
1177     resize.min = r->KeyRange.low + 1;
1178     resize.max = next_region ? next_region->KeyRange.low : 128;
1179 persson 1623 return resize.min != resize.max;
1180 schoenebeck 1225 }
1181     }
1182     prev_region = r;
1183     }
1184     }
1185     return false;
1186     }
1187    
1188 schoenebeck 1339 sigc::signal<void>& RegionChooser::signal_region_selected()
1189 schoenebeck 1225 {
1190 persson 1261 return region_selected;
1191 schoenebeck 1225 }
1192    
1193 schoenebeck 1339 sigc::signal<void>& RegionChooser::signal_instrument_changed()
1194 persson 1261 {
1195     return instrument_changed;
1196     }
1197    
1198 schoenebeck 1225 void RegionChooser::show_region_properties()
1199     {
1200     if (!region) return;
1201 persson 1831 Gtk::Dialog dialog(_("Region Properties"), true /*modal*/);
1202 schoenebeck 1225 // add "Keygroup" checkbox
1203 persson 1831 Gtk::CheckButton checkBoxKeygroup(_("Member of a Keygroup (Exclusive Group)"));
1204 schoenebeck 1225 checkBoxKeygroup.set_active(region->KeyGroup);
1205 schoenebeck 3364 #if USE_GTKMM_BOX
1206     dialog.get_content_area()->pack_start(checkBoxKeygroup);
1207     #else
1208 schoenebeck 1225 dialog.get_vbox()->pack_start(checkBoxKeygroup);
1209 schoenebeck 3364 #endif
1210 schoenebeck 1225 checkBoxKeygroup.show();
1211     // add "Keygroup" spinbox
1212 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
1213     Gtk::Adjustment adjustment(1, 1, 999);
1214 schoenebeck 1225 Gtk::SpinButton spinBox(adjustment);
1215 persson 2169 #else
1216     Gtk::SpinButton spinBox(Gtk::Adjustment::create(1, 1, 999));
1217     #endif
1218 schoenebeck 1225 if (region->KeyGroup) spinBox.set_value(region->KeyGroup);
1219 schoenebeck 3364 #if USE_GTKMM_BOX
1220     dialog.get_content_area()->pack_start(spinBox);
1221     #else
1222 schoenebeck 1225 dialog.get_vbox()->pack_start(spinBox);
1223 schoenebeck 3364 #endif
1224 schoenebeck 1225 spinBox.show();
1225     // add OK and CANCEL buttons to the dialog
1226 schoenebeck 3364 #if HAS_GTKMM_STOCK
1227 schoenebeck 3158 dialog.add_button(Gtk::Stock::OK, 0);
1228     dialog.add_button(Gtk::Stock::CANCEL, 1);
1229 schoenebeck 3364 #else
1230     dialog.add_button(_("_OK"), 0);
1231     dialog.add_button(_("_Cancel"), 1);
1232     #endif
1233 schoenebeck 3226 dialog.set_position(Gtk::WIN_POS_MOUSE);
1234 schoenebeck 3364 #if HAS_GTKMM_SHOW_ALL_CHILDREN
1235 schoenebeck 1225 dialog.show_all_children();
1236 schoenebeck 3364 #endif
1237 schoenebeck 1225 if (!dialog.run()) { // OK selected ...
1238     region->KeyGroup =
1239     (checkBoxKeygroup.get_active()) ? spinBox.get_value_as_int() : 0;
1240     }
1241     }
1242    
1243     void RegionChooser::add_region()
1244     {
1245 schoenebeck 1322 instrument_struct_to_be_changed_signal.emit(instrument);
1246    
1247 schoenebeck 1225 region = instrument->AddRegion();
1248 schoenebeck 1336 region->SetKeyRange(new_region_pos, new_region_pos);
1249 schoenebeck 1225
1250 schoenebeck 1322 instrument_struct_changed_signal.emit(instrument);
1251 persson 1623 regions.update(instrument);
1252 schoenebeck 1322
1253 schoenebeck 1225 queue_draw();
1254 persson 1261 region_selected();
1255 persson 1677 dimensionManager.set_region(region);
1256 persson 1261 instrument_changed();
1257 schoenebeck 1225 }
1258    
1259     void RegionChooser::delete_region()
1260     {
1261 schoenebeck 1322 instrument_struct_to_be_changed_signal.emit(instrument);
1262 schoenebeck 1225 instrument->DeleteRegion(region);
1263 schoenebeck 1322 instrument_struct_changed_signal.emit(instrument);
1264 persson 1623 regions.update(instrument);
1265 schoenebeck 1322
1266 schoenebeck 1225 region = 0;
1267     queue_draw();
1268 persson 1261 region_selected();
1269 persson 1677 dimensionManager.set_region(region);
1270 persson 1261 instrument_changed();
1271 schoenebeck 1225 }
1272    
1273     void RegionChooser::manage_dimensions()
1274     {
1275     gig::Region* region = get_region();
1276     if (!region) return;
1277     dimensionManager.show(region);
1278     }
1279    
1280     void RegionChooser::on_dimension_manager_changed() {
1281 persson 1261 region_selected();
1282     instrument_changed();
1283 schoenebeck 1225 }
1284 schoenebeck 1322
1285 schoenebeck 1339 sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_to_be_changed() {
1286 schoenebeck 1322 return instrument_struct_to_be_changed_signal;
1287     }
1288    
1289 schoenebeck 1339 sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_changed() {
1290 schoenebeck 1322 return instrument_struct_changed_signal;
1291     }
1292    
1293 schoenebeck 1339 sigc::signal<void, gig::Region*>& RegionChooser::signal_region_to_be_changed() {
1294 schoenebeck 1322 return region_to_be_changed_signal;
1295     }
1296    
1297 schoenebeck 1339 sigc::signal<void, gig::Region*>& RegionChooser::signal_region_changed_signal() {
1298 schoenebeck 1322 return region_changed_signal;
1299     }
1300 schoenebeck 1660
1301     sigc::signal<void, int/*key*/, int/*velocity*/>& RegionChooser::signal_keyboard_key_hit() {
1302     return keyboard_key_hit_signal;
1303     }
1304    
1305     sigc::signal<void, int/*key*/, int/*velocity*/>& RegionChooser::signal_keyboard_key_released() {
1306     return keyboard_key_released_signal;
1307     }

  ViewVC Help
Powered by ViewVC