/[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 2169 - (hide annotations) (download)
Sun Mar 6 07:51:04 2011 UTC (13 years, 1 month ago) by persson
File size: 35276 byte(s)
* ported to gtkmm 3, keeping compatibility with gtkmm 2

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

  ViewVC Help
Powered by ViewVC