/[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 3106 - (hide annotations) (download)
Sat Feb 11 17:04:48 2017 UTC (7 years, 2 months ago) by schoenebeck
File size: 36100 byte(s)
* Show graphical symbol on region that uses loop(s).
* Show graphical symbol on region that misses sample
  reference(s).
* Bumped version (1.0.0.svn27).

1 schoenebeck 1225 /*
2 schoenebeck 2695 * Copyright (C) 2006-2015 Andreas Persson
3 schoenebeck 1225 *
4     * This program is free software; you can redistribute it and/or
5     * modify it under the terms of the GNU General Public License as
6     * published by the Free Software Foundation; either version 2, or (at
7     * your option) any later version.
8     *
9     * This program is distributed in the hope that it will be useful, but
10     * WITHOUT ANY WARRANTY; without even the implied warranty of
11     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12     * General Public License for more details.
13     *
14     * You should have received a copy of the GNU General Public License
15     * along with program; see the file COPYING. If not, write to the Free
16     * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
17     * 02110-1301 USA.
18     */
19    
20     #include "regionchooser.h"
21 persson 2151
22 persson 1623 #include <algorithm>
23 persson 2151
24     #include <cairomm/context.h>
25     #include <gdkmm/general.h>
26 schoenebeck 1225 #include <gdkmm/cursor.h>
27 schoenebeck 3106 #include <gdkmm/pixbuf.h>
28 schoenebeck 1225 #include <gtkmm/spinbutton.h>
29     #include <gtkmm/dialog.h>
30    
31 schoenebeck 1396 #include "global.h"
32 schoenebeck 2773 #include "Settings.h"
33 schoenebeck 3106 #include "gfx/builtinpix.h"
34 schoenebeck 1225
35 schoenebeck 2627 #define REGION_BLOCK_HEIGHT 30
36 persson 2246 #define KEYBOARD_HEIGHT 40
37 schoenebeck 1660
38 schoenebeck 3106 struct RegionFeatures {
39     int sampleRefs;
40     int loops;
41    
42     RegionFeatures() {
43     sampleRefs = loops = 0;
44     }
45     };
46    
47     static RegionFeatures regionFeatures(gig::Region* rgn) {
48     RegionFeatures f;
49     for (int i = 0; i < rgn->DimensionRegions; ++i) {
50     gig::DimensionRegion* dr = rgn->pDimensionRegions[i];
51     if (dr->pSample) f.sampleRefs++;
52     // the user doesn't care about loop if there is no valid sample reference
53     if (dr->pSample && dr->SampleLoops) f.loops++;
54     }
55     return f;
56     }
57    
58 persson 1623 void SortedRegions::update(gig::Instrument* instrument) {
59     // Usually, the regions in a gig file are ordered after their key
60     // range, but there are files where they are not. The
61     // RegionChooser code needs a sorted list of regions.
62     regions.clear();
63     if (instrument) {
64 persson 2151 for (gig::Region* r = instrument->GetFirstRegion() ;
65 persson 1623 r ;
66     r = instrument->GetNextRegion()) {
67     regions.push_back(r);
68     }
69     sort(regions.begin(), regions.end(), *this);
70     }
71     }
72    
73     gig::Region* SortedRegions::first() {
74     region_iterator = regions.begin();
75     return region_iterator == regions.end() ? 0 : *region_iterator;
76     }
77    
78     gig::Region* SortedRegions::next() {
79 persson 2841 ++region_iterator;
80 persson 1623 return region_iterator == regions.end() ? 0 : *region_iterator;
81     }
82    
83    
84    
85 schoenebeck 1661 RegionChooser::RegionChooser() :
86 persson 2169 activeKeyColor("red"),
87     red("#8070ff"),
88     grey1("grey69"),
89     white("white"),
90     black("black"),
91 schoenebeck 1661 m_VirtKeybModeChoice(_("Virtual Keyboard Mode")),
92     currentActiveKey(-1)
93 schoenebeck 1225 {
94 persson 2169 set_size_request(500, KEYBOARD_HEIGHT + REGION_BLOCK_HEIGHT);
95 schoenebeck 1225
96 schoenebeck 3106 loadBuiltInPix();
97    
98 schoenebeck 1225 instrument = 0;
99     region = 0;
100     resize.active = false;
101 persson 1303 move.active = false;
102 schoenebeck 1225 cursor_is_resize = false;
103 schoenebeck 1660 h1 = REGION_BLOCK_HEIGHT;
104 schoenebeck 1225
105 persson 1831 // properties of the virtual keyboard
106 schoenebeck 1661 {
107 persson 2246 const char* choices[] = { _("normal"), _("chord"), 0 };
108 schoenebeck 1661 static const virt_keyboard_mode_t values[] = {
109     VIRT_KEYBOARD_MODE_NORMAL,
110     VIRT_KEYBOARD_MODE_CHORD
111     };
112     m_VirtKeybModeChoice.set_choices(choices, values);
113     m_VirtKeybModeChoice.set_value(VIRT_KEYBOARD_MODE_NORMAL);
114     }
115     m_VirtKeybVelocityLabelDescr.set_text(_("Note-On Velocity:"));
116     m_VirtKeybVelocityLabel.set_text("-");
117     m_VirtKeybOffVelocityLabelDescr.set_text(_("Note-Off Velocity:"));
118     m_VirtKeybOffVelocityLabel.set_text("-");
119     m_VirtKeybPropsBox.pack_start(m_VirtKeybModeChoice.label, Gtk::PACK_SHRINK);
120     m_VirtKeybPropsBox.pack_start(m_VirtKeybModeChoice.widget, Gtk::PACK_SHRINK);
121     m_VirtKeybPropsBox.pack_start(m_VirtKeybVelocityLabelDescr, Gtk::PACK_SHRINK);
122     m_VirtKeybPropsBox.pack_start(m_VirtKeybVelocityLabel, Gtk::PACK_SHRINK);
123     m_VirtKeybPropsBox.pack_start(m_VirtKeybOffVelocityLabelDescr, Gtk::PACK_SHRINK);
124     m_VirtKeybPropsBox.pack_start(m_VirtKeybOffVelocityLabel, Gtk::PACK_SHRINK);
125     m_VirtKeybPropsBox.set_spacing(10);
126     m_VirtKeybPropsBox.show();
127 persson 2246 for (int i = 0 ; i < 128 ; i++) key_pressed[i] = false;
128 schoenebeck 1661
129 schoenebeck 1225 actionGroup = Gtk::ActionGroup::create();
130 persson 2845 actionGroup->add(Gtk::Action::create("Properties", _("_Properties")),
131 schoenebeck 1225 sigc::mem_fun(*this,
132     &RegionChooser::show_region_properties));
133 persson 2845 actionGroup->add(Gtk::Action::create("Remove", _("_Remove")),
134 schoenebeck 1225 sigc::mem_fun(*this, &RegionChooser::delete_region));
135 persson 2845 actionGroup->add(Gtk::Action::create("Add", _("_Add")),
136 schoenebeck 1225 sigc::mem_fun(*this, &RegionChooser::add_region));
137     actionGroup->add(Gtk::Action::create("Dimensions", _("Dimensions...")),
138     sigc::mem_fun(*this, &RegionChooser::manage_dimensions));
139    
140     uiManager = Gtk::UIManager::create();
141     uiManager->insert_action_group(actionGroup);
142     Glib::ustring ui_info =
143     "<ui>"
144     " <popup name='PopupMenuInsideRegion'>"
145     " <menuitem action='Properties'/>"
146     " <menuitem action='Dimensions'/>"
147     " <menuitem action='Remove'/>"
148     " </popup>"
149     " <popup name='PopupMenuOutsideRegion'>"
150     " <menuitem action='Add'/>"
151     " </popup>"
152     "</ui>";
153     uiManager->add_ui_from_string(ui_info);
154    
155     popup_menu_inside_region = dynamic_cast<Gtk::Menu*>(
156     uiManager->get_widget("/PopupMenuInsideRegion"));
157     popup_menu_outside_region = dynamic_cast<Gtk::Menu*>(
158     uiManager->get_widget("/PopupMenuOutsideRegion"));
159    
160     add_events(Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK |
161     Gdk::POINTER_MOTION_MASK | Gdk::POINTER_MOTION_HINT_MASK);
162    
163 schoenebeck 1322 dimensionManager.region_to_be_changed_signal.connect(
164     region_to_be_changed_signal.make_slot()
165 schoenebeck 1225 );
166 schoenebeck 1322 dimensionManager.region_changed_signal.connect(
167     region_changed_signal.make_slot()
168     );
169     dimensionManager.region_changed_signal.connect(
170     sigc::hide(
171     sigc::mem_fun(*this, &RegionChooser::on_dimension_manager_changed)
172     )
173     );
174 schoenebeck 1660 keyboard_key_hit_signal.connect(
175     sigc::mem_fun(*this, &RegionChooser::on_note_on_event)
176     );
177     keyboard_key_released_signal.connect(
178     sigc::mem_fun(*this, &RegionChooser::on_note_off_event)
179     );
180 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."));
181 schoenebeck 1225 }
182    
183     RegionChooser::~RegionChooser()
184     {
185     }
186    
187 persson 2246 void RegionChooser::invalidate_key(int key) {
188     const int h = KEYBOARD_HEIGHT;
189     const int w = get_width() - 1;
190     int x1 = key_to_x(key - 0.5, w);
191     int x2 = key_to_x(key + 1.5, w);
192    
193     Gdk::Rectangle rect(x1 + 1, h1 + 1, x2 - x1 - 1, h - 2);
194     get_window()->invalidate_rect(rect, false);
195     }
196    
197 schoenebeck 1654 void RegionChooser::on_note_on_event(int key, int velocity) {
198 persson 2246 key_pressed[key] = true;
199     invalidate_key(key);
200 schoenebeck 1661 m_VirtKeybVelocityLabel.set_text(ToString(velocity));
201 schoenebeck 1654 }
202    
203     void RegionChooser::on_note_off_event(int key, int velocity) {
204 persson 2246 key_pressed[key] = false;
205     invalidate_key(key);
206 schoenebeck 1661 m_VirtKeybOffVelocityLabel.set_text(ToString(velocity));
207 schoenebeck 1654 }
208    
209 persson 2246
210 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
211 persson 2246 bool RegionChooser::on_expose_event(GdkEventExpose* e) {
212     double clipx1 = e->area.x;
213     double clipx2 = e->area.x + e->area.width;
214     double clipy1 = e->area.y;
215     double clipy2 = e->area.y + e->area.height;
216    
217     const Cairo::RefPtr<Cairo::Context>& cr =
218     get_window()->create_cairo_context();
219     #if 0
220 persson 2169 }
221     #endif
222 persson 2246 #else
223     bool RegionChooser::on_draw(const Cairo::RefPtr<Cairo::Context>& cr) {
224     double clipx1, clipx2, clipy1, clipy2;
225     cr->get_clip_extents(clipx1, clipy1, clipx2, clipy2);
226     #endif
227 schoenebeck 1225
228 persson 2169 cr->save();
229     cr->set_line_width(1);
230 schoenebeck 1225
231 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
232     const Gdk::Color bg = get_style()->get_bg(Gtk::STATE_NORMAL);
233     #else
234     const Gdk::RGBA bg = get_style_context()->get_background_color();
235     #endif
236     Gdk::Cairo::set_source_rgba(cr, bg);
237     cr->paint();
238 schoenebeck 1225
239 persson 2246 if (clipy2 > h1) {
240     draw_keyboard(cr, clipx1, clipx2);
241     }
242    
243     if (clipy1 < h1 && instrument) {
244     draw_regions(cr, clipx1, clipx2);
245     }
246    
247     cr->restore();
248    
249     return true;
250     }
251    
252     void RegionChooser::draw_keyboard(const Cairo::RefPtr<Cairo::Context>& cr,
253     int clip_low, int clip_high) {
254     const int h = KEYBOARD_HEIGHT;
255     const int w = get_width() - 1;
256     const int bh = int(h * 0.55);
257    
258 persson 2169 Gdk::Cairo::set_source_rgba(cr, black);
259     cr->rectangle(0.5, h1 + 0.5, w, h - 1);
260     cr->stroke();
261 schoenebeck 1225
262 persson 2246 int x1 = key_to_x(20.5, w);
263 persson 2169 Gdk::Cairo::set_source_rgba(cr, grey1);
264     cr->rectangle(1, h1 + 1, x1 - 1, h - 2);
265     cr->fill();
266 schoenebeck 1225
267 persson 2246 int x2 = key_to_x(109.5, w);
268 persson 2169 Gdk::Cairo::set_source_rgba(cr, white);
269     cr->rectangle(x1 + 1, h1 + 1, x2 - x1 - 1, h - 2);
270     cr->fill();
271 persson 2151
272 persson 2169 Gdk::Cairo::set_source_rgba(cr, grey1);
273     cr->rectangle(x2 + 1, h1 + 1, w - x2 - 1, h - 2);
274     cr->fill();
275 persson 2151
276 persson 2169 Gdk::Cairo::set_source_rgba(cr, black);
277 persson 2246
278     int clipkey1 = std::max(0, x_to_key_right(clip_low - 1, w));
279     int clipkey2 = std::min(x_to_key_right(clip_high - 1, w) + 1, 128);
280    
281     for (int i = clipkey1 ; i < clipkey2 ; i++) {
282 persson 2169 int note = (i + 3) % 12;
283 persson 2246 int x = key_to_x(i, w);
284 persson 2151
285 persson 2169 if (note == 1 || note == 4 || note == 6 ||
286     note == 9 || note == 11) {
287 persson 2246 // black key: short line in the middle, with a rectangle
288     // on top
289     int x2 = key_to_x(i + 0.5, w);
290 persson 2169 cr->move_to(x2 + 0.5, h1 + bh + 0.5);
291     cr->line_to(x2 + 0.5, h1 + h - 1);
292     cr->stroke();
293 persson 2151
294 persson 2246 int x3 = key_to_x(i + 1, w);
295 persson 2169 cr->rectangle(x, h1 + 1, x3 - x + 1, bh);
296     cr->fill();
297     } else if (note == 3 || note == 8) {
298 persson 2246 // C or F: long line to the left
299 persson 2169 cr->move_to(x + 0.5, h1 + 1);
300     cr->line_to(x + 0.5, h1 + h - 1);
301     cr->stroke();
302 persson 2246 }
303 persson 2151
304 persson 2246 if (key_pressed[i]) draw_key(cr, i);
305    
306     if (note == 3) draw_digit(cr, i);
307 persson 2169 }
308 persson 2246 }
309 schoenebeck 1225
310 persson 2151
311 persson 2246 void RegionChooser::draw_regions(const Cairo::RefPtr<Cairo::Context>& cr,
312     int clip_low, int clip_high) {
313     const int w = get_width() - 1;
314    
315     Gdk::Cairo::set_source_rgba(cr, black);
316     gig::Region* next_region;
317     int x3 = -1;
318     for (gig::Region* r = regions.first() ; r ; r = next_region) {
319     next_region = regions.next();
320    
321     if (x3 < 0) {
322     x3 = key_to_x(r->KeyRange.low, w);
323     if (x3 >= clip_high) break;
324     }
325     if (!next_region ||
326     r->KeyRange.high + 1 != next_region->KeyRange.low ||
327     r == region || next_region == region) {
328    
329     int x2 = key_to_x(r->KeyRange.high + 1, w);
330     if (x2 >= clip_low) {
331 persson 2169 cr->move_to(x3, 0.5);
332     cr->line_to(x2 + 0.5, 0.5);
333     cr->line_to(x2 + 0.5, h1 - 0.5);
334     cr->line_to(x3, h1 - 0.5);
335     cr->stroke();
336 persson 2151
337 persson 2246 Gdk::Cairo::set_source_rgba(cr, region == r ? red : white);
338 persson 2169 cr->rectangle(x3 + 1, 1, x2 - x3 - 1, h1 - 2);
339     cr->fill();
340     Gdk::Cairo::set_source_rgba(cr, black);
341 persson 2151 }
342 persson 2246 x3 = -1;
343 persson 2169 }
344 persson 2246 }
345 persson 2151
346 persson 2246 for (gig::Region* r = regions.first() ; r ; r = regions.next()) {
347     int x = key_to_x(r->KeyRange.low, w);
348 schoenebeck 3106 int x2 = key_to_x(r->KeyRange.high + 1, w);
349 persson 2151
350 schoenebeck 3106 RegionFeatures features = regionFeatures(r);
351    
352     const bool bShowLoopSymbol = features.loops > 0;
353     const bool bShowSampleRefSymbol = features.sampleRefs < r->DimensionRegions;
354     if (bShowLoopSymbol || bShowSampleRefSymbol) {
355     const int margin = 2;
356     const int wRgn = x2 - x;
357     //printf("x=%d x2=%d wRgn=%d\n", x, x2, wRgn);
358    
359     cr->save();
360     cr->set_line_width(1);
361     cr->rectangle(x, 1, wRgn, h1 - 1);
362     cr->clip();
363     if (bShowSampleRefSymbol) {
364     const int wPic = 8;
365     const int hPic = 8;
366     Gdk::Cairo::set_source_pixbuf(
367     cr, (features.sampleRefs) ? yellowDot : redDot,
368     x + (wRgn-wPic)/2.f,
369     (bShowLoopSymbol) ? margin : (h1-hPic)/2.f
370     );
371     cr->paint();
372     }
373     if (bShowLoopSymbol) {
374     const int wPic = 12;
375     const int hPic = 14;
376     Gdk::Cairo::set_source_pixbuf(
377     cr, (features.loops == r->DimensionRegions) ? blackLoop : grayLoop,
378     x + (wRgn-wPic)/2.f,
379     (bShowSampleRefSymbol) ? h1 - hPic - margin : (h1-hPic)/2.f
380     );
381     cr->paint();
382     }
383     cr->restore();
384     }
385     }
386    
387     for (gig::Region* r = regions.first() ; r ; r = regions.next()) {
388     int x = key_to_x(r->KeyRange.low, w);
389    
390 persson 2246 if (x < clip_low) continue;
391     if (x >= clip_high) break;
392    
393     cr->move_to(x + 0.5, 1);
394     cr->line_to(x + 0.5, h1 - 1);
395     cr->stroke();
396 schoenebeck 1225 }
397 schoenebeck 2536
398     // if there is no region yet, show the user some hint text that he may
399     // right click on this area to create a new region
400     if (!regions.first()) {
401     Glib::RefPtr<Pango::Context> context = get_pango_context();
402     Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create(context);
403     layout->set_alignment(Pango::ALIGN_CENTER);
404     layout->set_text(Glib::ustring("*** ") + _("Right click here to create a region.") + " ***");
405     layout->set_width(get_width() * Pango::SCALE);
406 schoenebeck 2627 //layout->set_height(get_height() * Pango::SCALE);
407     layout->set_spacing(10);
408     Gdk::Cairo::set_source_rgba(cr, red);
409     // get the text dimensions
410     int text_width, text_height;
411     layout->get_pixel_size(text_width, text_height);
412     cr->move_to(0, (REGION_BLOCK_HEIGHT - text_height) / 2);
413 schoenebeck 2536 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 16) || GTKMM_MAJOR_VERSION < 2
414     pango_cairo_show_layout(cr->cobj(), layout->gobj());
415     #else
416     layout->show_in_cairo_context(cr);
417     #endif
418     }
419 schoenebeck 1225 }
420    
421 schoenebeck 1654 bool RegionChooser::is_black_key(int key) {
422     const int note = (key + 3) % 12;
423     return note == 1 || note == 4 || note == 6 || note == 9 || note == 11;
424     }
425 schoenebeck 1225
426 persson 2246 void RegionChooser::draw_digit(const Cairo::RefPtr<Cairo::Context>& cr,
427     int key) {
428 schoenebeck 1660 const int h = KEYBOARD_HEIGHT;
429 persson 1658 const int w = get_width() - 1;
430 persson 2246 Glib::RefPtr<Pango::Layout> layout =
431     Pango::Layout::create(get_pango_context());
432 persson 1658 char buf[30];
433     sprintf(buf, "<span size=\"8000\">%d</span>", key / 12 - 1);
434     layout->set_markup(buf);
435     Pango::Rectangle rectangle = layout->get_logical_extents();
436     double text_w = double(rectangle.get_width()) / Pango::SCALE;
437     double text_h = double(rectangle.get_height()) / Pango::SCALE;
438     double x = w * (key + 0.75) / 128.0;
439 persson 2169 Gdk::Cairo::set_source_rgba(cr, black);
440 persson 2151 cr->move_to(int(x - text_w / 2 + 1), int(h1 + h - text_h + 0.5));
441     #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 16) || GTKMM_MAJOR_VERSION < 2
442     pango_cairo_show_layout(cr->cobj(), layout->gobj());
443     #else
444     layout->show_in_cairo_context(cr);
445     #endif
446 persson 1658 }
447    
448 persson 2246 void RegionChooser::draw_key(const Cairo::RefPtr<Cairo::Context>& cr,
449     int key) {
450 schoenebeck 1660 const int h = KEYBOARD_HEIGHT;
451 persson 1658 const int w = get_width() - 1;
452 schoenebeck 1225 const int bh = int(h * 0.55);
453    
454 persson 2246 Gdk::Cairo::set_source_rgba(cr, activeKeyColor);
455 schoenebeck 1225
456 persson 2151 int note = (key + 3) % 12;
457 persson 2246 int x = key_to_x(key, w) + 1;
458     int x2 = key_to_x(key + 1.5, w);
459     int x3 = key_to_x(key + 1, w);
460     int x4 = key_to_x(key - 0.5, w);
461 persson 2151 int w1 = x3 - x;
462     switch (note) {
463     case 0: case 5: case 10:
464     cr->rectangle(x, h1 + 1, w1, bh);
465     cr->fill();
466     cr->rectangle(x4 + 1, h1 + bh + 1, x2 - x4 - 1, h - bh - 2);
467     cr->fill();
468     break;
469     case 2: case 7:
470     cr->rectangle(x, h1 + 1, w1, bh);
471     cr->fill();
472     cr->rectangle(x4 + 1, h1 + bh + 1, x3 - x4 - 1, h - bh - 2);
473     cr->fill();
474     break;
475     case 3: case 8:
476     cr->rectangle(x, h1 + 1, w1, bh);
477     cr->fill();
478     cr->rectangle(x, h1 + bh + 1, x2 - x, h - bh - 2);
479     cr->fill();
480     break;
481     default:
482     cr->rectangle(x, h1 + 1, w1, bh - 1);
483     cr->fill();
484     break;
485 schoenebeck 1225 }
486 persson 2246 Gdk::Cairo::set_source_rgba(cr, black);
487 schoenebeck 1225 }
488    
489     void RegionChooser::set_instrument(gig::Instrument* instrument)
490     {
491     this->instrument = instrument;
492 persson 1623 regions.update(instrument);
493     region = regions.first();
494 schoenebeck 1225 queue_draw();
495 persson 1261 region_selected();
496 persson 1677 dimensionManager.set_region(region);
497 schoenebeck 1225 }
498    
499     bool RegionChooser::on_button_release_event(GdkEventButton* event)
500     {
501 persson 2246 const int k = x_to_key(event->x, get_width() - 1);
502 schoenebeck 1660
503 schoenebeck 1661 // handle-note off on virtual keyboard
504 schoenebeck 1660 if (event->type == GDK_BUTTON_RELEASE) {
505 schoenebeck 1661 int velocity = (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
506     int(float(event->y - REGION_BLOCK_HEIGHT) / float(KEYBOARD_HEIGHT) * 128.0f) + 1;
507     if (velocity <= 0) velocity = 1;
508     switch (m_VirtKeybModeChoice.get_value()) {
509     case VIRT_KEYBOARD_MODE_CHORD:
510     if (event->y >= REGION_BLOCK_HEIGHT)
511     keyboard_key_released_signal.emit(k, velocity);
512     break;
513     case VIRT_KEYBOARD_MODE_NORMAL:
514     default:
515     if (currentActiveKey >= 0 && currentActiveKey <= 127) {
516     keyboard_key_released_signal.emit(currentActiveKey, velocity);
517     currentActiveKey = -1;
518     }
519     break;
520 schoenebeck 1660 }
521     }
522    
523 schoenebeck 1225 if (resize.active) {
524 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
525 schoenebeck 1225 get_window()->pointer_ungrab(event->time);
526 persson 2169 #else
527     Glib::wrap(event->device, true)->ungrab(event->time);
528     #endif
529 schoenebeck 1225 resize.active = false;
530    
531     if (!is_in_resize_zone(event->x, event->y) && cursor_is_resize) {
532     get_window()->set_cursor();
533     cursor_is_resize = false;
534     }
535 persson 1262 } else if (move.active) {
536 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
537 persson 1262 get_window()->pointer_ungrab(event->time);
538 persson 2169 #else
539     Glib::wrap(event->device, true)->ungrab(event->time);
540     #endif
541 persson 1262 move.active = false;
542    
543     if (is_in_resize_zone(event->x, event->y)) {
544 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
545 persson 1262 get_window()->set_cursor(Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW));
546 persson 2169 #else
547     get_window()->set_cursor(Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW));
548     #endif
549 persson 1262 cursor_is_resize = true;
550     }
551 schoenebeck 1225 }
552     return true;
553     }
554    
555 persson 2246 void RegionChooser::update_after_resize()
556     {
557     if (resize.mode == resize.moving_high_limit) {
558     if (resize.region->KeyRange.high != resize.pos - 1) {
559     instrument_struct_to_be_changed_signal.emit(instrument);
560     resize.region->SetKeyRange(resize.region->KeyRange.low,
561     resize.pos - 1);
562     regions.update(instrument);
563     instrument_changed.emit();
564     instrument_struct_changed_signal.emit(instrument);
565     }
566     } else if (resize.mode == resize.moving_low_limit) {
567     if (resize.region->KeyRange.low != resize.pos) {
568     instrument_struct_to_be_changed_signal.emit(instrument);
569     resize.region->SetKeyRange(resize.pos,
570     resize.region->KeyRange.high);
571     regions.update(instrument);
572     instrument_changed.emit();
573     instrument_struct_changed_signal.emit(instrument);
574     }
575     }
576     }
577    
578     void RegionChooser::update_after_move(int pos)
579     {
580     instrument_struct_to_be_changed_signal.emit(instrument);
581 schoenebeck 2773 const int range = region->KeyRange.high - region->KeyRange.low;
582     const int diff = pos - int(region->KeyRange.low);
583     region->SetKeyRange(pos, pos + range);
584     if (Settings::singleton()->moveRootNoteWithRegionMoved) {
585     for (int i = 0; i < 256; ++i) {
586     gig::DimensionRegion* dimrgn = region->pDimensionRegions[i];
587     if (!dimrgn || !dimrgn->pSample || !dimrgn->PitchTrack) continue;
588     dimrgn->UnityNote += diff;
589     }
590     }
591 persson 2246 regions.update(instrument);
592     instrument_changed.emit();
593     instrument_struct_changed_signal.emit(instrument);
594     }
595    
596 schoenebeck 1225 bool RegionChooser::on_button_press_event(GdkEventButton* event)
597     {
598     if (!instrument) return true;
599    
600 persson 2246 const int w = get_width() - 1;
601     const int k = x_to_key(event->x, w);
602 schoenebeck 1225
603 schoenebeck 1660 if (event->type == GDK_BUTTON_PRESS) {
604     if (event->y >= REGION_BLOCK_HEIGHT) {
605     int velocity = (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
606     int(float(event->y - REGION_BLOCK_HEIGHT) / float(KEYBOARD_HEIGHT) * 128.0f) + 1;
607 schoenebeck 1661 currentActiveKey = k;
608 schoenebeck 1660 keyboard_key_hit_signal.emit(k, velocity);
609     }
610     }
611    
612 schoenebeck 2641 // left mouse button double click
613     if (event->type == GDK_2BUTTON_PRESS && event->button == 1) {
614 schoenebeck 2663 if (event->y < REGION_BLOCK_HEIGHT) {
615     // show dimension manager dialog for this region
616     manage_dimensions();
617     }
618 schoenebeck 2641 }
619    
620 schoenebeck 1660 if (event->y >= REGION_BLOCK_HEIGHT) return true;
621 schoenebeck 1225 if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
622     gig::Region* r = get_region(k);
623     if (r) {
624     region = r;
625     queue_draw();
626 persson 1261 region_selected();
627 persson 1677 dimensionManager.set_region(region);
628 schoenebeck 1225 popup_menu_inside_region->popup(event->button, event->time);
629     } else {
630     new_region_pos = k;
631     popup_menu_outside_region->popup(event->button, event->time);
632     }
633     } else {
634     if (is_in_resize_zone(event->x, event->y)) {
635 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
636 schoenebeck 1225 get_window()->pointer_grab(false,
637     Gdk::BUTTON_RELEASE_MASK |
638     Gdk::POINTER_MOTION_MASK |
639     Gdk::POINTER_MOTION_HINT_MASK,
640 persson 2169 Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW),
641     event->time);
642     #else
643     Glib::wrap(event->device, true)->grab(get_window(),
644     Gdk::OWNERSHIP_NONE,
645     false,
646     Gdk::BUTTON_RELEASE_MASK |
647     Gdk::POINTER_MOTION_MASK |
648     Gdk::POINTER_MOTION_HINT_MASK,
649     Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW),
650     event->time);
651     #endif
652 schoenebeck 1225 resize.active = true;
653     } else {
654     gig::Region* r = get_region(k);
655     if (r) {
656     region = r;
657     queue_draw();
658 persson 1261 region_selected();
659 persson 1677 dimensionManager.set_region(region);
660 persson 1262
661 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
662 persson 1262 get_window()->pointer_grab(false,
663     Gdk::BUTTON_RELEASE_MASK |
664     Gdk::POINTER_MOTION_MASK |
665     Gdk::POINTER_MOTION_HINT_MASK,
666 persson 2169 Gdk::Cursor(Gdk::FLEUR),
667     event->time);
668     #else
669     Glib::wrap(event->device, true)->grab(get_window(),
670     Gdk::OWNERSHIP_NONE,
671     false,
672     Gdk::BUTTON_RELEASE_MASK |
673     Gdk::POINTER_MOTION_MASK |
674     Gdk::POINTER_MOTION_HINT_MASK,
675     Gdk::Cursor::create(Gdk::FLEUR),
676     event->time);
677     #endif
678 persson 1262 move.active = true;
679 persson 2246 move.offset = event->x - key_to_x(region->KeyRange.low, w);
680 schoenebeck 1225 }
681     }
682     }
683     return true;
684     }
685    
686     gig::Region* RegionChooser::get_region(int key)
687     {
688 persson 2246 for (gig::Region* r = regions.first() ; r ; r = regions.next()) {
689 schoenebeck 1225 if (key < r->KeyRange.low) return 0;
690 persson 2246 if (key <= r->KeyRange.high) return r;
691 schoenebeck 1225 }
692     return 0;
693     }
694    
695 schoenebeck 2695 void RegionChooser::set_region(gig::Region* region) {
696     this->region = region;
697     queue_draw();
698     region_selected();
699     dimensionManager.set_region(region);
700     }
701    
702 persson 1262 void RegionChooser::motion_resize_region(int x, int y)
703 schoenebeck 1225 {
704 persson 1623 const int w = get_width() - 1;
705 schoenebeck 1225
706 persson 1262 int k = int(double(x) / w * 128.0 + 0.5);
707 schoenebeck 1225
708 persson 1262 if (k < resize.min) k = resize.min;
709     else if (k > resize.max) k = resize.max;
710    
711     if (k != resize.pos) {
712     if (resize.mode == resize.undecided) {
713     if (k < resize.pos) {
714     // edit high limit of prev_region
715     resize.max = resize.region->KeyRange.low;
716     resize.region = resize.prev_region;
717     resize.mode = resize.moving_high_limit;
718     } else {
719     // edit low limit of region
720     resize.min = resize.prev_region->KeyRange.high + 1;
721     resize.mode = resize.moving_low_limit;
722 schoenebeck 1225 }
723 persson 1262 }
724 persson 2246 resize.pos = k;
725 persson 2151
726 persson 2246 int x1, x2;
727 persson 1262 if (resize.mode == resize.moving_high_limit) {
728 persson 2246 if (resize.region->KeyRange.high < resize.pos - 1) {
729     x1 = resize.region->KeyRange.high;
730     x2 = resize.pos - 1;
731 persson 1262 } else {
732 persson 2246 x1 = resize.pos - 1;
733     x2 = resize.region->KeyRange.high;
734 schoenebeck 1225 }
735 persson 1262 } else {
736 persson 2246 if (resize.region->KeyRange.low < resize.pos) {
737     x1 = resize.region->KeyRange.low;
738     x2 = resize.pos;
739 persson 1262 } else {
740 persson 2246 x1 = resize.pos;
741     x2 = resize.region->KeyRange.low;
742 persson 1262 }
743     }
744 persson 2246 x1 = key_to_x(x1, w);
745     x2 = key_to_x(x2 + 1, w) + 1;
746     Gdk::Rectangle rect(x1, 0, x2 - x1, h1);
747    
748     update_after_resize();
749    
750 schoenebeck 3106 //get_window()->invalidate_rect(rect, false);
751     get_window()->invalidate(false); // repaint entire region, otherwise it would create visual artifacts
752 persson 1262 }
753     }
754 schoenebeck 1225
755 persson 1262 void RegionChooser::motion_move_region(int x, int y)
756     {
757 persson 1623 const int w = get_width() - 1;
758 persson 1262
759 persson 2246 int l = int(double(x - move.offset) / w * 128.0 + 0.5);
760    
761     if (l == region->KeyRange.low) return;
762     int new_l;
763     int regionsize = region->KeyRange.high - region->KeyRange.low;
764 persson 1262 int a = 0;
765 persson 2246 if (l > region->KeyRange.low) {
766 persson 1623 for (gig::Region* r = regions.first() ; ; r = regions.next()) {
767 persson 1262 if (r != region) {
768     int b = r ? r->KeyRange.low : 128;
769    
770     // gap: from a to b (not inclusive b)
771    
772 persson 2246 if (region->KeyRange.high >= b) {
773 persson 1262 // not found the current gap yet, just continue
774 schoenebeck 1225 } else {
775 persson 1262
776 persson 2246 if (a > l) {
777 persson 1262 // this gap is too far to the right, break
778     break;
779     }
780    
781 persson 2246 int newhigh = std::min(l + regionsize, b - 1);
782     int newlo = newhigh - regionsize;
783 persson 1262
784     if (newlo >= a) {
785     // yes it fits - it's a candidate
786 persson 2246 new_l = newlo;
787 persson 1262 }
788 schoenebeck 1225 }
789 persson 1262 if (!r) break;
790     a = r->KeyRange.high + 1;
791     }
792     }
793     } else {
794 persson 1623 for (gig::Region* r = regions.first() ; ; r = regions.next()) {
795 persson 1262 if (r != region) {
796     int b = r ? r->KeyRange.low : 128;
797    
798     // gap from a to b (not inclusive b)
799    
800 persson 2246 if (l + regionsize >= b) {
801 persson 1262 // not found the current gap yet, just continue
802 schoenebeck 1225 } else {
803 persson 1262
804 persson 2246 if (a > region->KeyRange.low) {
805 persson 1262 // this gap is too far to the right, break
806     break;
807     }
808    
809 persson 2246 int newlo = std::max(l, a);
810     int newhigh = newlo + regionsize;
811 persson 1262
812     if (newhigh < b) {
813     // yes it fits - break as the first one is the best
814 persson 2246 new_l = newlo;
815 persson 1262 break;
816     }
817 schoenebeck 1225 }
818 persson 1262 if (!r) break;
819     a = r->KeyRange.high + 1;
820 schoenebeck 1225 }
821     }
822 persson 1262 }
823 persson 2246 if (new_l == region->KeyRange.low) return;
824 persson 1262
825 persson 2246 int x1 = key_to_x(std::min(int(region->KeyRange.low), new_l), w);
826     int x2 = key_to_x(std::max(int(region->KeyRange.high),
827     new_l + regionsize) + 1, w) + 1;
828 persson 2169
829 persson 2246 Gdk::Rectangle rect(x1, 0, x2 - x1, h1);
830     update_after_move(new_l);
831 persson 1262
832 persson 2246 get_window()->invalidate_rect(rect, false);
833 persson 1262 }
834    
835    
836     bool RegionChooser::on_motion_notify_event(GdkEventMotion* event)
837     {
838     Glib::RefPtr<Gdk::Window> window = get_window();
839     int x, y;
840     Gdk::ModifierType state = Gdk::ModifierType(0);
841     window->get_pointer(x, y, state);
842    
843 schoenebeck 1661 // handle virtual MIDI keyboard
844     if (m_VirtKeybModeChoice.get_value() != VIRT_KEYBOARD_MODE_CHORD &&
845     currentActiveKey > 0 &&
846     event->y >= REGION_BLOCK_HEIGHT &&
847     event->y < REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT)
848     {
849 persson 2246 const int k = x_to_key(event->x, get_width() - 1);
850 persson 1898 if (k != currentActiveKey) {
851     int velocity =
852     (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
853     int(float(event->y - REGION_BLOCK_HEIGHT) /
854     float(KEYBOARD_HEIGHT) * 128.0f) + 1;
855     if (velocity <= 0) velocity = 1;
856     keyboard_key_released_signal.emit(currentActiveKey, velocity);
857     currentActiveKey = k;
858     keyboard_key_hit_signal.emit(k, velocity);
859     }
860 schoenebeck 1661 }
861    
862 persson 1262 if (resize.active) {
863     motion_resize_region(x, y);
864     } else if (move.active) {
865     motion_move_region(x, y);
866     } else {
867 schoenebeck 1225 if (is_in_resize_zone(x, y)) {
868     if (!cursor_is_resize) {
869 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
870 persson 1262 window->set_cursor(Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW));
871 persson 2169 #else
872     window->set_cursor(Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW));
873     #endif
874 schoenebeck 1225 cursor_is_resize = true;
875     }
876     } else if (cursor_is_resize) {
877     window->set_cursor();
878     cursor_is_resize = false;
879     }
880     }
881    
882     return true;
883     }
884    
885     bool RegionChooser::is_in_resize_zone(double x, double y) {
886 persson 1623 const int w = get_width() - 1;
887 schoenebeck 1225
888     if (instrument && y >= 0 && y <= h1) {
889     gig::Region* prev_region = 0;
890     gig::Region* next_region;
891 persson 1623 for (gig::Region* r = regions.first(); r ; r = next_region) {
892     next_region = regions.next();
893 schoenebeck 1225
894 persson 2246 int lo = key_to_x(r->KeyRange.low, w);
895 schoenebeck 1225 if (x <= lo - 2) break;
896     if (x < lo + 2) {
897     resize.region = r;
898     resize.pos = r->KeyRange.low;
899     resize.max = r->KeyRange.high;
900    
901     if (prev_region && prev_region->KeyRange.high + 1 == r->KeyRange.low) {
902     // we don't know yet if it's the high limit of
903     // prev_region or the low limit of r that's going
904     // to be edited
905     resize.mode = resize.undecided;
906     resize.min = prev_region->KeyRange.low + 1;
907     resize.prev_region = prev_region;
908 persson 1623 return resize.min != resize.max;
909 schoenebeck 1225 }
910    
911     // edit low limit
912     resize.mode = resize.moving_low_limit;
913     resize.min = prev_region ? prev_region->KeyRange.high + 1 : 0;
914 persson 1623 return resize.min != resize.max;
915 schoenebeck 1225 }
916     if (!next_region || r->KeyRange.high + 1 != next_region->KeyRange.low) {
917 persson 2246 int hi = key_to_x(r->KeyRange.high + 1, w);
918 schoenebeck 1225 if (x <= hi - 2) break;
919     if (x < hi + 2) {
920     // edit high limit
921     resize.region = r;
922     resize.pos = r->KeyRange.high + 1;
923     resize.mode = resize.moving_high_limit;
924     resize.min = r->KeyRange.low + 1;
925     resize.max = next_region ? next_region->KeyRange.low : 128;
926 persson 1623 return resize.min != resize.max;
927 schoenebeck 1225 }
928     }
929     prev_region = r;
930     }
931     }
932     return false;
933     }
934    
935 schoenebeck 1339 sigc::signal<void>& RegionChooser::signal_region_selected()
936 schoenebeck 1225 {
937 persson 1261 return region_selected;
938 schoenebeck 1225 }
939    
940 schoenebeck 1339 sigc::signal<void>& RegionChooser::signal_instrument_changed()
941 persson 1261 {
942     return instrument_changed;
943     }
944    
945 schoenebeck 1225 void RegionChooser::show_region_properties()
946     {
947     if (!region) return;
948 persson 1831 Gtk::Dialog dialog(_("Region Properties"), true /*modal*/);
949 schoenebeck 1225 // add "Keygroup" checkbox
950 persson 1831 Gtk::CheckButton checkBoxKeygroup(_("Member of a Keygroup (Exclusive Group)"));
951 schoenebeck 1225 checkBoxKeygroup.set_active(region->KeyGroup);
952     dialog.get_vbox()->pack_start(checkBoxKeygroup);
953     checkBoxKeygroup.show();
954     // add "Keygroup" spinbox
955 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
956     Gtk::Adjustment adjustment(1, 1, 999);
957 schoenebeck 1225 Gtk::SpinButton spinBox(adjustment);
958 persson 2169 #else
959     Gtk::SpinButton spinBox(Gtk::Adjustment::create(1, 1, 999));
960     #endif
961 schoenebeck 1225 if (region->KeyGroup) spinBox.set_value(region->KeyGroup);
962     dialog.get_vbox()->pack_start(spinBox);
963     spinBox.show();
964     // add OK and CANCEL buttons to the dialog
965 persson 2845 dialog.add_button(_("_OK"), 0);
966     dialog.add_button(_("_Cancel"), 1);
967 schoenebeck 1225 dialog.show_all_children();
968     if (!dialog.run()) { // OK selected ...
969     region->KeyGroup =
970     (checkBoxKeygroup.get_active()) ? spinBox.get_value_as_int() : 0;
971     }
972     }
973    
974     void RegionChooser::add_region()
975     {
976 schoenebeck 1322 instrument_struct_to_be_changed_signal.emit(instrument);
977    
978 schoenebeck 1225 region = instrument->AddRegion();
979 schoenebeck 1336 region->SetKeyRange(new_region_pos, new_region_pos);
980 schoenebeck 1225
981 schoenebeck 1322 instrument_struct_changed_signal.emit(instrument);
982 persson 1623 regions.update(instrument);
983 schoenebeck 1322
984 schoenebeck 1225 queue_draw();
985 persson 1261 region_selected();
986 persson 1677 dimensionManager.set_region(region);
987 persson 1261 instrument_changed();
988 schoenebeck 1225 }
989    
990     void RegionChooser::delete_region()
991     {
992 schoenebeck 1322 instrument_struct_to_be_changed_signal.emit(instrument);
993 schoenebeck 1225 instrument->DeleteRegion(region);
994 schoenebeck 1322 instrument_struct_changed_signal.emit(instrument);
995 persson 1623 regions.update(instrument);
996 schoenebeck 1322
997 schoenebeck 1225 region = 0;
998     queue_draw();
999 persson 1261 region_selected();
1000 persson 1677 dimensionManager.set_region(region);
1001 persson 1261 instrument_changed();
1002 schoenebeck 1225 }
1003    
1004     void RegionChooser::manage_dimensions()
1005     {
1006     gig::Region* region = get_region();
1007     if (!region) return;
1008     dimensionManager.show(region);
1009     }
1010    
1011     void RegionChooser::on_dimension_manager_changed() {
1012 persson 1261 region_selected();
1013     instrument_changed();
1014 schoenebeck 1225 }
1015 schoenebeck 1322
1016 schoenebeck 1339 sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_to_be_changed() {
1017 schoenebeck 1322 return instrument_struct_to_be_changed_signal;
1018     }
1019    
1020 schoenebeck 1339 sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_changed() {
1021 schoenebeck 1322 return instrument_struct_changed_signal;
1022     }
1023    
1024 schoenebeck 1339 sigc::signal<void, gig::Region*>& RegionChooser::signal_region_to_be_changed() {
1025 schoenebeck 1322 return region_to_be_changed_signal;
1026     }
1027    
1028 schoenebeck 1339 sigc::signal<void, gig::Region*>& RegionChooser::signal_region_changed_signal() {
1029 schoenebeck 1322 return region_changed_signal;
1030     }
1031 schoenebeck 1660
1032     sigc::signal<void, int/*key*/, int/*velocity*/>& RegionChooser::signal_keyboard_key_hit() {
1033     return keyboard_key_hit_signal;
1034     }
1035    
1036     sigc::signal<void, int/*key*/, int/*velocity*/>& RegionChooser::signal_keyboard_key_released() {
1037     return keyboard_key_released_signal;
1038     }

  ViewVC Help
Powered by ViewVC