/[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 2773 - (hide annotations) (download)
Fri Jun 12 17:57:52 2015 UTC (8 years, 10 months ago) by schoenebeck
File size: 34004 byte(s)
* Region Chooser: when moving a region around automatically move the
  keyboard position dependent pitch accordingly (can be turned off with
  "Settings"->"Move root key with region moved").

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

  ViewVC Help
Powered by ViewVC