/[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 2695 - (hide annotations) (download)
Tue Jan 6 18:11:27 2015 UTC (9 years, 3 months ago) by schoenebeck
File size: 33614 byte(s)
* Sample Referenve View Dialog: Clicking on a reference in the list closes
  the dialog and jumps directly to the respective instrument, region and
  dimension region the respective sample reference is located at.

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

  ViewVC Help
Powered by ViewVC