/[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 2641 - (hide annotations) (download)
Mon Jun 16 15:24:54 2014 UTC (9 years, 10 months ago) by schoenebeck
File size: 33384 byte(s)
* Region Chooser: Double click opens dimension manager dialog.
* Dimension Manager: Clicking on dimension type cell of an existing
  dimension opens a popup to alter the dimension type.

1 schoenebeck 1225 /*
2 persson 2507 * Copyright (C) 2006-2014 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     // show dimension manager dialog for this region
544     manage_dimensions();
545     }
546    
547 schoenebeck 1660 if (event->y >= REGION_BLOCK_HEIGHT) return true;
548 schoenebeck 1225 if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
549     gig::Region* r = get_region(k);
550     if (r) {
551     region = r;
552     queue_draw();
553 persson 1261 region_selected();
554 persson 1677 dimensionManager.set_region(region);
555 schoenebeck 1225 popup_menu_inside_region->popup(event->button, event->time);
556     } else {
557     new_region_pos = k;
558     popup_menu_outside_region->popup(event->button, event->time);
559     }
560     } else {
561     if (is_in_resize_zone(event->x, event->y)) {
562 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
563 schoenebeck 1225 get_window()->pointer_grab(false,
564     Gdk::BUTTON_RELEASE_MASK |
565     Gdk::POINTER_MOTION_MASK |
566     Gdk::POINTER_MOTION_HINT_MASK,
567 persson 2169 Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW),
568     event->time);
569     #else
570     Glib::wrap(event->device, true)->grab(get_window(),
571     Gdk::OWNERSHIP_NONE,
572     false,
573     Gdk::BUTTON_RELEASE_MASK |
574     Gdk::POINTER_MOTION_MASK |
575     Gdk::POINTER_MOTION_HINT_MASK,
576     Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW),
577     event->time);
578     #endif
579 schoenebeck 1225 resize.active = true;
580     } else {
581     gig::Region* r = get_region(k);
582     if (r) {
583     region = r;
584     queue_draw();
585 persson 1261 region_selected();
586 persson 1677 dimensionManager.set_region(region);
587 persson 1262
588 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
589 persson 1262 get_window()->pointer_grab(false,
590     Gdk::BUTTON_RELEASE_MASK |
591     Gdk::POINTER_MOTION_MASK |
592     Gdk::POINTER_MOTION_HINT_MASK,
593 persson 2169 Gdk::Cursor(Gdk::FLEUR),
594     event->time);
595     #else
596     Glib::wrap(event->device, true)->grab(get_window(),
597     Gdk::OWNERSHIP_NONE,
598     false,
599     Gdk::BUTTON_RELEASE_MASK |
600     Gdk::POINTER_MOTION_MASK |
601     Gdk::POINTER_MOTION_HINT_MASK,
602     Gdk::Cursor::create(Gdk::FLEUR),
603     event->time);
604     #endif
605 persson 1262 move.active = true;
606 persson 2246 move.offset = event->x - key_to_x(region->KeyRange.low, w);
607 schoenebeck 1225 }
608     }
609     }
610     return true;
611     }
612    
613     gig::Region* RegionChooser::get_region(int key)
614     {
615 persson 2246 for (gig::Region* r = regions.first() ; r ; r = regions.next()) {
616 schoenebeck 1225 if (key < r->KeyRange.low) return 0;
617 persson 2246 if (key <= r->KeyRange.high) return r;
618 schoenebeck 1225 }
619     return 0;
620     }
621    
622 persson 1262 void RegionChooser::motion_resize_region(int x, int y)
623 schoenebeck 1225 {
624 persson 1623 const int w = get_width() - 1;
625 schoenebeck 1225
626 persson 1262 int k = int(double(x) / w * 128.0 + 0.5);
627 schoenebeck 1225
628 persson 1262 if (k < resize.min) k = resize.min;
629     else if (k > resize.max) k = resize.max;
630    
631     if (k != resize.pos) {
632     if (resize.mode == resize.undecided) {
633     if (k < resize.pos) {
634     // edit high limit of prev_region
635     resize.max = resize.region->KeyRange.low;
636     resize.region = resize.prev_region;
637     resize.mode = resize.moving_high_limit;
638     } else {
639     // edit low limit of region
640     resize.min = resize.prev_region->KeyRange.high + 1;
641     resize.mode = resize.moving_low_limit;
642 schoenebeck 1225 }
643 persson 1262 }
644 persson 2246 resize.pos = k;
645 persson 2151
646 persson 2246 int x1, x2;
647 persson 1262 if (resize.mode == resize.moving_high_limit) {
648 persson 2246 if (resize.region->KeyRange.high < resize.pos - 1) {
649     x1 = resize.region->KeyRange.high;
650     x2 = resize.pos - 1;
651 persson 1262 } else {
652 persson 2246 x1 = resize.pos - 1;
653     x2 = resize.region->KeyRange.high;
654 schoenebeck 1225 }
655 persson 1262 } else {
656 persson 2246 if (resize.region->KeyRange.low < resize.pos) {
657     x1 = resize.region->KeyRange.low;
658     x2 = resize.pos;
659 persson 1262 } else {
660 persson 2246 x1 = resize.pos;
661     x2 = resize.region->KeyRange.low;
662 persson 1262 }
663     }
664 persson 2246 x1 = key_to_x(x1, w);
665     x2 = key_to_x(x2 + 1, w) + 1;
666     Gdk::Rectangle rect(x1, 0, x2 - x1, h1);
667    
668     update_after_resize();
669    
670     get_window()->invalidate_rect(rect, false);
671 persson 1262 }
672     }
673 schoenebeck 1225
674 persson 1262 void RegionChooser::motion_move_region(int x, int y)
675     {
676 persson 1623 const int w = get_width() - 1;
677 persson 1262
678 persson 2246 int l = int(double(x - move.offset) / w * 128.0 + 0.5);
679    
680     if (l == region->KeyRange.low) return;
681     int new_l;
682     int regionsize = region->KeyRange.high - region->KeyRange.low;
683 persson 1262 int a = 0;
684 persson 2246 if (l > region->KeyRange.low) {
685 persson 1623 for (gig::Region* r = regions.first() ; ; r = regions.next()) {
686 persson 1262 if (r != region) {
687     int b = r ? r->KeyRange.low : 128;
688    
689     // gap: from a to b (not inclusive b)
690    
691 persson 2246 if (region->KeyRange.high >= b) {
692 persson 1262 // not found the current gap yet, just continue
693 schoenebeck 1225 } else {
694 persson 1262
695 persson 2246 if (a > l) {
696 persson 1262 // this gap is too far to the right, break
697     break;
698     }
699    
700 persson 2246 int newhigh = std::min(l + regionsize, b - 1);
701     int newlo = newhigh - regionsize;
702 persson 1262
703     if (newlo >= a) {
704     // yes it fits - it's a candidate
705 persson 2246 new_l = newlo;
706 persson 1262 }
707 schoenebeck 1225 }
708 persson 1262 if (!r) break;
709     a = r->KeyRange.high + 1;
710     }
711     }
712     } else {
713 persson 1623 for (gig::Region* r = regions.first() ; ; r = regions.next()) {
714 persson 1262 if (r != region) {
715     int b = r ? r->KeyRange.low : 128;
716    
717     // gap from a to b (not inclusive b)
718    
719 persson 2246 if (l + regionsize >= b) {
720 persson 1262 // not found the current gap yet, just continue
721 schoenebeck 1225 } else {
722 persson 1262
723 persson 2246 if (a > region->KeyRange.low) {
724 persson 1262 // this gap is too far to the right, break
725     break;
726     }
727    
728 persson 2246 int newlo = std::max(l, a);
729     int newhigh = newlo + regionsize;
730 persson 1262
731     if (newhigh < b) {
732     // yes it fits - break as the first one is the best
733 persson 2246 new_l = newlo;
734 persson 1262 break;
735     }
736 schoenebeck 1225 }
737 persson 1262 if (!r) break;
738     a = r->KeyRange.high + 1;
739 schoenebeck 1225 }
740     }
741 persson 1262 }
742 persson 2246 if (new_l == region->KeyRange.low) return;
743 persson 1262
744 persson 2246 int x1 = key_to_x(std::min(int(region->KeyRange.low), new_l), w);
745     int x2 = key_to_x(std::max(int(region->KeyRange.high),
746     new_l + regionsize) + 1, w) + 1;
747 persson 2169
748 persson 2246 Gdk::Rectangle rect(x1, 0, x2 - x1, h1);
749     update_after_move(new_l);
750 persson 1262
751 persson 2246 get_window()->invalidate_rect(rect, false);
752 persson 1262 }
753    
754    
755     bool RegionChooser::on_motion_notify_event(GdkEventMotion* event)
756     {
757     Glib::RefPtr<Gdk::Window> window = get_window();
758     int x, y;
759     Gdk::ModifierType state = Gdk::ModifierType(0);
760     window->get_pointer(x, y, state);
761    
762 schoenebeck 1661 // handle virtual MIDI keyboard
763     if (m_VirtKeybModeChoice.get_value() != VIRT_KEYBOARD_MODE_CHORD &&
764     currentActiveKey > 0 &&
765     event->y >= REGION_BLOCK_HEIGHT &&
766     event->y < REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT)
767     {
768 persson 2246 const int k = x_to_key(event->x, get_width() - 1);
769 persson 1898 if (k != currentActiveKey) {
770     int velocity =
771     (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
772     int(float(event->y - REGION_BLOCK_HEIGHT) /
773     float(KEYBOARD_HEIGHT) * 128.0f) + 1;
774     if (velocity <= 0) velocity = 1;
775     keyboard_key_released_signal.emit(currentActiveKey, velocity);
776     currentActiveKey = k;
777     keyboard_key_hit_signal.emit(k, velocity);
778     }
779 schoenebeck 1661 }
780    
781 persson 1262 if (resize.active) {
782     motion_resize_region(x, y);
783     } else if (move.active) {
784     motion_move_region(x, y);
785     } else {
786 schoenebeck 1225 if (is_in_resize_zone(x, y)) {
787     if (!cursor_is_resize) {
788 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
789 persson 1262 window->set_cursor(Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW));
790 persson 2169 #else
791     window->set_cursor(Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW));
792     #endif
793 schoenebeck 1225 cursor_is_resize = true;
794     }
795     } else if (cursor_is_resize) {
796     window->set_cursor();
797     cursor_is_resize = false;
798     }
799     }
800    
801     return true;
802     }
803    
804     bool RegionChooser::is_in_resize_zone(double x, double y) {
805 persson 1623 const int w = get_width() - 1;
806 schoenebeck 1225
807     if (instrument && y >= 0 && y <= h1) {
808     gig::Region* prev_region = 0;
809     gig::Region* next_region;
810 persson 1623 for (gig::Region* r = regions.first(); r ; r = next_region) {
811     next_region = regions.next();
812 schoenebeck 1225
813 persson 2246 int lo = key_to_x(r->KeyRange.low, w);
814 schoenebeck 1225 if (x <= lo - 2) break;
815     if (x < lo + 2) {
816     resize.region = r;
817     resize.pos = r->KeyRange.low;
818     resize.max = r->KeyRange.high;
819    
820     if (prev_region && prev_region->KeyRange.high + 1 == r->KeyRange.low) {
821     // we don't know yet if it's the high limit of
822     // prev_region or the low limit of r that's going
823     // to be edited
824     resize.mode = resize.undecided;
825     resize.min = prev_region->KeyRange.low + 1;
826     resize.prev_region = prev_region;
827 persson 1623 return resize.min != resize.max;
828 schoenebeck 1225 }
829    
830     // edit low limit
831     resize.mode = resize.moving_low_limit;
832     resize.min = prev_region ? prev_region->KeyRange.high + 1 : 0;
833 persson 1623 return resize.min != resize.max;
834 schoenebeck 1225 }
835     if (!next_region || r->KeyRange.high + 1 != next_region->KeyRange.low) {
836 persson 2246 int hi = key_to_x(r->KeyRange.high + 1, w);
837 schoenebeck 1225 if (x <= hi - 2) break;
838     if (x < hi + 2) {
839     // edit high limit
840     resize.region = r;
841     resize.pos = r->KeyRange.high + 1;
842     resize.mode = resize.moving_high_limit;
843     resize.min = r->KeyRange.low + 1;
844     resize.max = next_region ? next_region->KeyRange.low : 128;
845 persson 1623 return resize.min != resize.max;
846 schoenebeck 1225 }
847     }
848     prev_region = r;
849     }
850     }
851     return false;
852     }
853    
854 schoenebeck 1339 sigc::signal<void>& RegionChooser::signal_region_selected()
855 schoenebeck 1225 {
856 persson 1261 return region_selected;
857 schoenebeck 1225 }
858    
859 schoenebeck 1339 sigc::signal<void>& RegionChooser::signal_instrument_changed()
860 persson 1261 {
861     return instrument_changed;
862     }
863    
864 schoenebeck 1225 void RegionChooser::show_region_properties()
865     {
866     if (!region) return;
867 persson 1831 Gtk::Dialog dialog(_("Region Properties"), true /*modal*/);
868 schoenebeck 1225 // add "Keygroup" checkbox
869 persson 1831 Gtk::CheckButton checkBoxKeygroup(_("Member of a Keygroup (Exclusive Group)"));
870 schoenebeck 1225 checkBoxKeygroup.set_active(region->KeyGroup);
871     dialog.get_vbox()->pack_start(checkBoxKeygroup);
872     checkBoxKeygroup.show();
873     // add "Keygroup" spinbox
874 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
875     Gtk::Adjustment adjustment(1, 1, 999);
876 schoenebeck 1225 Gtk::SpinButton spinBox(adjustment);
877 persson 2169 #else
878     Gtk::SpinButton spinBox(Gtk::Adjustment::create(1, 1, 999));
879     #endif
880 schoenebeck 1225 if (region->KeyGroup) spinBox.set_value(region->KeyGroup);
881     dialog.get_vbox()->pack_start(spinBox);
882     spinBox.show();
883     // add OK and CANCEL buttons to the dialog
884     dialog.add_button(Gtk::Stock::OK, 0);
885     dialog.add_button(Gtk::Stock::CANCEL, 1);
886     dialog.show_all_children();
887     if (!dialog.run()) { // OK selected ...
888     region->KeyGroup =
889     (checkBoxKeygroup.get_active()) ? spinBox.get_value_as_int() : 0;
890     }
891     }
892    
893     void RegionChooser::add_region()
894     {
895 schoenebeck 1322 instrument_struct_to_be_changed_signal.emit(instrument);
896    
897 schoenebeck 1225 region = instrument->AddRegion();
898 schoenebeck 1336 region->SetKeyRange(new_region_pos, new_region_pos);
899 schoenebeck 1225
900 schoenebeck 1322 instrument_struct_changed_signal.emit(instrument);
901 persson 1623 regions.update(instrument);
902 schoenebeck 1322
903 schoenebeck 1225 queue_draw();
904 persson 1261 region_selected();
905 persson 1677 dimensionManager.set_region(region);
906 persson 1261 instrument_changed();
907 schoenebeck 1225 }
908    
909     void RegionChooser::delete_region()
910     {
911 schoenebeck 1322 instrument_struct_to_be_changed_signal.emit(instrument);
912 schoenebeck 1225 instrument->DeleteRegion(region);
913 schoenebeck 1322 instrument_struct_changed_signal.emit(instrument);
914 persson 1623 regions.update(instrument);
915 schoenebeck 1322
916 schoenebeck 1225 region = 0;
917     queue_draw();
918 persson 1261 region_selected();
919 persson 1677 dimensionManager.set_region(region);
920 persson 1261 instrument_changed();
921 schoenebeck 1225 }
922    
923     void RegionChooser::manage_dimensions()
924     {
925     gig::Region* region = get_region();
926     if (!region) return;
927     dimensionManager.show(region);
928     }
929    
930     void RegionChooser::on_dimension_manager_changed() {
931 persson 1261 region_selected();
932     instrument_changed();
933 schoenebeck 1225 }
934 schoenebeck 1322
935 schoenebeck 1339 sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_to_be_changed() {
936 schoenebeck 1322 return instrument_struct_to_be_changed_signal;
937     }
938    
939 schoenebeck 1339 sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_changed() {
940 schoenebeck 1322 return instrument_struct_changed_signal;
941     }
942    
943 schoenebeck 1339 sigc::signal<void, gig::Region*>& RegionChooser::signal_region_to_be_changed() {
944 schoenebeck 1322 return region_to_be_changed_signal;
945     }
946    
947 schoenebeck 1339 sigc::signal<void, gig::Region*>& RegionChooser::signal_region_changed_signal() {
948 schoenebeck 1322 return region_changed_signal;
949     }
950 schoenebeck 1660
951     sigc::signal<void, int/*key*/, int/*velocity*/>& RegionChooser::signal_keyboard_key_hit() {
952     return keyboard_key_hit_signal;
953     }
954    
955     sigc::signal<void, int/*key*/, int/*velocity*/>& RegionChooser::signal_keyboard_key_released() {
956     return keyboard_key_released_signal;
957     }

  ViewVC Help
Powered by ViewVC