/[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 2246 - (hide annotations) (download)
Fri Aug 19 10:55:41 2011 UTC (12 years, 7 months ago) by persson
File size: 32008 byte(s)
* gtkmm 3 fix: rewrote the custom widgets (regionchooser and
  dimregionchooser) so they only draw pixels in the on_draw
  method. This should make them work again in newer gtkmm 3
  environments.

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

  ViewVC Help
Powered by ViewVC