/[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 2151 - (hide annotations) (download)
Sun Nov 21 12:38:41 2010 UTC (13 years, 5 months ago) by persson
File size: 33886 byte(s)
* use Cairo instead of deprecated gdk drawing primitives
* avoid deprecated gtk methods when using newer gtk versions
* raised minimum supported gtkmm version to 2.8

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

  ViewVC Help
Powered by ViewVC