/[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 2845 - (hide annotations) (download)
Sun Sep 20 10:18:22 2015 UTC (8 years, 7 months ago) by persson
File size: 33845 byte(s)
* avoid using gtk stock items, as they are deprecated in gtk 3.10

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

  ViewVC Help
Powered by ViewVC