/[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 1660 - (hide annotations) (download)
Sun Feb 3 00:19:55 2008 UTC (16 years, 2 months ago) by schoenebeck
File size: 28208 byte(s)
* call it virtually baby: the keyboard finally can trigger notes on
  sampler side (only in live-mode of course)
* added a red cross on top of the detached-mode icon to make
  it more obvious

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

  ViewVC Help
Powered by ViewVC