/[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 2442 - (hide annotations) (download)
Sun Apr 14 07:29:59 2013 UTC (11 years ago) by persson
File size: 31873 byte(s)
* made sure the instruments menu is updated when instruments are
  added, removed or renamed

1 schoenebeck 1225 /*
2 persson 2169 * Copyright (C) 2006-2011 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 persson 2246 #define REGION_BLOCK_HEIGHT 20
34     #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 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 const int w = get_width() - 1;
216    
217     if (clipy2 > h1) {
218     draw_keyboard(cr, clipx1, clipx2);
219     }
220    
221     if (clipy1 < h1 && instrument) {
222     draw_regions(cr, clipx1, clipx2);
223     }
224    
225     cr->restore();
226    
227     return true;
228     }
229    
230     void RegionChooser::draw_keyboard(const Cairo::RefPtr<Cairo::Context>& cr,
231     int clip_low, int clip_high) {
232     const int h = KEYBOARD_HEIGHT;
233     const int w = get_width() - 1;
234     const int bh = int(h * 0.55);
235    
236 persson 2169 Gdk::Cairo::set_source_rgba(cr, black);
237     cr->rectangle(0.5, h1 + 0.5, w, h - 1);
238     cr->stroke();
239 schoenebeck 1225
240 persson 2246 int x1 = key_to_x(20.5, w);
241 persson 2169 Gdk::Cairo::set_source_rgba(cr, grey1);
242     cr->rectangle(1, h1 + 1, x1 - 1, h - 2);
243     cr->fill();
244 schoenebeck 1225
245 persson 2246 int x2 = key_to_x(109.5, w);
246 persson 2169 Gdk::Cairo::set_source_rgba(cr, white);
247     cr->rectangle(x1 + 1, h1 + 1, x2 - x1 - 1, h - 2);
248     cr->fill();
249 persson 2151
250 persson 2169 Gdk::Cairo::set_source_rgba(cr, grey1);
251     cr->rectangle(x2 + 1, h1 + 1, w - x2 - 1, h - 2);
252     cr->fill();
253 persson 2151
254 persson 2169 Gdk::Cairo::set_source_rgba(cr, black);
255 persson 2246
256     int clipkey1 = std::max(0, x_to_key_right(clip_low - 1, w));
257     int clipkey2 = std::min(x_to_key_right(clip_high - 1, w) + 1, 128);
258    
259     for (int i = clipkey1 ; i < clipkey2 ; i++) {
260 persson 2169 int note = (i + 3) % 12;
261 persson 2246 int x = key_to_x(i, w);
262 persson 2151
263 persson 2169 if (note == 1 || note == 4 || note == 6 ||
264     note == 9 || note == 11) {
265 persson 2246 // black key: short line in the middle, with a rectangle
266     // on top
267     int x2 = key_to_x(i + 0.5, w);
268 persson 2169 cr->move_to(x2 + 0.5, h1 + bh + 0.5);
269     cr->line_to(x2 + 0.5, h1 + h - 1);
270     cr->stroke();
271 persson 2151
272 persson 2246 int x3 = key_to_x(i + 1, w);
273 persson 2169 cr->rectangle(x, h1 + 1, x3 - x + 1, bh);
274     cr->fill();
275     } else if (note == 3 || note == 8) {
276 persson 2246 // C or F: long line to the left
277 persson 2169 cr->move_to(x + 0.5, h1 + 1);
278     cr->line_to(x + 0.5, h1 + h - 1);
279     cr->stroke();
280 persson 2246 }
281 persson 2151
282 persson 2246 if (key_pressed[i]) draw_key(cr, i);
283    
284     if (note == 3) draw_digit(cr, i);
285 persson 2169 }
286 persson 2246 }
287 schoenebeck 1225
288 persson 2151
289 persson 2246 void RegionChooser::draw_regions(const Cairo::RefPtr<Cairo::Context>& cr,
290     int clip_low, int clip_high) {
291     const int w = get_width() - 1;
292    
293     Gdk::Cairo::set_source_rgba(cr, black);
294     gig::Region* next_region;
295     int x3 = -1;
296     for (gig::Region* r = regions.first() ; r ; r = next_region) {
297     next_region = regions.next();
298    
299     if (x3 < 0) {
300     x3 = key_to_x(r->KeyRange.low, w);
301     if (x3 >= clip_high) break;
302     }
303     if (!next_region ||
304     r->KeyRange.high + 1 != next_region->KeyRange.low ||
305     r == region || next_region == region) {
306    
307     int x2 = key_to_x(r->KeyRange.high + 1, w);
308     if (x2 >= clip_low) {
309 persson 2169 cr->move_to(x3, 0.5);
310     cr->line_to(x2 + 0.5, 0.5);
311     cr->line_to(x2 + 0.5, h1 - 0.5);
312     cr->line_to(x3, h1 - 0.5);
313     cr->stroke();
314 persson 2151
315 persson 2246 Gdk::Cairo::set_source_rgba(cr, region == r ? red : white);
316 persson 2169 cr->rectangle(x3 + 1, 1, x2 - x3 - 1, h1 - 2);
317     cr->fill();
318     Gdk::Cairo::set_source_rgba(cr, black);
319 persson 2151 }
320 persson 2246 x3 = -1;
321 persson 2169 }
322 persson 2246 }
323 persson 2151
324 persson 2246 for (gig::Region* r = regions.first() ; r ; r = regions.next()) {
325     int x = key_to_x(r->KeyRange.low, w);
326 persson 2151
327 persson 2246 if (x < clip_low) continue;
328     if (x >= clip_high) break;
329    
330     cr->move_to(x + 0.5, 1);
331     cr->line_to(x + 0.5, h1 - 1);
332     cr->stroke();
333 schoenebeck 1225 }
334     }
335    
336 schoenebeck 1654 bool RegionChooser::is_black_key(int key) {
337     const int note = (key + 3) % 12;
338     return note == 1 || note == 4 || note == 6 || note == 9 || note == 11;
339     }
340 schoenebeck 1225
341 persson 2246 void RegionChooser::draw_digit(const Cairo::RefPtr<Cairo::Context>& cr,
342     int key) {
343 schoenebeck 1660 const int h = KEYBOARD_HEIGHT;
344 persson 1658 const int w = get_width() - 1;
345 persson 2246 Glib::RefPtr<Pango::Layout> layout =
346     Pango::Layout::create(get_pango_context());
347 persson 1658 char buf[30];
348     sprintf(buf, "<span size=\"8000\">%d</span>", key / 12 - 1);
349     layout->set_markup(buf);
350     Pango::Rectangle rectangle = layout->get_logical_extents();
351     double text_w = double(rectangle.get_width()) / Pango::SCALE;
352     double text_h = double(rectangle.get_height()) / Pango::SCALE;
353     double x = w * (key + 0.75) / 128.0;
354 persson 2169 Gdk::Cairo::set_source_rgba(cr, black);
355 persson 2151 cr->move_to(int(x - text_w / 2 + 1), int(h1 + h - text_h + 0.5));
356     #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 16) || GTKMM_MAJOR_VERSION < 2
357     pango_cairo_show_layout(cr->cobj(), layout->gobj());
358     #else
359     layout->show_in_cairo_context(cr);
360     #endif
361 persson 1658 }
362    
363 persson 2246 void RegionChooser::draw_key(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 schoenebeck 1225 const int bh = int(h * 0.55);
368    
369 persson 2246 Gdk::Cairo::set_source_rgba(cr, activeKeyColor);
370 schoenebeck 1225
371 persson 2151 int note = (key + 3) % 12;
372 persson 2246 int x = key_to_x(key, w) + 1;
373     int x2 = key_to_x(key + 1.5, w);
374     int x3 = key_to_x(key + 1, w);
375     int x4 = key_to_x(key - 0.5, w);
376 persson 2151 int w1 = x3 - x;
377     switch (note) {
378     case 0: case 5: case 10:
379     cr->rectangle(x, h1 + 1, w1, bh);
380     cr->fill();
381     cr->rectangle(x4 + 1, h1 + bh + 1, x2 - x4 - 1, h - bh - 2);
382     cr->fill();
383     break;
384     case 2: case 7:
385     cr->rectangle(x, h1 + 1, w1, bh);
386     cr->fill();
387     cr->rectangle(x4 + 1, h1 + bh + 1, x3 - x4 - 1, h - bh - 2);
388     cr->fill();
389     break;
390     case 3: case 8:
391     cr->rectangle(x, h1 + 1, w1, bh);
392     cr->fill();
393     cr->rectangle(x, h1 + bh + 1, x2 - x, h - bh - 2);
394     cr->fill();
395     break;
396     default:
397     cr->rectangle(x, h1 + 1, w1, bh - 1);
398     cr->fill();
399     break;
400 schoenebeck 1225 }
401 persson 2246 Gdk::Cairo::set_source_rgba(cr, black);
402 schoenebeck 1225 }
403    
404     void RegionChooser::set_instrument(gig::Instrument* instrument)
405     {
406     this->instrument = instrument;
407 persson 1623 regions.update(instrument);
408     region = regions.first();
409 schoenebeck 1225 queue_draw();
410 persson 1261 region_selected();
411 persson 1677 dimensionManager.set_region(region);
412 schoenebeck 1225 }
413    
414     bool RegionChooser::on_button_release_event(GdkEventButton* event)
415     {
416 persson 2246 const int k = x_to_key(event->x, get_width() - 1);
417 schoenebeck 1660
418 schoenebeck 1661 // handle-note off on virtual keyboard
419 schoenebeck 1660 if (event->type == GDK_BUTTON_RELEASE) {
420 schoenebeck 1661 int velocity = (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
421     int(float(event->y - REGION_BLOCK_HEIGHT) / float(KEYBOARD_HEIGHT) * 128.0f) + 1;
422     if (velocity <= 0) velocity = 1;
423     switch (m_VirtKeybModeChoice.get_value()) {
424     case VIRT_KEYBOARD_MODE_CHORD:
425     if (event->y >= REGION_BLOCK_HEIGHT)
426     keyboard_key_released_signal.emit(k, velocity);
427     break;
428     case VIRT_KEYBOARD_MODE_NORMAL:
429     default:
430     if (currentActiveKey >= 0 && currentActiveKey <= 127) {
431     keyboard_key_released_signal.emit(currentActiveKey, velocity);
432     currentActiveKey = -1;
433     }
434     break;
435 schoenebeck 1660 }
436     }
437    
438 schoenebeck 1225 if (resize.active) {
439 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
440 schoenebeck 1225 get_window()->pointer_ungrab(event->time);
441 persson 2169 #else
442     Glib::wrap(event->device, true)->ungrab(event->time);
443     #endif
444 schoenebeck 1225 resize.active = false;
445    
446     if (!is_in_resize_zone(event->x, event->y) && cursor_is_resize) {
447     get_window()->set_cursor();
448     cursor_is_resize = false;
449     }
450 persson 1262 } else if (move.active) {
451 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
452 persson 1262 get_window()->pointer_ungrab(event->time);
453 persson 2169 #else
454     Glib::wrap(event->device, true)->ungrab(event->time);
455     #endif
456 persson 1262 move.active = false;
457    
458     if (is_in_resize_zone(event->x, event->y)) {
459 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
460 persson 1262 get_window()->set_cursor(Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW));
461 persson 2169 #else
462     get_window()->set_cursor(Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW));
463     #endif
464 persson 1262 cursor_is_resize = true;
465     }
466 schoenebeck 1225 }
467     return true;
468     }
469    
470 persson 2246 void RegionChooser::update_after_resize()
471     {
472     if (resize.mode == resize.moving_high_limit) {
473     if (resize.region->KeyRange.high != resize.pos - 1) {
474     instrument_struct_to_be_changed_signal.emit(instrument);
475     resize.region->SetKeyRange(resize.region->KeyRange.low,
476     resize.pos - 1);
477     regions.update(instrument);
478     instrument_changed.emit();
479     instrument_struct_changed_signal.emit(instrument);
480     }
481     } else if (resize.mode == resize.moving_low_limit) {
482     if (resize.region->KeyRange.low != resize.pos) {
483     instrument_struct_to_be_changed_signal.emit(instrument);
484     resize.region->SetKeyRange(resize.pos,
485     resize.region->KeyRange.high);
486     regions.update(instrument);
487     instrument_changed.emit();
488     instrument_struct_changed_signal.emit(instrument);
489     }
490     }
491     }
492    
493     void RegionChooser::update_after_move(int pos)
494     {
495     instrument_struct_to_be_changed_signal.emit(instrument);
496     region->SetKeyRange(pos, pos + region->KeyRange.high -
497     region->KeyRange.low);
498     regions.update(instrument);
499     instrument_changed.emit();
500     instrument_struct_changed_signal.emit(instrument);
501     }
502    
503 schoenebeck 1225 bool RegionChooser::on_button_press_event(GdkEventButton* event)
504     {
505     if (!instrument) return true;
506    
507 persson 2246 const int w = get_width() - 1;
508     const int k = x_to_key(event->x, w);
509 schoenebeck 1225
510 schoenebeck 1660 if (event->type == GDK_BUTTON_PRESS) {
511     if (event->y >= REGION_BLOCK_HEIGHT) {
512     int velocity = (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
513     int(float(event->y - REGION_BLOCK_HEIGHT) / float(KEYBOARD_HEIGHT) * 128.0f) + 1;
514 schoenebeck 1661 currentActiveKey = k;
515 schoenebeck 1660 keyboard_key_hit_signal.emit(k, velocity);
516     }
517     }
518    
519     if (event->y >= REGION_BLOCK_HEIGHT) return true;
520 schoenebeck 1225 if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
521     gig::Region* r = get_region(k);
522     if (r) {
523     region = r;
524     queue_draw();
525 persson 1261 region_selected();
526 persson 1677 dimensionManager.set_region(region);
527 schoenebeck 1225 popup_menu_inside_region->popup(event->button, event->time);
528     } else {
529     new_region_pos = k;
530     popup_menu_outside_region->popup(event->button, event->time);
531     }
532     } else {
533     if (is_in_resize_zone(event->x, event->y)) {
534 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
535 schoenebeck 1225 get_window()->pointer_grab(false,
536     Gdk::BUTTON_RELEASE_MASK |
537     Gdk::POINTER_MOTION_MASK |
538     Gdk::POINTER_MOTION_HINT_MASK,
539 persson 2169 Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW),
540     event->time);
541     #else
542     Glib::wrap(event->device, true)->grab(get_window(),
543     Gdk::OWNERSHIP_NONE,
544     false,
545     Gdk::BUTTON_RELEASE_MASK |
546     Gdk::POINTER_MOTION_MASK |
547     Gdk::POINTER_MOTION_HINT_MASK,
548     Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW),
549     event->time);
550     #endif
551 schoenebeck 1225 resize.active = true;
552     } else {
553     gig::Region* r = get_region(k);
554     if (r) {
555     region = r;
556     queue_draw();
557 persson 1261 region_selected();
558 persson 1677 dimensionManager.set_region(region);
559 persson 1262
560 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
561 persson 1262 get_window()->pointer_grab(false,
562     Gdk::BUTTON_RELEASE_MASK |
563     Gdk::POINTER_MOTION_MASK |
564     Gdk::POINTER_MOTION_HINT_MASK,
565 persson 2169 Gdk::Cursor(Gdk::FLEUR),
566     event->time);
567     #else
568     Glib::wrap(event->device, true)->grab(get_window(),
569     Gdk::OWNERSHIP_NONE,
570     false,
571     Gdk::BUTTON_RELEASE_MASK |
572     Gdk::POINTER_MOTION_MASK |
573     Gdk::POINTER_MOTION_HINT_MASK,
574     Gdk::Cursor::create(Gdk::FLEUR),
575     event->time);
576     #endif
577 persson 1262 move.active = true;
578 persson 2246 move.offset = event->x - key_to_x(region->KeyRange.low, w);
579 schoenebeck 1225 }
580     }
581     }
582     return true;
583     }
584    
585     gig::Region* RegionChooser::get_region(int key)
586     {
587 persson 2246 for (gig::Region* r = regions.first() ; r ; r = regions.next()) {
588 schoenebeck 1225 if (key < r->KeyRange.low) return 0;
589 persson 2246 if (key <= r->KeyRange.high) return r;
590 schoenebeck 1225 }
591     return 0;
592     }
593    
594 persson 1262 void RegionChooser::motion_resize_region(int x, int y)
595 schoenebeck 1225 {
596 persson 1623 const int w = get_width() - 1;
597 schoenebeck 1225
598 persson 1262 int k = int(double(x) / w * 128.0 + 0.5);
599 schoenebeck 1225
600 persson 1262 if (k < resize.min) k = resize.min;
601     else if (k > resize.max) k = resize.max;
602    
603     if (k != resize.pos) {
604     if (resize.mode == resize.undecided) {
605     if (k < resize.pos) {
606     // edit high limit of prev_region
607     resize.max = resize.region->KeyRange.low;
608     resize.region = resize.prev_region;
609     resize.mode = resize.moving_high_limit;
610     } else {
611     // edit low limit of region
612     resize.min = resize.prev_region->KeyRange.high + 1;
613     resize.mode = resize.moving_low_limit;
614 schoenebeck 1225 }
615 persson 1262 }
616 persson 2246 resize.pos = k;
617 persson 2151
618 persson 2246 int x1, x2;
619 persson 1262 if (resize.mode == resize.moving_high_limit) {
620 persson 2246 if (resize.region->KeyRange.high < resize.pos - 1) {
621     x1 = resize.region->KeyRange.high;
622     x2 = resize.pos - 1;
623 persson 1262 } else {
624 persson 2246 x1 = resize.pos - 1;
625     x2 = resize.region->KeyRange.high;
626 schoenebeck 1225 }
627 persson 1262 } else {
628 persson 2246 if (resize.region->KeyRange.low < resize.pos) {
629     x1 = resize.region->KeyRange.low;
630     x2 = resize.pos;
631 persson 1262 } else {
632 persson 2246 x1 = resize.pos;
633     x2 = resize.region->KeyRange.low;
634 persson 1262 }
635     }
636 persson 2246 x1 = key_to_x(x1, w);
637     x2 = key_to_x(x2 + 1, w) + 1;
638     Gdk::Rectangle rect(x1, 0, x2 - x1, h1);
639    
640     update_after_resize();
641    
642     get_window()->invalidate_rect(rect, false);
643 persson 1262 }
644     }
645 schoenebeck 1225
646 persson 1262 void RegionChooser::motion_move_region(int x, int y)
647     {
648 persson 1623 const int w = get_width() - 1;
649 persson 1262
650 persson 2246 int l = int(double(x - move.offset) / w * 128.0 + 0.5);
651    
652     if (l == region->KeyRange.low) return;
653     int new_l;
654     int regionsize = region->KeyRange.high - region->KeyRange.low;
655 persson 1262 int a = 0;
656 persson 2246 if (l > region->KeyRange.low) {
657 persson 1623 for (gig::Region* r = regions.first() ; ; r = regions.next()) {
658 persson 1262 if (r != region) {
659     int b = r ? r->KeyRange.low : 128;
660    
661     // gap: from a to b (not inclusive b)
662    
663 persson 2246 if (region->KeyRange.high >= b) {
664 persson 1262 // not found the current gap yet, just continue
665 schoenebeck 1225 } else {
666 persson 1262
667 persson 2246 if (a > l) {
668 persson 1262 // this gap is too far to the right, break
669     break;
670     }
671    
672 persson 2246 int newhigh = std::min(l + regionsize, b - 1);
673     int newlo = newhigh - regionsize;
674 persson 1262
675     if (newlo >= a) {
676     // yes it fits - it's a candidate
677 persson 2246 new_l = newlo;
678 persson 1262 }
679 schoenebeck 1225 }
680 persson 1262 if (!r) break;
681     a = r->KeyRange.high + 1;
682     }
683     }
684     } else {
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 (l + regionsize >= b) {
692 persson 1262 // not found the current gap yet, just continue
693 schoenebeck 1225 } else {
694 persson 1262
695 persson 2246 if (a > region->KeyRange.low) {
696 persson 1262 // this gap is too far to the right, break
697     break;
698     }
699    
700 persson 2246 int newlo = std::max(l, a);
701     int newhigh = newlo + regionsize;
702 persson 1262
703     if (newhigh < b) {
704     // yes it fits - break as the first one is the best
705 persson 2246 new_l = newlo;
706 persson 1262 break;
707     }
708 schoenebeck 1225 }
709 persson 1262 if (!r) break;
710     a = r->KeyRange.high + 1;
711 schoenebeck 1225 }
712     }
713 persson 1262 }
714 persson 2246 if (new_l == region->KeyRange.low) return;
715 persson 1262
716 persson 2246 int x1 = key_to_x(std::min(int(region->KeyRange.low), new_l), w);
717     int x2 = key_to_x(std::max(int(region->KeyRange.high),
718     new_l + regionsize) + 1, w) + 1;
719 persson 2169
720 persson 2246 Gdk::Rectangle rect(x1, 0, x2 - x1, h1);
721     update_after_move(new_l);
722 persson 1262
723 persson 2246 get_window()->invalidate_rect(rect, false);
724 persson 1262 }
725    
726    
727     bool RegionChooser::on_motion_notify_event(GdkEventMotion* event)
728     {
729     Glib::RefPtr<Gdk::Window> window = get_window();
730     int x, y;
731     Gdk::ModifierType state = Gdk::ModifierType(0);
732     window->get_pointer(x, y, state);
733    
734 schoenebeck 1661 // handle virtual MIDI keyboard
735     if (m_VirtKeybModeChoice.get_value() != VIRT_KEYBOARD_MODE_CHORD &&
736     currentActiveKey > 0 &&
737     event->y >= REGION_BLOCK_HEIGHT &&
738     event->y < REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT)
739     {
740 persson 2246 const int k = x_to_key(event->x, get_width() - 1);
741 persson 1898 if (k != currentActiveKey) {
742     int velocity =
743     (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
744     int(float(event->y - REGION_BLOCK_HEIGHT) /
745     float(KEYBOARD_HEIGHT) * 128.0f) + 1;
746     if (velocity <= 0) velocity = 1;
747     keyboard_key_released_signal.emit(currentActiveKey, velocity);
748     currentActiveKey = k;
749     keyboard_key_hit_signal.emit(k, velocity);
750     }
751 schoenebeck 1661 }
752    
753 persson 1262 if (resize.active) {
754     motion_resize_region(x, y);
755     } else if (move.active) {
756     motion_move_region(x, y);
757     } else {
758 schoenebeck 1225 if (is_in_resize_zone(x, y)) {
759     if (!cursor_is_resize) {
760 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
761 persson 1262 window->set_cursor(Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW));
762 persson 2169 #else
763     window->set_cursor(Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW));
764     #endif
765 schoenebeck 1225 cursor_is_resize = true;
766     }
767     } else if (cursor_is_resize) {
768     window->set_cursor();
769     cursor_is_resize = false;
770     }
771     }
772    
773     return true;
774     }
775    
776     bool RegionChooser::is_in_resize_zone(double x, double y) {
777 persson 1623 const int w = get_width() - 1;
778 schoenebeck 1225
779     if (instrument && y >= 0 && y <= h1) {
780     gig::Region* prev_region = 0;
781     gig::Region* next_region;
782 persson 1623 for (gig::Region* r = regions.first(); r ; r = next_region) {
783     next_region = regions.next();
784 schoenebeck 1225
785 persson 2246 int lo = key_to_x(r->KeyRange.low, w);
786 schoenebeck 1225 if (x <= lo - 2) break;
787     if (x < lo + 2) {
788     resize.region = r;
789     resize.pos = r->KeyRange.low;
790     resize.max = r->KeyRange.high;
791    
792     if (prev_region && prev_region->KeyRange.high + 1 == r->KeyRange.low) {
793     // we don't know yet if it's the high limit of
794     // prev_region or the low limit of r that's going
795     // to be edited
796     resize.mode = resize.undecided;
797     resize.min = prev_region->KeyRange.low + 1;
798     resize.prev_region = prev_region;
799 persson 1623 return resize.min != resize.max;
800 schoenebeck 1225 }
801    
802     // edit low limit
803     resize.mode = resize.moving_low_limit;
804     resize.min = prev_region ? prev_region->KeyRange.high + 1 : 0;
805 persson 1623 return resize.min != resize.max;
806 schoenebeck 1225 }
807     if (!next_region || r->KeyRange.high + 1 != next_region->KeyRange.low) {
808 persson 2246 int hi = key_to_x(r->KeyRange.high + 1, w);
809 schoenebeck 1225 if (x <= hi - 2) break;
810     if (x < hi + 2) {
811     // edit high limit
812     resize.region = r;
813     resize.pos = r->KeyRange.high + 1;
814     resize.mode = resize.moving_high_limit;
815     resize.min = r->KeyRange.low + 1;
816     resize.max = next_region ? next_region->KeyRange.low : 128;
817 persson 1623 return resize.min != resize.max;
818 schoenebeck 1225 }
819     }
820     prev_region = r;
821     }
822     }
823     return false;
824     }
825    
826 schoenebeck 1339 sigc::signal<void>& RegionChooser::signal_region_selected()
827 schoenebeck 1225 {
828 persson 1261 return region_selected;
829 schoenebeck 1225 }
830    
831 schoenebeck 1339 sigc::signal<void>& RegionChooser::signal_instrument_changed()
832 persson 1261 {
833     return instrument_changed;
834     }
835    
836 schoenebeck 1225 void RegionChooser::show_region_properties()
837     {
838     if (!region) return;
839 persson 1831 Gtk::Dialog dialog(_("Region Properties"), true /*modal*/);
840 schoenebeck 1225 // add "Keygroup" checkbox
841 persson 1831 Gtk::CheckButton checkBoxKeygroup(_("Member of a Keygroup (Exclusive Group)"));
842 schoenebeck 1225 checkBoxKeygroup.set_active(region->KeyGroup);
843     dialog.get_vbox()->pack_start(checkBoxKeygroup);
844     checkBoxKeygroup.show();
845     // add "Keygroup" spinbox
846 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
847     Gtk::Adjustment adjustment(1, 1, 999);
848 schoenebeck 1225 Gtk::SpinButton spinBox(adjustment);
849 persson 2169 #else
850     Gtk::SpinButton spinBox(Gtk::Adjustment::create(1, 1, 999));
851     #endif
852 schoenebeck 1225 if (region->KeyGroup) spinBox.set_value(region->KeyGroup);
853     dialog.get_vbox()->pack_start(spinBox);
854     spinBox.show();
855     // add OK and CANCEL buttons to the dialog
856     dialog.add_button(Gtk::Stock::OK, 0);
857     dialog.add_button(Gtk::Stock::CANCEL, 1);
858     dialog.show_all_children();
859     if (!dialog.run()) { // OK selected ...
860     region->KeyGroup =
861     (checkBoxKeygroup.get_active()) ? spinBox.get_value_as_int() : 0;
862     }
863     }
864    
865     void RegionChooser::add_region()
866     {
867 schoenebeck 1322 instrument_struct_to_be_changed_signal.emit(instrument);
868    
869 schoenebeck 1225 region = instrument->AddRegion();
870 schoenebeck 1336 region->SetKeyRange(new_region_pos, new_region_pos);
871 schoenebeck 1225
872 schoenebeck 1322 instrument_struct_changed_signal.emit(instrument);
873 persson 1623 regions.update(instrument);
874 schoenebeck 1322
875 schoenebeck 1225 queue_draw();
876 persson 1261 region_selected();
877 persson 1677 dimensionManager.set_region(region);
878 persson 1261 instrument_changed();
879 schoenebeck 1225 }
880    
881     void RegionChooser::delete_region()
882     {
883 schoenebeck 1322 instrument_struct_to_be_changed_signal.emit(instrument);
884 schoenebeck 1225 instrument->DeleteRegion(region);
885 schoenebeck 1322 instrument_struct_changed_signal.emit(instrument);
886 persson 1623 regions.update(instrument);
887 schoenebeck 1322
888 schoenebeck 1225 region = 0;
889     queue_draw();
890 persson 1261 region_selected();
891 persson 1677 dimensionManager.set_region(region);
892 persson 1261 instrument_changed();
893 schoenebeck 1225 }
894    
895     void RegionChooser::manage_dimensions()
896     {
897     gig::Region* region = get_region();
898     if (!region) return;
899     dimensionManager.show(region);
900     }
901    
902     void RegionChooser::on_dimension_manager_changed() {
903 persson 1261 region_selected();
904     instrument_changed();
905 schoenebeck 1225 }
906 schoenebeck 1322
907 schoenebeck 1339 sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_to_be_changed() {
908 schoenebeck 1322 return instrument_struct_to_be_changed_signal;
909     }
910    
911 schoenebeck 1339 sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_changed() {
912 schoenebeck 1322 return instrument_struct_changed_signal;
913     }
914    
915 schoenebeck 1339 sigc::signal<void, gig::Region*>& RegionChooser::signal_region_to_be_changed() {
916 schoenebeck 1322 return region_to_be_changed_signal;
917     }
918    
919 schoenebeck 1339 sigc::signal<void, gig::Region*>& RegionChooser::signal_region_changed_signal() {
920 schoenebeck 1322 return region_changed_signal;
921     }
922 schoenebeck 1660
923     sigc::signal<void, int/*key*/, int/*velocity*/>& RegionChooser::signal_keyboard_key_hit() {
924     return keyboard_key_hit_signal;
925     }
926    
927     sigc::signal<void, int/*key*/, int/*velocity*/>& RegionChooser::signal_keyboard_key_released() {
928     return keyboard_key_released_signal;
929     }

  ViewVC Help
Powered by ViewVC