/[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 1898 - (hide annotations) (download)
Sun May 10 09:35:56 2009 UTC (14 years, 10 months ago) by persson
File size: 31345 byte(s)
* Windows: look for translations using base directory of libgigedit
  dll
* virtual keyboard fixes: restore to grey when outside keyboard. Don't
  trigger multiple notes for each key when moving mouse.

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

  ViewVC Help
Powered by ViewVC