/[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 2536 - (hide annotations) (download)
Mon Apr 21 17:49:17 2014 UTC (9 years, 11 months ago) by schoenebeck
File size: 32830 byte(s)
* if there is no region yet, show a red hint text to the user that he may
  right click on the region chooser area to add a region
* added tooltips to main menu entries (was buggy before)
* added tooltips to instruments tree view and samples tree view
* added various tooltips and adjusted some labels on the region settings
  pane

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

  ViewVC Help
Powered by ViewVC