1 |
/* |
/* |
2 |
* Copyright (C) 2006, 2007 Andreas Persson |
* Copyright (C) 2006-2017 Andreas Persson |
3 |
* |
* |
4 |
* This program is free software; you can redistribute it and/or |
* This program is free software; you can redistribute it and/or |
5 |
* modify it under the terms of the GNU General Public License as |
* modify it under the terms of the GNU General Public License as |
17 |
* 02110-1301 USA. |
* 02110-1301 USA. |
18 |
*/ |
*/ |
19 |
|
|
20 |
|
#include "global.h" |
21 |
#include "regionchooser.h" |
#include "regionchooser.h" |
22 |
|
|
23 |
|
#include <algorithm> |
24 |
|
#include <assert.h> |
25 |
|
|
26 |
|
#include <cairomm/context.h> |
27 |
|
#include <gdkmm/general.h> |
28 |
#include <gdkmm/cursor.h> |
#include <gdkmm/cursor.h> |
29 |
#include <gtkmm/stock.h> |
#include <gtkmm/stock.h> |
30 |
|
#include <gdkmm/pixbuf.h> |
31 |
#include <gtkmm/spinbutton.h> |
#include <gtkmm/spinbutton.h> |
32 |
#include <gtkmm/dialog.h> |
#include <gtkmm/dialog.h> |
|
#include <libintl.h> |
|
|
#include <math.h> |
|
33 |
|
|
34 |
#define _(String) gettext(String) |
#include "Settings.h" |
35 |
|
#include "gfx/builtinpix.h" |
36 |
|
|
37 |
|
#define REGION_BLOCK_HEIGHT 30 |
38 |
|
#define KEYBOARD_HEIGHT 40 |
39 |
|
|
40 |
|
struct RegionFeatures { |
41 |
|
int sampleRefs; |
42 |
|
int loops; |
43 |
|
|
44 |
|
RegionFeatures() { |
45 |
|
sampleRefs = loops = 0; |
46 |
|
} |
47 |
|
}; |
48 |
|
|
49 |
|
static RegionFeatures regionFeatures(gig::Region* rgn) { |
50 |
|
RegionFeatures f; |
51 |
|
for (int i = 0; i < rgn->DimensionRegions; ++i) { |
52 |
|
gig::DimensionRegion* dr = rgn->pDimensionRegions[i]; |
53 |
|
if (dr->pSample) f.sampleRefs++; |
54 |
|
// the user doesn't care about loop if there is no valid sample reference |
55 |
|
if (dr->pSample && dr->SampleLoops) f.loops++; |
56 |
|
} |
57 |
|
return f; |
58 |
|
} |
59 |
|
|
60 |
RegionChooser::RegionChooser() |
void SortedRegions::update(gig::Instrument* instrument) { |
61 |
|
// Usually, the regions in a gig file are ordered after their key |
62 |
|
// range, but there are files where they are not. The |
63 |
|
// RegionChooser code needs a sorted list of regions. |
64 |
|
regions.clear(); |
65 |
|
if (instrument) { |
66 |
|
for (gig::Region* r = instrument->GetFirstRegion() ; |
67 |
|
r ; |
68 |
|
r = instrument->GetNextRegion()) { |
69 |
|
regions.push_back(r); |
70 |
|
} |
71 |
|
sort(regions.begin(), regions.end(), *this); |
72 |
|
} |
73 |
|
} |
74 |
|
|
75 |
|
gig::Region* SortedRegions::first() { |
76 |
|
region_iterator = regions.begin(); |
77 |
|
return region_iterator == regions.end() ? 0 : *region_iterator; |
78 |
|
} |
79 |
|
|
80 |
|
gig::Region* SortedRegions::next() { |
81 |
|
++region_iterator; |
82 |
|
return region_iterator == regions.end() ? 0 : *region_iterator; |
83 |
|
} |
84 |
|
|
85 |
|
|
86 |
|
|
87 |
|
RegionChooser::RegionChooser() : |
88 |
|
activeKeyColor("red"), |
89 |
|
blue("#4796ff"), |
90 |
|
grey1("grey69"), |
91 |
|
white("white"), |
92 |
|
black("black"), |
93 |
|
m_VirtKeybModeChoice(_("Virtual Keyboard Mode")), |
94 |
|
currentActiveKey(-1), |
95 |
|
modifyallregions(false) |
96 |
{ |
{ |
97 |
Glib::RefPtr<Gdk::Colormap> colormap = get_default_colormap(); |
set_size_request(500, KEYBOARD_HEIGHT + REGION_BLOCK_HEIGHT); |
98 |
|
|
99 |
|
loadBuiltInPix(); |
100 |
|
|
101 |
|
// create gray blue hatched pattern |
102 |
|
{ |
103 |
|
const int width = grayBlueHatchedPattern->get_width(); |
104 |
|
const int height = grayBlueHatchedPattern->get_height(); |
105 |
|
const int stride = grayBlueHatchedPattern->get_rowstride(); |
106 |
|
|
107 |
|
// manually convert from RGBA to ARGB |
108 |
|
this->grayBlueHatchedPatternARGB = grayBlueHatchedPattern->copy(); |
109 |
|
const int pixelSize = stride / width; |
110 |
|
const int totalPixels = width * height; |
111 |
|
assert(pixelSize == 4); |
112 |
|
unsigned char* ptr = this->grayBlueHatchedPatternARGB->get_pixels(); |
113 |
|
for (int iPixel = 0; iPixel < totalPixels; ++iPixel, ptr += pixelSize) { |
114 |
|
const unsigned char r = ptr[0]; |
115 |
|
const unsigned char g = ptr[1]; |
116 |
|
const unsigned char b = ptr[2]; |
117 |
|
const unsigned char a = ptr[3]; |
118 |
|
ptr[0] = b; |
119 |
|
ptr[1] = g; |
120 |
|
ptr[2] = r; |
121 |
|
ptr[3] = a; |
122 |
|
} |
123 |
|
|
124 |
|
Cairo::RefPtr<Cairo::ImageSurface> imageSurface = Cairo::ImageSurface::create( |
125 |
|
this->grayBlueHatchedPatternARGB->get_pixels(), Cairo::FORMAT_ARGB32, width, height, stride |
126 |
|
); |
127 |
|
this->grayBlueHatchedSurfacePattern = Cairo::SurfacePattern::create(imageSurface); |
128 |
|
this->grayBlueHatchedSurfacePattern->set_extend(Cairo::EXTEND_REPEAT); |
129 |
|
} |
130 |
|
|
|
black = Gdk::Color("black"); |
|
|
white = Gdk::Color("white"); |
|
|
red = Gdk::Color("#8070ff"); |
|
|
blue = Gdk::Color("#c098ff"); |
|
|
green = Gdk::Color("#a088ff"); |
|
|
grey1 = Gdk::Color("red"); |
|
|
|
|
|
colormap->alloc_color(black); |
|
|
colormap->alloc_color(white); |
|
|
colormap->alloc_color(red); |
|
|
colormap->alloc_color(blue); |
|
|
colormap->alloc_color(green); |
|
|
colormap->alloc_color(grey1); |
|
131 |
instrument = 0; |
instrument = 0; |
132 |
region = 0; |
region = 0; |
133 |
resize.active = false; |
resize.active = false; |
134 |
|
move.active = false; |
135 |
cursor_is_resize = false; |
cursor_is_resize = false; |
136 |
h1 = 20; |
h1 = REGION_BLOCK_HEIGHT; |
137 |
width = 800; |
|
138 |
|
// properties of the virtual keyboard |
139 |
|
{ |
140 |
|
const char* choices[] = { _("normal"), _("chord"), 0 }; |
141 |
|
static const virt_keyboard_mode_t values[] = { |
142 |
|
VIRT_KEYBOARD_MODE_NORMAL, |
143 |
|
VIRT_KEYBOARD_MODE_CHORD |
144 |
|
}; |
145 |
|
m_VirtKeybModeChoice.set_choices(choices, values); |
146 |
|
m_VirtKeybModeChoice.set_value(VIRT_KEYBOARD_MODE_NORMAL); |
147 |
|
} |
148 |
|
m_VirtKeybVelocityLabelDescr.set_text(_("Note-On Velocity:")); |
149 |
|
m_VirtKeybVelocityLabel.set_text("-"); |
150 |
|
m_VirtKeybOffVelocityLabelDescr.set_text(_("Note-Off Velocity:")); |
151 |
|
m_VirtKeybOffVelocityLabel.set_text("-"); |
152 |
|
m_VirtKeybPropsBox.pack_start(m_VirtKeybModeChoice.label, Gtk::PACK_SHRINK); |
153 |
|
m_VirtKeybPropsBox.pack_start(m_VirtKeybModeChoice.widget, Gtk::PACK_SHRINK); |
154 |
|
m_VirtKeybPropsBox.pack_start(m_VirtKeybVelocityLabelDescr, Gtk::PACK_SHRINK); |
155 |
|
m_VirtKeybPropsBox.pack_start(m_VirtKeybVelocityLabel, Gtk::PACK_SHRINK); |
156 |
|
m_VirtKeybPropsBox.pack_start(m_VirtKeybOffVelocityLabelDescr, Gtk::PACK_SHRINK); |
157 |
|
m_VirtKeybPropsBox.pack_start(m_VirtKeybOffVelocityLabel, Gtk::PACK_SHRINK); |
158 |
|
m_VirtKeybPropsBox.set_spacing(10); |
159 |
|
m_VirtKeybPropsBox.show(); |
160 |
|
for (int i = 0 ; i < 128 ; i++) key_pressed[i] = false; |
161 |
|
|
162 |
actionGroup = Gtk::ActionGroup::create(); |
actionGroup = Gtk::ActionGroup::create(); |
163 |
actionGroup->add(Gtk::Action::create("Properties", |
actionGroup->add(Gtk::Action::create("Properties", |
194 |
add_events(Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | |
add_events(Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | |
195 |
Gdk::POINTER_MOTION_MASK | Gdk::POINTER_MOTION_HINT_MASK); |
Gdk::POINTER_MOTION_MASK | Gdk::POINTER_MOTION_HINT_MASK); |
196 |
|
|
197 |
dimensionManager.articulation_changed_signal.connect( |
dimensionManager.region_to_be_changed_signal.connect( |
198 |
sigc::mem_fun(*this, &RegionChooser::on_dimension_manager_changed) |
region_to_be_changed_signal.make_slot() |
199 |
|
); |
200 |
|
dimensionManager.region_changed_signal.connect( |
201 |
|
region_changed_signal.make_slot() |
202 |
|
); |
203 |
|
dimensionManager.region_changed_signal.connect( |
204 |
|
sigc::hide( |
205 |
|
sigc::mem_fun(*this, &RegionChooser::on_dimension_manager_changed) |
206 |
|
) |
207 |
); |
); |
208 |
|
keyboard_key_hit_signal.connect( |
209 |
|
sigc::mem_fun(*this, &RegionChooser::on_note_on_event) |
210 |
|
); |
211 |
|
keyboard_key_released_signal.connect( |
212 |
|
sigc::mem_fun(*this, &RegionChooser::on_note_off_event) |
213 |
|
); |
214 |
|
set_tooltip_text(_("Right click here for adding new region. Use mouse pointer for moving (dragging) or resizing existing regions (by pointing at region's boundary). Right click on an existing region for more actions.")); |
215 |
} |
} |
216 |
|
|
217 |
RegionChooser::~RegionChooser() |
RegionChooser::~RegionChooser() |
218 |
{ |
{ |
219 |
} |
} |
220 |
|
|
221 |
void RegionChooser::on_realize() |
void RegionChooser::setModifyAllRegions(bool b) { |
222 |
{ |
modifyallregions = b; |
223 |
// We need to call the base on_realize() |
// redraw required parts |
224 |
Gtk::DrawingArea::on_realize(); |
queue_draw(); |
225 |
|
} |
226 |
|
|
227 |
// Now we can allocate any additional resources we need |
void RegionChooser::invalidate_key(int key) { |
228 |
Glib::RefPtr<Gdk::Window> window = get_window(); |
const int h = KEYBOARD_HEIGHT; |
229 |
gc = Gdk::GC::create(window); |
const int w = get_width() - 1; |
230 |
window->clear(); |
int x1 = key_to_x(key - 0.5, w); |
231 |
|
int x2 = key_to_x(key + 1.5, w); |
232 |
|
|
233 |
|
Gdk::Rectangle rect(x1 + 1, h1 + 1, x2 - x1 - 1, h - 2); |
234 |
|
get_window()->invalidate_rect(rect, false); |
235 |
} |
} |
236 |
|
|
237 |
bool RegionChooser::on_expose_event(GdkEventExpose* event) |
void RegionChooser::on_note_on_event(int key, int velocity) { |
238 |
{ |
key_pressed[key] = true; |
239 |
Glib::RefPtr<Gdk::Window> window = get_window(); |
invalidate_key(key); |
240 |
window->clear(); |
m_VirtKeybVelocityLabel.set_text(ToString(velocity)); |
241 |
const int h = 40; |
} |
242 |
const int w = width - 1; |
|
243 |
|
void RegionChooser::on_note_off_event(int key, int velocity) { |
244 |
|
key_pressed[key] = false; |
245 |
|
invalidate_key(key); |
246 |
|
m_VirtKeybOffVelocityLabel.set_text(ToString(velocity)); |
247 |
|
} |
248 |
|
|
249 |
|
|
250 |
|
#if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2 |
251 |
|
bool RegionChooser::on_expose_event(GdkEventExpose* e) { |
252 |
|
double clipx1 = e->area.x; |
253 |
|
double clipx2 = e->area.x + e->area.width; |
254 |
|
double clipy1 = e->area.y; |
255 |
|
double clipy2 = e->area.y + e->area.height; |
256 |
|
|
257 |
|
const Cairo::RefPtr<Cairo::Context>& cr = |
258 |
|
get_window()->create_cairo_context(); |
259 |
|
#if 0 |
260 |
|
} |
261 |
|
#endif |
262 |
|
#else |
263 |
|
bool RegionChooser::on_draw(const Cairo::RefPtr<Cairo::Context>& cr) { |
264 |
|
double clipx1, clipx2, clipy1, clipy2; |
265 |
|
cr->get_clip_extents(clipx1, clipy1, clipx2, clipy2); |
266 |
|
#endif |
267 |
|
|
268 |
|
cr->save(); |
269 |
|
cr->set_line_width(1); |
270 |
|
|
271 |
|
#if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2 |
272 |
|
const Gdk::Color bg = get_style()->get_bg(Gtk::STATE_NORMAL); |
273 |
|
#else |
274 |
|
const Gdk::RGBA bg = get_style_context()->get_background_color(); |
275 |
|
#endif |
276 |
|
Gdk::Cairo::set_source_rgba(cr, bg); |
277 |
|
cr->paint(); |
278 |
|
|
279 |
|
if (clipy2 > h1) { |
280 |
|
draw_keyboard(cr, clipx1, clipx2); |
281 |
|
} |
282 |
|
|
283 |
|
if (clipy1 < h1 && instrument) { |
284 |
|
draw_regions(cr, clipx1, clipx2); |
285 |
|
} |
286 |
|
|
287 |
|
cr->restore(); |
288 |
|
|
289 |
|
return true; |
290 |
|
} |
291 |
|
|
292 |
|
void RegionChooser::draw_keyboard(const Cairo::RefPtr<Cairo::Context>& cr, |
293 |
|
int clip_low, int clip_high) { |
294 |
|
const int h = KEYBOARD_HEIGHT; |
295 |
|
const int w = get_width() - 1; |
296 |
const int bh = int(h * 0.55); |
const int bh = int(h * 0.55); |
297 |
|
|
298 |
Glib::RefPtr<const Gdk::GC> black = get_style()->get_black_gc(); |
Gdk::Cairo::set_source_rgba(cr, black); |
299 |
Glib::RefPtr<const Gdk::GC> white = get_style()->get_white_gc(); |
cr->rectangle(0.5, h1 + 0.5, w, h - 1); |
300 |
|
cr->stroke(); |
301 |
|
|
302 |
|
int x1 = key_to_x(20.5, w); |
303 |
|
Gdk::Cairo::set_source_rgba(cr, grey1); |
304 |
|
cr->rectangle(1, h1 + 1, x1 - 1, h - 2); |
305 |
|
cr->fill(); |
306 |
|
|
307 |
|
int x2 = key_to_x(109.5, w); |
308 |
|
Gdk::Cairo::set_source_rgba(cr, white); |
309 |
|
cr->rectangle(x1 + 1, h1 + 1, x2 - x1 - 1, h - 2); |
310 |
|
cr->fill(); |
311 |
|
|
312 |
|
Gdk::Cairo::set_source_rgba(cr, grey1); |
313 |
|
cr->rectangle(x2 + 1, h1 + 1, w - x2 - 1, h - 2); |
314 |
|
cr->fill(); |
315 |
|
|
316 |
window->draw_rectangle(black, false, 0, h1, w, h - 1); |
Gdk::Cairo::set_source_rgba(cr, black); |
|
window->draw_rectangle(white, true, 1, h1 + 1, w - 1, h - 2); |
|
|
for (int i = 0 ; i < 128 ; i++) { |
|
|
int note = (i + 3) % 12; |
|
|
int x = int(w * i / 128.0 + 0.5); |
|
317 |
|
|
318 |
if (note == 1 || note == 4 || note == 6 || note == 9 || note == 11) { |
int clipkey1 = std::max(0, x_to_key_right(clip_low - 1, w)); |
319 |
int x2 = int(w * (i + 0.5) / 128.0 + 0.5); |
int clipkey2 = std::min(x_to_key_right(clip_high - 1, w) + 1, 128); |
|
window->draw_line(black, x2, h1 + bh, x2, h1 + h); |
|
320 |
|
|
321 |
int x3 = int(w * (i + 1) / 128.0 + 0.5); |
for (int i = clipkey1 ; i < clipkey2 ; i++) { |
322 |
window->draw_rectangle(black, true, x, h1 + 1, x3 - x + 1, bh); |
int note = (i + 3) % 12; |
323 |
|
int x = key_to_x(i, w); |
324 |
|
|
325 |
|
if (note == 1 || note == 4 || note == 6 || |
326 |
|
note == 9 || note == 11) { |
327 |
|
// black key: short line in the middle, with a rectangle |
328 |
|
// on top |
329 |
|
int x2 = key_to_x(i + 0.5, w); |
330 |
|
cr->move_to(x2 + 0.5, h1 + bh + 0.5); |
331 |
|
cr->line_to(x2 + 0.5, h1 + h - 1); |
332 |
|
cr->stroke(); |
333 |
|
|
334 |
|
int x3 = key_to_x(i + 1, w); |
335 |
|
cr->rectangle(x, h1 + 1, x3 - x + 1, bh); |
336 |
|
cr->fill(); |
337 |
} else if (note == 3 || note == 8) { |
} else if (note == 3 || note == 8) { |
338 |
window->draw_line(black, x, h1 + 1, x, h1 + h); |
// C or F: long line to the left |
339 |
|
cr->move_to(x + 0.5, h1 + 1); |
340 |
|
cr->line_to(x + 0.5, h1 + h - 1); |
341 |
|
cr->stroke(); |
342 |
} |
} |
343 |
|
|
344 |
|
if (key_pressed[i]) draw_key(cr, i); |
345 |
|
|
346 |
|
if (note == 3) draw_digit(cr, i); |
347 |
} |
} |
348 |
|
} |
349 |
|
|
|
if (instrument) { |
|
|
int i = 0; |
|
|
gig::Region *nextRegion; |
|
|
int x3 = -1; |
|
|
for (gig::Region *r = instrument->GetFirstRegion() ; |
|
|
r ; |
|
|
r = nextRegion) { |
|
350 |
|
|
351 |
if (x3 < 0) x3 = int(w * (r->KeyRange.low) / 128.0 + 0.5); |
void RegionChooser::draw_regions(const Cairo::RefPtr<Cairo::Context>& cr, |
352 |
nextRegion = instrument->GetNextRegion(); |
int clip_low, int clip_high) { |
353 |
if (!nextRegion || r->KeyRange.high + 1 != nextRegion->KeyRange.low) { |
const int w = get_width() - 1; |
354 |
int x2 = int(w * (r->KeyRange.high + 1) / 128.0 + 0.5); |
|
355 |
window->draw_line(black, x3, 0, x2, 0); |
Gdk::Cairo::set_source_rgba(cr, black); |
356 |
window->draw_line(black, x3, h1 - 1, x2, h1 - 1); |
gig::Region* next_region; |
357 |
window->draw_line(black, x2, 1, x2, h1 - 2); |
int x3 = -1; |
358 |
window->draw_rectangle(white, true, x3 + 1, 1, x2 - x3 - 1, h1 - 2); |
for (gig::Region* r = regions.first() ; r ; r = next_region) { |
359 |
x3 = -1; |
next_region = regions.next(); |
360 |
|
|
361 |
|
if (x3 < 0) { |
362 |
|
x3 = key_to_x(r->KeyRange.low, w); |
363 |
|
if (x3 >= clip_high) break; |
364 |
|
} |
365 |
|
if (!next_region || |
366 |
|
r->KeyRange.high + 1 != next_region->KeyRange.low || |
367 |
|
r == region || next_region == region) { |
368 |
|
|
369 |
|
int x2 = key_to_x(r->KeyRange.high + 1, w); |
370 |
|
if (x2 >= clip_low) { |
371 |
|
cr->move_to(x3, 0.5); |
372 |
|
cr->line_to(x2 + 0.5, 0.5); |
373 |
|
cr->line_to(x2 + 0.5, h1 - 0.5); |
374 |
|
cr->line_to(x3, h1 - 0.5); |
375 |
|
cr->stroke(); |
376 |
|
|
377 |
|
if (region == r) |
378 |
|
Gdk::Cairo::set_source_rgba(cr, blue); |
379 |
|
else if (modifyallregions) |
380 |
|
cr->set_source(grayBlueHatchedSurfacePattern); |
381 |
|
else |
382 |
|
Gdk::Cairo::set_source_rgba(cr, white); |
383 |
|
|
384 |
|
cr->rectangle(x3 + 1, 1, x2 - x3 - 1, h1 - 2); |
385 |
|
cr->fill(); |
386 |
|
Gdk::Cairo::set_source_rgba(cr, black); |
387 |
} |
} |
388 |
i++; |
x3 = -1; |
389 |
} |
} |
390 |
|
} |
391 |
|
|
392 |
for (gig::Region *r = instrument->GetFirstRegion() ; |
for (gig::Region* r = regions.first() ; r ; r = regions.next()) { |
393 |
r ; |
int x = key_to_x(r->KeyRange.low, w); |
394 |
r = instrument->GetNextRegion()) { |
int x2 = key_to_x(r->KeyRange.high + 1, w); |
395 |
int x = int(w * (r->KeyRange.low) / 128.0 + 0.5); |
|
396 |
window->draw_line(black, x, 1, x, h1 - 2); |
RegionFeatures features = regionFeatures(r); |
397 |
|
|
398 |
|
const bool bShowLoopSymbol = features.loops > 0; |
399 |
|
const bool bShowSampleRefSymbol = features.sampleRefs < r->DimensionRegions; |
400 |
|
if (bShowLoopSymbol || bShowSampleRefSymbol) { |
401 |
|
const int margin = 2; |
402 |
|
const int wRgn = x2 - x; |
403 |
|
//printf("x=%d x2=%d wRgn=%d\n", x, x2, wRgn); |
404 |
|
|
405 |
|
cr->save(); |
406 |
|
cr->set_line_width(1); |
407 |
|
cr->rectangle(x, 1, wRgn, h1 - 1); |
408 |
|
cr->clip(); |
409 |
|
if (bShowSampleRefSymbol) { |
410 |
|
const int wPic = 8; |
411 |
|
const int hPic = 8; |
412 |
|
Gdk::Cairo::set_source_pixbuf( |
413 |
|
cr, (features.sampleRefs) ? yellowDot : redDot, |
414 |
|
x + (wRgn-wPic)/2.f, |
415 |
|
(bShowLoopSymbol) ? margin : (h1-hPic)/2.f |
416 |
|
); |
417 |
|
cr->paint(); |
418 |
|
} |
419 |
|
if (bShowLoopSymbol) { |
420 |
|
const int wPic = 12; |
421 |
|
const int hPic = 14; |
422 |
|
Gdk::Cairo::set_source_pixbuf( |
423 |
|
cr, (features.loops == r->DimensionRegions) ? blackLoop : grayLoop, |
424 |
|
x + (wRgn-wPic)/2.f, |
425 |
|
(bShowSampleRefSymbol) ? h1 - hPic - margin : (h1-hPic)/2.f |
426 |
|
); |
427 |
|
cr->paint(); |
428 |
|
} |
429 |
|
cr->restore(); |
430 |
} |
} |
431 |
|
} |
432 |
|
|
433 |
if (region) { |
for (gig::Region* r = regions.first() ; r ; r = regions.next()) { |
434 |
int x1 = int(w * (region->KeyRange.low) / 128.0 + 0.5); |
int x = key_to_x(r->KeyRange.low, w); |
435 |
int x2 = int(w * (region->KeyRange.high + 1) / 128.0 + 0.5); |
|
436 |
gc->set_foreground(red); |
if (x < clip_low) continue; |
437 |
window->draw_rectangle(gc, true, x1 + 1, 1, x2 - x1 - 1, h1 - 2); |
if (x >= clip_high) break; |
438 |
} |
|
439 |
|
cr->move_to(x + 0.5, 1); |
440 |
|
cr->line_to(x + 0.5, h1 - 1); |
441 |
|
cr->stroke(); |
442 |
} |
} |
|
return true; |
|
|
} |
|
443 |
|
|
444 |
|
// if there is no region yet, show the user some hint text that he may |
445 |
|
// right click on this area to create a new region |
446 |
|
if (!regions.first()) { |
447 |
|
Glib::RefPtr<Pango::Context> context = get_pango_context(); |
448 |
|
Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create(context); |
449 |
|
layout->set_alignment(Pango::ALIGN_CENTER); |
450 |
|
layout->set_text(Glib::ustring("*** ") + _("Right click here to create a region.") + " ***"); |
451 |
|
layout->set_width(get_width() * Pango::SCALE); |
452 |
|
//layout->set_height(get_height() * Pango::SCALE); |
453 |
|
layout->set_spacing(10); |
454 |
|
Gdk::Cairo::set_source_rgba(cr, blue); |
455 |
|
// get the text dimensions |
456 |
|
int text_width, text_height; |
457 |
|
layout->get_pixel_size(text_width, text_height); |
458 |
|
cr->move_to(0, (REGION_BLOCK_HEIGHT - text_height) / 2); |
459 |
|
#if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 16) || GTKMM_MAJOR_VERSION < 2 |
460 |
|
pango_cairo_show_layout(cr->cobj(), layout->gobj()); |
461 |
|
#else |
462 |
|
layout->show_in_cairo_context(cr); |
463 |
|
#endif |
464 |
|
} |
465 |
|
} |
466 |
|
|
467 |
void RegionChooser::on_size_request(GtkRequisition* requisition) |
bool RegionChooser::is_black_key(int key) { |
468 |
{ |
const int note = (key + 3) % 12; |
469 |
*requisition = GtkRequisition(); |
return note == 1 || note == 4 || note == 6 || note == 9 || note == 11; |
|
requisition->height = 40 + 20; |
|
|
requisition->width = 500; |
|
470 |
} |
} |
471 |
|
|
472 |
|
void RegionChooser::draw_digit(const Cairo::RefPtr<Cairo::Context>& cr, |
473 |
|
int key) { |
474 |
|
const int h = KEYBOARD_HEIGHT; |
475 |
|
const int w = get_width() - 1; |
476 |
|
Glib::RefPtr<Pango::Layout> layout = |
477 |
|
Pango::Layout::create(get_pango_context()); |
478 |
|
char buf[30]; |
479 |
|
sprintf(buf, "<span size=\"8000\">%d</span>", key / 12 - 1); |
480 |
|
layout->set_markup(buf); |
481 |
|
Pango::Rectangle rectangle = layout->get_logical_extents(); |
482 |
|
double text_w = double(rectangle.get_width()) / Pango::SCALE; |
483 |
|
double text_h = double(rectangle.get_height()) / Pango::SCALE; |
484 |
|
double x = w * (key + 0.75) / 128.0; |
485 |
|
Gdk::Cairo::set_source_rgba(cr, black); |
486 |
|
cr->move_to(int(x - text_w / 2 + 1), int(h1 + h - text_h + 0.5)); |
487 |
|
#if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 16) || GTKMM_MAJOR_VERSION < 2 |
488 |
|
pango_cairo_show_layout(cr->cobj(), layout->gobj()); |
489 |
|
#else |
490 |
|
layout->show_in_cairo_context(cr); |
491 |
|
#endif |
492 |
|
} |
493 |
|
|
494 |
// not used |
void RegionChooser::draw_key(const Cairo::RefPtr<Cairo::Context>& cr, |
495 |
void RegionChooser::draw_region(int from, int to, const Gdk::Color& color) |
int key) { |
496 |
{ |
const int h = KEYBOARD_HEIGHT; |
497 |
const int h = 40; |
const int w = get_width() - 1; |
|
const int w = width; |
|
498 |
const int bh = int(h * 0.55); |
const int bh = int(h * 0.55); |
499 |
|
|
500 |
Glib::RefPtr<Gdk::Window> window = get_window(); |
Gdk::Cairo::set_source_rgba(cr, activeKeyColor); |
|
gc->set_foreground(color); |
|
501 |
|
|
502 |
for (int i = from ; i < to ; i++) { |
int note = (key + 3) % 12; |
503 |
int note = (i + 3) % 12; |
int x = key_to_x(key, w) + 1; |
504 |
int x = int(w * i / 128.0 + 0.5) + 1; |
int x2 = key_to_x(key + 1.5, w); |
505 |
int x2 = int(w * (i + 1.5) / 128.0 + 0.5); |
int x3 = key_to_x(key + 1, w); |
506 |
int x3 = int(w * (i + 1) / 128.0 + 0.5); |
int x4 = key_to_x(key - 0.5, w); |
507 |
int x4 = int(w * (i - 0.5) / 128 + 0.5) + 1; |
int w1 = x3 - x; |
508 |
int w1 = x3 - x; |
switch (note) { |
509 |
switch (note) { |
case 0: case 5: case 10: |
510 |
case 0: case 5: case 10: |
cr->rectangle(x, h1 + 1, w1, bh); |
511 |
window->draw_rectangle(gc, true, x, h1 + 1, w1, bh); |
cr->fill(); |
512 |
window->draw_rectangle(gc, true, x4, h1 + bh + 1, x2 - x4, h - bh - 2); |
cr->rectangle(x4 + 1, h1 + bh + 1, x2 - x4 - 1, h - bh - 2); |
513 |
break; |
cr->fill(); |
514 |
case 2: case 7: |
break; |
515 |
window->draw_rectangle(gc, true, x, h1 + 1, w1, bh); |
case 2: case 7: |
516 |
window->draw_rectangle(gc, true, x4, h1 + bh + 1, x3 - x4, h - bh - 2); |
cr->rectangle(x, h1 + 1, w1, bh); |
517 |
break; |
cr->fill(); |
518 |
case 3: case 8: |
cr->rectangle(x4 + 1, h1 + bh + 1, x3 - x4 - 1, h - bh - 2); |
519 |
window->draw_rectangle(gc, true, x, h1 + 1, w1, bh); |
cr->fill(); |
520 |
window->draw_rectangle(gc, true, x, h1 + bh + 1, x2 - x, h - bh - 2); |
break; |
521 |
break; |
case 3: case 8: |
522 |
default: |
cr->rectangle(x, h1 + 1, w1, bh); |
523 |
window->draw_rectangle(gc, true, x, h1 + 1, w1, bh - 1); |
cr->fill(); |
524 |
break; |
cr->rectangle(x, h1 + bh + 1, x2 - x, h - bh - 2); |
525 |
} |
cr->fill(); |
526 |
|
break; |
527 |
|
default: |
528 |
|
cr->rectangle(x, h1 + 1, w1, bh - 1); |
529 |
|
cr->fill(); |
530 |
|
break; |
531 |
} |
} |
532 |
|
Gdk::Cairo::set_source_rgba(cr, black); |
533 |
} |
} |
534 |
|
|
535 |
void RegionChooser::set_instrument(gig::Instrument* instrument) |
void RegionChooser::set_instrument(gig::Instrument* instrument) |
536 |
{ |
{ |
537 |
this->instrument = instrument; |
this->instrument = instrument; |
538 |
region = instrument ? instrument->GetFirstRegion() : 0; |
regions.update(instrument); |
539 |
|
region = regions.first(); |
540 |
queue_draw(); |
queue_draw(); |
541 |
region_selected(); |
region_selected(); |
542 |
|
dimensionManager.set_region(region); |
543 |
} |
} |
544 |
|
|
545 |
bool RegionChooser::on_button_release_event(GdkEventButton* event) |
bool RegionChooser::on_button_release_event(GdkEventButton* event) |
546 |
{ |
{ |
547 |
|
const int k = x_to_key(event->x, get_width() - 1); |
548 |
|
|
549 |
|
// handle-note off on virtual keyboard |
550 |
|
if (event->type == GDK_BUTTON_RELEASE) { |
551 |
|
int velocity = (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 : |
552 |
|
int(float(event->y - REGION_BLOCK_HEIGHT) / float(KEYBOARD_HEIGHT) * 128.0f) + 1; |
553 |
|
if (velocity <= 0) velocity = 1; |
554 |
|
switch (m_VirtKeybModeChoice.get_value()) { |
555 |
|
case VIRT_KEYBOARD_MODE_CHORD: |
556 |
|
if (event->y >= REGION_BLOCK_HEIGHT) |
557 |
|
keyboard_key_released_signal.emit(k, velocity); |
558 |
|
break; |
559 |
|
case VIRT_KEYBOARD_MODE_NORMAL: |
560 |
|
default: |
561 |
|
if (currentActiveKey >= 0 && currentActiveKey <= 127) { |
562 |
|
keyboard_key_released_signal.emit(currentActiveKey, velocity); |
563 |
|
currentActiveKey = -1; |
564 |
|
} |
565 |
|
break; |
566 |
|
} |
567 |
|
} |
568 |
|
|
569 |
if (resize.active) { |
if (resize.active) { |
570 |
|
#if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2 |
571 |
get_window()->pointer_ungrab(event->time); |
get_window()->pointer_ungrab(event->time); |
572 |
|
#else |
573 |
|
Glib::wrap(event->device, true)->ungrab(event->time); |
574 |
|
#endif |
575 |
resize.active = false; |
resize.active = false; |
576 |
|
|
|
if (resize.mode == resize.moving_high_limit) { |
|
|
if (resize.region->KeyRange.high != resize.pos - 1) { |
|
|
resize.region->KeyRange.high = resize.pos - 1; |
|
|
instrument_changed(); |
|
|
} |
|
|
} else if (resize.mode == resize.moving_low_limit) { |
|
|
if (resize.region->KeyRange.low != resize.pos) { |
|
|
resize.region->KeyRange.low = resize.pos; |
|
|
instrument_changed(); |
|
|
} |
|
|
} |
|
|
|
|
577 |
if (!is_in_resize_zone(event->x, event->y) && cursor_is_resize) { |
if (!is_in_resize_zone(event->x, event->y) && cursor_is_resize) { |
578 |
get_window()->set_cursor(); |
get_window()->set_cursor(); |
579 |
cursor_is_resize = false; |
cursor_is_resize = false; |
580 |
} |
} |
581 |
|
} else if (move.active) { |
582 |
|
#if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2 |
583 |
|
get_window()->pointer_ungrab(event->time); |
584 |
|
#else |
585 |
|
Glib::wrap(event->device, true)->ungrab(event->time); |
586 |
|
#endif |
587 |
|
move.active = false; |
588 |
|
|
589 |
|
if (is_in_resize_zone(event->x, event->y)) { |
590 |
|
#if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2 |
591 |
|
get_window()->set_cursor(Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW)); |
592 |
|
#else |
593 |
|
get_window()->set_cursor(Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW)); |
594 |
|
#endif |
595 |
|
cursor_is_resize = true; |
596 |
|
} |
597 |
} |
} |
598 |
return true; |
return true; |
599 |
} |
} |
600 |
|
|
601 |
|
void RegionChooser::update_after_resize() |
602 |
|
{ |
603 |
|
if (resize.mode == resize.moving_high_limit) { |
604 |
|
if (resize.region->KeyRange.high != resize.pos - 1) { |
605 |
|
instrument_struct_to_be_changed_signal.emit(instrument); |
606 |
|
resize.region->SetKeyRange(resize.region->KeyRange.low, |
607 |
|
resize.pos - 1); |
608 |
|
regions.update(instrument); |
609 |
|
instrument_changed.emit(); |
610 |
|
instrument_struct_changed_signal.emit(instrument); |
611 |
|
} |
612 |
|
} else if (resize.mode == resize.moving_low_limit) { |
613 |
|
if (resize.region->KeyRange.low != resize.pos) { |
614 |
|
instrument_struct_to_be_changed_signal.emit(instrument); |
615 |
|
resize.region->SetKeyRange(resize.pos, |
616 |
|
resize.region->KeyRange.high); |
617 |
|
regions.update(instrument); |
618 |
|
instrument_changed.emit(); |
619 |
|
instrument_struct_changed_signal.emit(instrument); |
620 |
|
} |
621 |
|
} |
622 |
|
} |
623 |
|
|
624 |
|
void RegionChooser::update_after_move(int pos) |
625 |
|
{ |
626 |
|
instrument_struct_to_be_changed_signal.emit(instrument); |
627 |
|
const int range = region->KeyRange.high - region->KeyRange.low; |
628 |
|
const int diff = pos - int(region->KeyRange.low); |
629 |
|
region->SetKeyRange(pos, pos + range); |
630 |
|
if (Settings::singleton()->moveRootNoteWithRegionMoved) { |
631 |
|
for (int i = 0; i < 256; ++i) { |
632 |
|
gig::DimensionRegion* dimrgn = region->pDimensionRegions[i]; |
633 |
|
if (!dimrgn || !dimrgn->pSample || !dimrgn->PitchTrack) continue; |
634 |
|
dimrgn->UnityNote += diff; |
635 |
|
} |
636 |
|
} |
637 |
|
regions.update(instrument); |
638 |
|
instrument_changed.emit(); |
639 |
|
instrument_struct_changed_signal.emit(instrument); |
640 |
|
} |
641 |
|
|
642 |
bool RegionChooser::on_button_press_event(GdkEventButton* event) |
bool RegionChooser::on_button_press_event(GdkEventButton* event) |
643 |
{ |
{ |
644 |
if (!instrument) return true; |
if (!instrument) return true; |
645 |
|
|
646 |
int k = int(event->x / (width - 1) * 128.0); |
const int w = get_width() - 1; |
647 |
|
const int k = x_to_key(event->x, w); |
648 |
|
|
649 |
|
if (event->type == GDK_BUTTON_PRESS) { |
650 |
|
if (event->y >= REGION_BLOCK_HEIGHT) { |
651 |
|
int velocity = (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 : |
652 |
|
int(float(event->y - REGION_BLOCK_HEIGHT) / float(KEYBOARD_HEIGHT) * 128.0f) + 1; |
653 |
|
currentActiveKey = k; |
654 |
|
keyboard_key_hit_signal.emit(k, velocity); |
655 |
|
} |
656 |
|
} |
657 |
|
|
658 |
|
// left mouse button double click |
659 |
|
if (event->type == GDK_2BUTTON_PRESS && event->button == 1) { |
660 |
|
if (event->y < REGION_BLOCK_HEIGHT) { |
661 |
|
// show dimension manager dialog for this region |
662 |
|
manage_dimensions(); |
663 |
|
} |
664 |
|
} |
665 |
|
|
666 |
|
if (event->y >= REGION_BLOCK_HEIGHT) return true; |
667 |
if (event->type == GDK_BUTTON_PRESS && event->button == 3) { |
if (event->type == GDK_BUTTON_PRESS && event->button == 3) { |
668 |
gig::Region* r = get_region(k); |
gig::Region* r = get_region(k); |
669 |
if (r) { |
if (r) { |
670 |
region = r; |
region = r; |
671 |
queue_draw(); |
queue_draw(); |
672 |
region_selected(); |
region_selected(); |
673 |
|
dimensionManager.set_region(region); |
674 |
popup_menu_inside_region->popup(event->button, event->time); |
popup_menu_inside_region->popup(event->button, event->time); |
675 |
} else { |
} else { |
676 |
new_region_pos = k; |
new_region_pos = k; |
678 |
} |
} |
679 |
} else { |
} else { |
680 |
if (is_in_resize_zone(event->x, event->y)) { |
if (is_in_resize_zone(event->x, event->y)) { |
681 |
Gdk::Cursor double_arrow(Gdk::SB_H_DOUBLE_ARROW); |
#if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2 |
682 |
get_window()->pointer_grab(false, |
get_window()->pointer_grab(false, |
683 |
Gdk::BUTTON_RELEASE_MASK | |
Gdk::BUTTON_RELEASE_MASK | |
684 |
Gdk::POINTER_MOTION_MASK | |
Gdk::POINTER_MOTION_MASK | |
685 |
Gdk::POINTER_MOTION_HINT_MASK, |
Gdk::POINTER_MOTION_HINT_MASK, |
686 |
double_arrow, event->time); |
Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW), |
687 |
|
event->time); |
688 |
|
#else |
689 |
|
Glib::wrap(event->device, true)->grab(get_window(), |
690 |
|
Gdk::OWNERSHIP_NONE, |
691 |
|
false, |
692 |
|
Gdk::BUTTON_RELEASE_MASK | |
693 |
|
Gdk::POINTER_MOTION_MASK | |
694 |
|
Gdk::POINTER_MOTION_HINT_MASK, |
695 |
|
Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW), |
696 |
|
event->time); |
697 |
|
#endif |
698 |
resize.active = true; |
resize.active = true; |
699 |
} else { |
} else { |
700 |
gig::Region* r = get_region(k); |
gig::Region* r = get_region(k); |
702 |
region = r; |
region = r; |
703 |
queue_draw(); |
queue_draw(); |
704 |
region_selected(); |
region_selected(); |
705 |
|
dimensionManager.set_region(region); |
706 |
|
|
707 |
|
#if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2 |
708 |
|
get_window()->pointer_grab(false, |
709 |
|
Gdk::BUTTON_RELEASE_MASK | |
710 |
|
Gdk::POINTER_MOTION_MASK | |
711 |
|
Gdk::POINTER_MOTION_HINT_MASK, |
712 |
|
Gdk::Cursor(Gdk::FLEUR), |
713 |
|
event->time); |
714 |
|
#else |
715 |
|
Glib::wrap(event->device, true)->grab(get_window(), |
716 |
|
Gdk::OWNERSHIP_NONE, |
717 |
|
false, |
718 |
|
Gdk::BUTTON_RELEASE_MASK | |
719 |
|
Gdk::POINTER_MOTION_MASK | |
720 |
|
Gdk::POINTER_MOTION_HINT_MASK, |
721 |
|
Gdk::Cursor::create(Gdk::FLEUR), |
722 |
|
event->time); |
723 |
|
#endif |
724 |
|
move.active = true; |
725 |
|
move.offset = event->x - key_to_x(region->KeyRange.low, w); |
726 |
} |
} |
727 |
} |
} |
728 |
} |
} |
731 |
|
|
732 |
gig::Region* RegionChooser::get_region(int key) |
gig::Region* RegionChooser::get_region(int key) |
733 |
{ |
{ |
734 |
for (gig::Region *r = instrument->GetFirstRegion() ; r ; |
for (gig::Region* r = regions.first() ; r ; r = regions.next()) { |
|
r = instrument->GetNextRegion()) { |
|
735 |
if (key < r->KeyRange.low) return 0; |
if (key < r->KeyRange.low) return 0; |
736 |
if (key <= r->KeyRange.high) return r; |
if (key <= r->KeyRange.high) return r; |
737 |
} |
} |
738 |
return 0; |
return 0; |
739 |
} |
} |
740 |
|
|
741 |
bool RegionChooser::on_motion_notify_event(GdkEventMotion* event) |
void RegionChooser::set_region(gig::Region* region) { |
742 |
{ |
this->region = region; |
743 |
const int w = width - 1; |
queue_draw(); |
744 |
Glib::RefPtr<Gdk::Window> window = get_window(); |
region_selected(); |
745 |
int x, y; |
dimensionManager.set_region(region); |
746 |
Gdk::ModifierType state = Gdk::ModifierType(0); |
} |
|
window->get_pointer(x, y, state); |
|
|
if (resize.active) { |
|
|
int k = int(double(x) / w * 128.0 + 0.5); |
|
747 |
|
|
748 |
if (k < resize.min) k = resize.min; |
void RegionChooser::select_next_region() { |
749 |
else if (k > resize.max) k = resize.max; |
if (!instrument) return; |
750 |
|
if (!region) { |
751 |
|
for (int i = 0; i < 128; ++i) { |
752 |
|
::gig::Region* rgn = instrument->GetRegion(i); |
753 |
|
if (rgn) { |
754 |
|
set_region(rgn); |
755 |
|
return; |
756 |
|
} |
757 |
|
} |
758 |
|
} else { |
759 |
|
bool currentFound = false; |
760 |
|
for (int i = 0; i < 128; ++i) { |
761 |
|
::gig::Region* rgn = instrument->GetRegion(i); |
762 |
|
if (!rgn) continue; |
763 |
|
if (currentFound) { |
764 |
|
if (rgn != region) { |
765 |
|
set_region(rgn); |
766 |
|
return; |
767 |
|
} |
768 |
|
} else { |
769 |
|
if (rgn == region) currentFound = true; |
770 |
|
} |
771 |
|
} |
772 |
|
} |
773 |
|
} |
774 |
|
|
775 |
if (k != resize.pos) { |
void RegionChooser::select_prev_region() { |
776 |
if (resize.mode == resize.undecided) { |
if (!instrument) return; |
777 |
if (k < resize.pos) { |
if (!region) { |
778 |
// edit high limit of prev_region |
for (int i = 0; i < 128; ++i) { |
779 |
resize.max = resize.region->KeyRange.low; |
::gig::Region* rgn = instrument->GetRegion(i); |
780 |
resize.region = resize.prev_region; |
if (rgn) { |
781 |
resize.mode = resize.moving_high_limit; |
set_region(rgn); |
782 |
} else { |
return; |
783 |
// edit low limit of region |
} |
784 |
resize.min = resize.prev_region->KeyRange.high + 1; |
} |
785 |
resize.mode = resize.moving_low_limit; |
} else { |
786 |
|
bool currentFound = false; |
787 |
|
for (int i = 127; i >= 0; --i) { |
788 |
|
::gig::Region* rgn = instrument->GetRegion(i); |
789 |
|
if (!rgn) continue; |
790 |
|
if (currentFound) { |
791 |
|
if (rgn != region) { |
792 |
|
set_region(rgn); |
793 |
|
return; |
794 |
} |
} |
795 |
|
} else { |
796 |
|
if (rgn == region) currentFound = true; |
797 |
|
} |
798 |
|
} |
799 |
|
} |
800 |
|
} |
801 |
|
|
802 |
|
void RegionChooser::motion_resize_region(int x, int y) |
803 |
|
{ |
804 |
|
const int w = get_width() - 1; |
805 |
|
|
806 |
|
int k = int(double(x) / w * 128.0 + 0.5); |
807 |
|
|
808 |
|
if (k < resize.min) k = resize.min; |
809 |
|
else if (k > resize.max) k = resize.max; |
810 |
|
|
811 |
|
if (k != resize.pos) { |
812 |
|
if (resize.mode == resize.undecided) { |
813 |
|
if (k < resize.pos) { |
814 |
|
// edit high limit of prev_region |
815 |
|
resize.max = resize.region->KeyRange.low; |
816 |
|
resize.region = resize.prev_region; |
817 |
|
resize.mode = resize.moving_high_limit; |
818 |
|
} else { |
819 |
|
// edit low limit of region |
820 |
|
resize.min = resize.prev_region->KeyRange.high + 1; |
821 |
|
resize.mode = resize.moving_low_limit; |
822 |
|
} |
823 |
|
} |
824 |
|
resize.pos = k; |
825 |
|
|
826 |
|
int x1, x2; |
827 |
|
if (resize.mode == resize.moving_high_limit) { |
828 |
|
if (resize.region->KeyRange.high < resize.pos - 1) { |
829 |
|
x1 = resize.region->KeyRange.high; |
830 |
|
x2 = resize.pos - 1; |
831 |
|
} else { |
832 |
|
x1 = resize.pos - 1; |
833 |
|
x2 = resize.region->KeyRange.high; |
834 |
|
} |
835 |
|
} else { |
836 |
|
if (resize.region->KeyRange.low < resize.pos) { |
837 |
|
x1 = resize.region->KeyRange.low; |
838 |
|
x2 = resize.pos; |
839 |
|
} else { |
840 |
|
x1 = resize.pos; |
841 |
|
x2 = resize.region->KeyRange.low; |
842 |
} |
} |
843 |
Glib::RefPtr<const Gdk::GC> black = get_style()->get_black_gc(); |
} |
844 |
Glib::RefPtr<const Gdk::GC> white = get_style()->get_white_gc(); |
x1 = key_to_x(x1, w); |
845 |
if (region == resize.region) { |
x2 = key_to_x(x2 + 1, w) + 1; |
846 |
gc->set_foreground(red); |
Gdk::Rectangle rect(x1, 0, x2 - x1, h1); |
847 |
white = gc; |
|
848 |
} |
update_after_resize(); |
849 |
Glib::RefPtr<const Gdk::GC> bg = get_style()->get_bg_gc(Gtk::STATE_NORMAL); |
|
850 |
int prevx = int(w * resize.pos / 128.0 + 0.5); |
//get_window()->invalidate_rect(rect, false); |
851 |
x = int(w * k / 128.0 + 0.5); |
get_window()->invalidate(false); // repaint entire region, otherwise it would create visual artifacts |
852 |
|
} |
853 |
if (resize.mode == resize.moving_high_limit) { |
} |
854 |
if (k > resize.pos) { |
|
855 |
window->draw_rectangle(white, true, prevx, 1, x - prevx, h1 - 2); |
void RegionChooser::motion_move_region(int x, int y) |
856 |
window->draw_line(black, prevx, 0, x, 0); |
{ |
857 |
window->draw_line(black, prevx, h1 - 1, x, h1 - 1); |
const int w = get_width() - 1; |
858 |
|
|
859 |
|
int l = int(double(x - move.offset) / w * 128.0 + 0.5); |
860 |
|
|
861 |
|
if (l == region->KeyRange.low) return; |
862 |
|
int new_l; |
863 |
|
int regionsize = region->KeyRange.high - region->KeyRange.low; |
864 |
|
int a = 0; |
865 |
|
if (l > region->KeyRange.low) { |
866 |
|
for (gig::Region* r = regions.first() ; ; r = regions.next()) { |
867 |
|
if (r != region) { |
868 |
|
int b = r ? r->KeyRange.low : 128; |
869 |
|
|
870 |
|
// gap: from a to b (not inclusive b) |
871 |
|
|
872 |
|
if (region->KeyRange.high >= b) { |
873 |
|
// not found the current gap yet, just continue |
874 |
} else { |
} else { |
875 |
int xx = ((resize.pos == resize.max && resize.max != 128) ? 1 : 0); |
|
876 |
window->draw_rectangle(bg, true, x + 1, 0, prevx - x - xx, h1); |
if (a > l) { |
877 |
|
// this gap is too far to the right, break |
878 |
|
break; |
879 |
|
} |
880 |
|
|
881 |
|
int newhigh = std::min(l + regionsize, b - 1); |
882 |
|
int newlo = newhigh - regionsize; |
883 |
|
|
884 |
|
if (newlo >= a) { |
885 |
|
// yes it fits - it's a candidate |
886 |
|
new_l = newlo; |
887 |
|
} |
888 |
} |
} |
889 |
} else { |
if (!r) break; |
890 |
if (k < resize.pos) { |
a = r->KeyRange.high + 1; |
891 |
window->draw_rectangle(white, true, x + 1, 1, prevx - x, h1 - 2); |
} |
892 |
window->draw_line(black, x, 0, prevx, 0); |
} |
893 |
window->draw_line(black, x, h1 - 1, prevx, h1 - 1); |
} else { |
894 |
|
for (gig::Region* r = regions.first() ; ; r = regions.next()) { |
895 |
|
if (r != region) { |
896 |
|
int b = r ? r->KeyRange.low : 128; |
897 |
|
|
898 |
|
// gap from a to b (not inclusive b) |
899 |
|
|
900 |
|
if (l + regionsize >= b) { |
901 |
|
// not found the current gap yet, just continue |
902 |
} else { |
} else { |
903 |
int xx = ((resize.pos == resize.min && resize.min != 0) ? 1 : 0); |
|
904 |
window->draw_rectangle(bg, true, prevx + xx, 0, x - prevx - xx, h1); |
if (a > region->KeyRange.low) { |
905 |
|
// this gap is too far to the right, break |
906 |
|
break; |
907 |
|
} |
908 |
|
|
909 |
|
int newlo = std::max(l, a); |
910 |
|
int newhigh = newlo + regionsize; |
911 |
|
|
912 |
|
if (newhigh < b) { |
913 |
|
// yes it fits - break as the first one is the best |
914 |
|
new_l = newlo; |
915 |
|
break; |
916 |
|
} |
917 |
} |
} |
918 |
|
if (!r) break; |
919 |
|
a = r->KeyRange.high + 1; |
920 |
} |
} |
|
window->draw_line(black, x, 1, x, h1 - 2); |
|
|
resize.pos = k; |
|
921 |
} |
} |
922 |
|
} |
923 |
|
if (new_l == region->KeyRange.low) return; |
924 |
|
|
925 |
|
int x1 = key_to_x(std::min(int(region->KeyRange.low), new_l), w); |
926 |
|
int x2 = key_to_x(std::max(int(region->KeyRange.high), |
927 |
|
new_l + regionsize) + 1, w) + 1; |
928 |
|
|
929 |
|
Gdk::Rectangle rect(x1, 0, x2 - x1, h1); |
930 |
|
update_after_move(new_l); |
931 |
|
|
932 |
|
get_window()->invalidate_rect(rect, false); |
933 |
|
} |
934 |
|
|
935 |
|
|
936 |
|
bool RegionChooser::on_motion_notify_event(GdkEventMotion* event) |
937 |
|
{ |
938 |
|
Glib::RefPtr<Gdk::Window> window = get_window(); |
939 |
|
int x, y; |
940 |
|
Gdk::ModifierType state = Gdk::ModifierType(0); |
941 |
|
window->get_pointer(x, y, state); |
942 |
|
|
943 |
|
// handle virtual MIDI keyboard |
944 |
|
if (m_VirtKeybModeChoice.get_value() != VIRT_KEYBOARD_MODE_CHORD && |
945 |
|
currentActiveKey > 0 && |
946 |
|
event->y >= REGION_BLOCK_HEIGHT && |
947 |
|
event->y < REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT) |
948 |
|
{ |
949 |
|
const int k = x_to_key(event->x, get_width() - 1); |
950 |
|
if (k != currentActiveKey) { |
951 |
|
int velocity = |
952 |
|
(event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 : |
953 |
|
int(float(event->y - REGION_BLOCK_HEIGHT) / |
954 |
|
float(KEYBOARD_HEIGHT) * 128.0f) + 1; |
955 |
|
if (velocity <= 0) velocity = 1; |
956 |
|
keyboard_key_released_signal.emit(currentActiveKey, velocity); |
957 |
|
currentActiveKey = k; |
958 |
|
keyboard_key_hit_signal.emit(k, velocity); |
959 |
|
} |
960 |
|
} |
961 |
|
|
962 |
|
if (resize.active) { |
963 |
|
motion_resize_region(x, y); |
964 |
|
} else if (move.active) { |
965 |
|
motion_move_region(x, y); |
966 |
} else { |
} else { |
967 |
if (is_in_resize_zone(x, y)) { |
if (is_in_resize_zone(x, y)) { |
968 |
if (!cursor_is_resize) { |
if (!cursor_is_resize) { |
969 |
Gdk::Cursor double_arrow(Gdk::SB_H_DOUBLE_ARROW); |
#if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2 |
970 |
window->set_cursor(double_arrow); |
window->set_cursor(Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW)); |
971 |
|
#else |
972 |
|
window->set_cursor(Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW)); |
973 |
|
#endif |
974 |
cursor_is_resize = true; |
cursor_is_resize = true; |
975 |
} |
} |
976 |
} else if (cursor_is_resize) { |
} else if (cursor_is_resize) { |
983 |
} |
} |
984 |
|
|
985 |
bool RegionChooser::is_in_resize_zone(double x, double y) { |
bool RegionChooser::is_in_resize_zone(double x, double y) { |
986 |
const int w = width - 1; |
const int w = get_width() - 1; |
987 |
|
|
988 |
if (instrument && y >= 0 && y <= h1) { |
if (instrument && y >= 0 && y <= h1) { |
989 |
gig::Region* prev_region = 0; |
gig::Region* prev_region = 0; |
990 |
gig::Region* next_region; |
gig::Region* next_region; |
991 |
for (gig::Region* r = instrument->GetFirstRegion() ; r ; r = next_region) { |
for (gig::Region* r = regions.first(); r ; r = next_region) { |
992 |
next_region = instrument->GetNextRegion(); |
next_region = regions.next(); |
993 |
|
|
994 |
int lo = int(w * (r->KeyRange.low) / 128.0 + 0.5); |
int lo = key_to_x(r->KeyRange.low, w); |
995 |
if (x <= lo - 2) break; |
if (x <= lo - 2) break; |
996 |
if (x < lo + 2) { |
if (x < lo + 2) { |
997 |
resize.region = r; |
resize.region = r; |
1005 |
resize.mode = resize.undecided; |
resize.mode = resize.undecided; |
1006 |
resize.min = prev_region->KeyRange.low + 1; |
resize.min = prev_region->KeyRange.low + 1; |
1007 |
resize.prev_region = prev_region; |
resize.prev_region = prev_region; |
1008 |
return true; |
return resize.min != resize.max; |
1009 |
} |
} |
1010 |
|
|
1011 |
// edit low limit |
// edit low limit |
1012 |
resize.mode = resize.moving_low_limit; |
resize.mode = resize.moving_low_limit; |
1013 |
resize.min = prev_region ? prev_region->KeyRange.high + 1 : 0; |
resize.min = prev_region ? prev_region->KeyRange.high + 1 : 0; |
1014 |
return true; |
return resize.min != resize.max; |
1015 |
} |
} |
1016 |
if (!next_region || r->KeyRange.high + 1 != next_region->KeyRange.low) { |
if (!next_region || r->KeyRange.high + 1 != next_region->KeyRange.low) { |
1017 |
int hi = int(w * (r->KeyRange.high + 1) / 128.0 + 0.5); |
int hi = key_to_x(r->KeyRange.high + 1, w); |
1018 |
if (x <= hi - 2) break; |
if (x <= hi - 2) break; |
1019 |
if (x < hi + 2) { |
if (x < hi + 2) { |
1020 |
// edit high limit |
// edit high limit |
1023 |
resize.mode = resize.moving_high_limit; |
resize.mode = resize.moving_high_limit; |
1024 |
resize.min = r->KeyRange.low + 1; |
resize.min = r->KeyRange.low + 1; |
1025 |
resize.max = next_region ? next_region->KeyRange.low : 128; |
resize.max = next_region ? next_region->KeyRange.low : 128; |
1026 |
return true; |
return resize.min != resize.max; |
1027 |
} |
} |
1028 |
} |
} |
1029 |
prev_region = r; |
prev_region = r; |
1032 |
return false; |
return false; |
1033 |
} |
} |
1034 |
|
|
1035 |
sigc::signal<void> RegionChooser::signal_region_selected() |
sigc::signal<void>& RegionChooser::signal_region_selected() |
1036 |
{ |
{ |
1037 |
return region_selected; |
return region_selected; |
1038 |
} |
} |
1039 |
|
|
1040 |
sigc::signal<void> RegionChooser::signal_instrument_changed() |
sigc::signal<void>& RegionChooser::signal_instrument_changed() |
1041 |
{ |
{ |
1042 |
return instrument_changed; |
return instrument_changed; |
1043 |
} |
} |
1045 |
void RegionChooser::show_region_properties() |
void RegionChooser::show_region_properties() |
1046 |
{ |
{ |
1047 |
if (!region) return; |
if (!region) return; |
1048 |
Gtk::Dialog dialog("Region Properties", true /*modal*/); |
Gtk::Dialog dialog(_("Region Properties"), true /*modal*/); |
1049 |
// add "Keygroup" checkbox |
// add "Keygroup" checkbox |
1050 |
Gtk::CheckButton checkBoxKeygroup("Member of a Keygroup (Exclusive Group)"); |
Gtk::CheckButton checkBoxKeygroup(_("Member of a Keygroup (Exclusive Group)")); |
1051 |
checkBoxKeygroup.set_active(region->KeyGroup); |
checkBoxKeygroup.set_active(region->KeyGroup); |
1052 |
dialog.get_vbox()->pack_start(checkBoxKeygroup); |
dialog.get_vbox()->pack_start(checkBoxKeygroup); |
1053 |
checkBoxKeygroup.show(); |
checkBoxKeygroup.show(); |
1054 |
// add "Keygroup" spinbox |
// add "Keygroup" spinbox |
1055 |
Gtk::Adjustment adjustment(1, 1, pow(2,32)); |
#if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2 |
1056 |
|
Gtk::Adjustment adjustment(1, 1, 999); |
1057 |
Gtk::SpinButton spinBox(adjustment); |
Gtk::SpinButton spinBox(adjustment); |
1058 |
|
#else |
1059 |
|
Gtk::SpinButton spinBox(Gtk::Adjustment::create(1, 1, 999)); |
1060 |
|
#endif |
1061 |
if (region->KeyGroup) spinBox.set_value(region->KeyGroup); |
if (region->KeyGroup) spinBox.set_value(region->KeyGroup); |
1062 |
dialog.get_vbox()->pack_start(spinBox); |
dialog.get_vbox()->pack_start(spinBox); |
1063 |
spinBox.show(); |
spinBox.show(); |
1064 |
// add OK and CANCEL buttons to the dialog |
// add OK and CANCEL buttons to the dialog |
1065 |
dialog.add_button(Gtk::Stock::OK, 0); |
dialog.add_button(Gtk::Stock::OK, 0); |
1066 |
dialog.add_button(Gtk::Stock::CANCEL, 1); |
dialog.add_button(Gtk::Stock::CANCEL, 1); |
1067 |
|
dialog.set_position(Gtk::WIN_POS_MOUSE); |
1068 |
dialog.show_all_children(); |
dialog.show_all_children(); |
1069 |
if (!dialog.run()) { // OK selected ... |
if (!dialog.run()) { // OK selected ... |
1070 |
region->KeyGroup = |
region->KeyGroup = |
1074 |
|
|
1075 |
void RegionChooser::add_region() |
void RegionChooser::add_region() |
1076 |
{ |
{ |
1077 |
gig::Region* r; |
instrument_struct_to_be_changed_signal.emit(instrument); |
|
for (r = instrument->GetFirstRegion() ; r ; r = instrument->GetNextRegion()) { |
|
|
if (r->KeyRange.low > new_region_pos) break; |
|
|
} |
|
1078 |
|
|
1079 |
region = instrument->AddRegion(); |
region = instrument->AddRegion(); |
1080 |
region->KeyRange.low = region->KeyRange.high = new_region_pos; |
region->SetKeyRange(new_region_pos, new_region_pos); |
1081 |
|
|
1082 |
|
instrument_struct_changed_signal.emit(instrument); |
1083 |
|
regions.update(instrument); |
1084 |
|
|
|
instrument->MoveRegion(region, r); |
|
1085 |
queue_draw(); |
queue_draw(); |
1086 |
region_selected(); |
region_selected(); |
1087 |
|
dimensionManager.set_region(region); |
1088 |
instrument_changed(); |
instrument_changed(); |
1089 |
} |
} |
1090 |
|
|
1091 |
void RegionChooser::delete_region() |
void RegionChooser::delete_region() |
1092 |
{ |
{ |
1093 |
|
instrument_struct_to_be_changed_signal.emit(instrument); |
1094 |
instrument->DeleteRegion(region); |
instrument->DeleteRegion(region); |
1095 |
|
instrument_struct_changed_signal.emit(instrument); |
1096 |
|
regions.update(instrument); |
1097 |
|
|
1098 |
region = 0; |
region = 0; |
1099 |
queue_draw(); |
queue_draw(); |
1100 |
region_selected(); |
region_selected(); |
1101 |
|
dimensionManager.set_region(region); |
1102 |
instrument_changed(); |
instrument_changed(); |
1103 |
} |
} |
1104 |
|
|
1113 |
region_selected(); |
region_selected(); |
1114 |
instrument_changed(); |
instrument_changed(); |
1115 |
} |
} |
1116 |
|
|
1117 |
|
sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_to_be_changed() { |
1118 |
|
return instrument_struct_to_be_changed_signal; |
1119 |
|
} |
1120 |
|
|
1121 |
|
sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_changed() { |
1122 |
|
return instrument_struct_changed_signal; |
1123 |
|
} |
1124 |
|
|
1125 |
|
sigc::signal<void, gig::Region*>& RegionChooser::signal_region_to_be_changed() { |
1126 |
|
return region_to_be_changed_signal; |
1127 |
|
} |
1128 |
|
|
1129 |
|
sigc::signal<void, gig::Region*>& RegionChooser::signal_region_changed_signal() { |
1130 |
|
return region_changed_signal; |
1131 |
|
} |
1132 |
|
|
1133 |
|
sigc::signal<void, int/*key*/, int/*velocity*/>& RegionChooser::signal_keyboard_key_hit() { |
1134 |
|
return keyboard_key_hit_signal; |
1135 |
|
} |
1136 |
|
|
1137 |
|
sigc::signal<void, int/*key*/, int/*velocity*/>& RegionChooser::signal_keyboard_key_released() { |
1138 |
|
return keyboard_key_released_signal; |
1139 |
|
} |