/[svn]/gigedit/trunk/src/gigedit/dimregionchooser.cpp
ViewVC logotype

Contents of /gigedit/trunk/src/gigedit/dimregionchooser.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3305 - (show annotations) (download)
Mon Jul 10 20:27:44 2017 UTC (4 years, 2 months ago) by schoenebeck
File size: 65089 byte(s)
* Draw icons on individual dimension region zones, similar to
  the icons already been drawn on regions.
* Bumped version (1.0.0.svn56).

1 /*
2 * Copyright (C) 2006-2017 Andreas Persson
3 *
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 "global.h"
21 #include <gtkmm/box.h>
22 #include "dimregionchooser.h"
23 #include <cairomm/context.h>
24 #include <cairomm/surface.h>
25 #include <gdkmm/cursor.h>
26 #include <gdkmm/general.h>
27 #include <glibmm/stringutils.h>
28 #include <gtkmm/stock.h>
29 #include <glibmm/ustring.h>
30 #include <gtkmm/messagedialog.h>
31 #include <assert.h>
32
33 #include "gfx/builtinpix.h"
34
35 //TODO: this function and dimensionCaseOf() from global.h are duplicates, eliminate either one of them!
36 static DimensionCase caseOfDimRegion(gig::DimensionRegion* dr, bool* isValidZone) {
37 DimensionCase dimCase;
38 if (!dr) {
39 *isValidZone = false;
40 return dimCase;
41 }
42
43 gig::Region* rgn = (gig::Region*) dr->GetParent();
44
45 // find the dimension region index of the passed dimension region
46 int drIndex;
47 for (drIndex = 0; drIndex < 256; ++drIndex)
48 if (rgn->pDimensionRegions[drIndex] == dr)
49 break;
50
51 // not found in region, something's horribly wrong
52 if (drIndex == 256) {
53 fprintf(stderr, "DimRegionChooser: ERROR: index of dim region not found!\n");
54 *isValidZone = false;
55 return DimensionCase();
56 }
57
58 for (int d = 0, baseBits = 0; d < rgn->Dimensions; ++d) {
59 const int bits = rgn->pDimensionDefinitions[d].bits;
60 dimCase[rgn->pDimensionDefinitions[d].dimension] =
61 (drIndex >> baseBits) & ((1 << bits) - 1);
62 baseBits += bits;
63 // there are also DimensionRegion objects of unused zones, skip them
64 if (dimCase[rgn->pDimensionDefinitions[d].dimension] >= rgn->pDimensionDefinitions[d].zones) {
65 *isValidZone = false;
66 return DimensionCase();
67 }
68 }
69
70 *isValidZone = true;
71 return dimCase;
72 }
73
74 DimRegionChooser::DimRegionChooser(Gtk::Window& window) :
75 red("#ff476e"),
76 blue("#4796ff"),
77 black("black"),
78 white("white")
79 {
80 // make sure blue hatched pattern pixmap is loaded
81 loadBuiltInPix();
82
83 // create blue hatched pattern 1
84 {
85 const int width = blueHatchedPattern->get_width();
86 const int height = blueHatchedPattern->get_height();
87 const int stride = blueHatchedPattern->get_rowstride();
88
89 // manually convert from RGBA to ARGB
90 this->blueHatchedPatternARGB = blueHatchedPattern->copy();
91 const int pixelSize = stride / width;
92 const int totalPixels = width * height;
93 assert(pixelSize == 4);
94 unsigned char* ptr = this->blueHatchedPatternARGB->get_pixels();
95 for (int iPixel = 0; iPixel < totalPixels; ++iPixel, ptr += pixelSize) {
96 const unsigned char r = ptr[0];
97 const unsigned char g = ptr[1];
98 const unsigned char b = ptr[2];
99 const unsigned char a = ptr[3];
100 ptr[0] = b;
101 ptr[1] = g;
102 ptr[2] = r;
103 ptr[3] = a;
104 }
105
106 Cairo::RefPtr<Cairo::ImageSurface> imageSurface = Cairo::ImageSurface::create(
107 this->blueHatchedPatternARGB->get_pixels(), Cairo::FORMAT_ARGB32, width, height, stride
108 );
109 this->blueHatchedSurfacePattern = Cairo::SurfacePattern::create(imageSurface);
110 this->blueHatchedSurfacePattern->set_extend(Cairo::EXTEND_REPEAT);
111 }
112
113 // create blue hatched pattern 2
114 {
115 const int width = blueHatchedPattern2->get_width();
116 const int height = blueHatchedPattern2->get_height();
117 const int stride = blueHatchedPattern2->get_rowstride();
118
119 // manually convert from RGBA to ARGB
120 this->blueHatchedPattern2ARGB = blueHatchedPattern2->copy();
121 const int pixelSize = stride / width;
122 const int totalPixels = width * height;
123 assert(pixelSize == 4);
124 unsigned char* ptr = this->blueHatchedPattern2ARGB->get_pixels();
125 for (int iPixel = 0; iPixel < totalPixels; ++iPixel, ptr += pixelSize) {
126 const unsigned char r = ptr[0];
127 const unsigned char g = ptr[1];
128 const unsigned char b = ptr[2];
129 const unsigned char a = ptr[3];
130 ptr[0] = b;
131 ptr[1] = g;
132 ptr[2] = r;
133 ptr[3] = a;
134 }
135
136 Cairo::RefPtr<Cairo::ImageSurface> imageSurface = Cairo::ImageSurface::create(
137 this->blueHatchedPattern2ARGB->get_pixels(), Cairo::FORMAT_ARGB32, width, height, stride
138 );
139 this->blueHatchedSurfacePattern2 = Cairo::SurfacePattern::create(imageSurface);
140 this->blueHatchedSurfacePattern2->set_extend(Cairo::EXTEND_REPEAT);
141 }
142
143 // create gray blue hatched pattern
144 {
145 const int width = grayBlueHatchedPattern->get_width();
146 const int height = grayBlueHatchedPattern->get_height();
147 const int stride = grayBlueHatchedPattern->get_rowstride();
148
149 // manually convert from RGBA to ARGB
150 this->grayBlueHatchedPatternARGB = grayBlueHatchedPattern->copy();
151 const int pixelSize = stride / width;
152 const int totalPixels = width * height;
153 assert(pixelSize == 4);
154 unsigned char* ptr = this->grayBlueHatchedPatternARGB->get_pixels();
155 for (int iPixel = 0; iPixel < totalPixels; ++iPixel, ptr += pixelSize) {
156 const unsigned char r = ptr[0];
157 const unsigned char g = ptr[1];
158 const unsigned char b = ptr[2];
159 const unsigned char a = ptr[3];
160 ptr[0] = b;
161 ptr[1] = g;
162 ptr[2] = r;
163 ptr[3] = a;
164 }
165
166 Cairo::RefPtr<Cairo::ImageSurface> imageSurface = Cairo::ImageSurface::create(
167 this->grayBlueHatchedPatternARGB->get_pixels(), Cairo::FORMAT_ARGB32, width, height, stride
168 );
169 this->grayBlueHatchedSurfacePattern = Cairo::SurfacePattern::create(imageSurface);
170 this->grayBlueHatchedSurfacePattern->set_extend(Cairo::EXTEND_REPEAT);
171 }
172
173 instrument = 0;
174 region = 0;
175 maindimregno = -1;
176 maindimtype = gig::dimension_none; // initialize with invalid dimension type
177 focus_line = 0;
178 resize.active = false;
179 cursor_is_resize = false;
180 h = 24;
181 multiSelectKeyDown = false;
182 primaryKeyDown = false;
183 shiftKeyDown = false;
184 modifybothchannels = modifyalldimregs = modifybothchannels = false;
185 set_can_focus();
186
187 const Glib::ustring txtUseCheckBoxAllRegions =
188 _("Use checkbox 'all regions' to control whether this should apply to all regions.");
189
190 actionGroup = Gtk::ActionGroup::create();
191 actionSplitDimZone = Gtk::Action::create("SplitDimZone", _("Split Dimensions Zone"), txtUseCheckBoxAllRegions);
192 actionSplitDimZone->set_tooltip(txtUseCheckBoxAllRegions); //FIXME: doesn't work? why???
193 actionGroup->add(
194 actionSplitDimZone,
195 sigc::mem_fun(*this, &DimRegionChooser::split_dimension_zone)
196 );
197 actionDeleteDimZone = Gtk::Action::create("DeleteDimZone", _("Delete Dimension Zone"), txtUseCheckBoxAllRegions);
198 actionDeleteDimZone->set_tooltip(txtUseCheckBoxAllRegions); //FIXME: doesn't work? why???
199 actionGroup->add(
200 actionDeleteDimZone,
201 sigc::mem_fun(*this, &DimRegionChooser::delete_dimension_zone)
202 );
203
204 uiManager = Gtk::UIManager::create();
205 uiManager->insert_action_group(actionGroup);
206 Glib::ustring ui_info =
207 "<ui>"
208 " <popup name='PopupMenuInsideDimRegion'>"
209 " <menuitem action='SplitDimZone'/>"
210 " <menuitem action='DeleteDimZone'/>"
211 " </popup>"
212 // " <popup name='PopupMenuOutsideDimRegion'>"
213 // " <menuitem action='Add'/>"
214 // " </popup>"
215 "</ui>";
216 uiManager->add_ui_from_string(ui_info);
217
218 popup_menu_inside_dimregion = dynamic_cast<Gtk::Menu*>(
219 uiManager->get_widget("/PopupMenuInsideDimRegion"));
220 // popup_menu_outside_dimregion = dynamic_cast<Gtk::Menu*>(
221 // uiManager->get_widget("/PopupMenuOutsideDimRegion"));
222
223 add_events(Gdk::BUTTON_PRESS_MASK | Gdk::POINTER_MOTION_MASK |
224 Gdk::POINTER_MOTION_HINT_MASK);
225
226 labels_changed = true;
227
228 set_tooltip_text(_(
229 "Right click here for options on altering dimension zones. Press and "
230 "hold CTRL key for selecting multiple dimension zones simultaniously."
231 ));
232
233 window.signal_key_press_event().connect(
234 sigc::mem_fun(*this, &DimRegionChooser::onKeyPressed)
235 );
236 window.signal_key_release_event().connect(
237 sigc::mem_fun(*this, &DimRegionChooser::onKeyReleased)
238 );
239 }
240
241 DimRegionChooser::~DimRegionChooser()
242 {
243 }
244
245 void DimRegionChooser::setModifyBothChannels(bool b) {
246 modifybothchannels = b;
247 // redraw required parts
248 queue_draw();
249 }
250
251 void DimRegionChooser::setModifyAllDimensionRegions(bool b) {
252 modifyalldimregs = b;
253 // redraw required parts
254 queue_draw();
255 }
256
257 void DimRegionChooser::setModifyAllRegions(bool b) {
258 modifyallregions = b;
259
260 actionDeleteDimZone->set_label(b ? _("Delete Dimension Zone [ALL REGIONS]") : _("Delete Dimension Zone"));
261 actionSplitDimZone->set_label(b ? _("Split Dimensions Zone [ALL REGIONS]") : _("Split Dimensions Zone"));
262
263 // redraw required parts
264 queue_draw();
265 }
266
267 void DimRegionChooser::drawIconsFor(
268 gig::dimension_t dimension, uint zone,
269 const Cairo::RefPtr<Cairo::Context>& cr,
270 int x, int y, int w, int h)
271 {
272 DimensionCase dimCase;
273 dimCase[dimension] = zone;
274
275 std::vector<gig::DimensionRegion*> dimregs =
276 dimensionRegionsMatching(dimCase, region);
277
278 if (dimregs.empty()) return;
279
280 int iSampleRefs = 0;
281 int iLoops = 0;
282
283 for (uint i = 0; i < dimregs.size(); ++i) {
284 if (dimregs[i]->pSample) iSampleRefs++;
285 if (dimregs[i]->SampleLoops) iLoops++;
286 }
287
288 bool bShowLoopSymbol = (iLoops > 0);
289 bool bShowSampleRefSymbol = (iSampleRefs < dimregs.size());
290
291 if (bShowLoopSymbol || bShowSampleRefSymbol) {
292 const int margin = 1;
293
294 cr->save();
295 cr->set_line_width(1);
296 cr->rectangle(x, y + margin, w, h - 2*margin);
297 cr->clip();
298 if (bShowSampleRefSymbol) {
299 const int wPic = 8;
300 const int hPic = 8;
301 Gdk::Cairo::set_source_pixbuf(
302 cr, (iSampleRefs) ? yellowDot : redDot,
303 x + (w-wPic)/2.f,
304 y + (
305 (bShowLoopSymbol) ? margin : (h-hPic)/2.f
306 )
307 );
308 cr->paint();
309 }
310 if (bShowLoopSymbol) {
311 const int wPic = 12;
312 const int hPic = 14;
313 Gdk::Cairo::set_source_pixbuf(
314 cr, (iLoops == dimregs.size()) ? blackLoop : grayLoop,
315 x + (w-wPic)/2.f,
316 y + (
317 (bShowSampleRefSymbol) ? h - hPic - margin : (h-hPic)/2.f
318 )
319 );
320 cr->paint();
321 }
322 cr->restore();
323 }
324 }
325
326 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
327 bool DimRegionChooser::on_expose_event(GdkEventExpose* e)
328 {
329 double clipx1 = e->area.x;
330 double clipx2 = e->area.x + e->area.width;
331 double clipy1 = e->area.y;
332 double clipy2 = e->area.y + e->area.height;
333
334 const Cairo::RefPtr<Cairo::Context>& cr =
335 get_window()->create_cairo_context();
336 #else
337 bool DimRegionChooser::on_draw(const Cairo::RefPtr<Cairo::Context>& cr)
338 {
339 double clipx1, clipx2, clipy1, clipy2;
340 cr->get_clip_extents(clipx1, clipy1, clipx2, clipy2);
341 #endif
342
343 if (!region) return true;
344
345 // This is where we draw on the window
346 int w = get_width();
347 Glib::RefPtr<Pango::Context> context = get_pango_context();
348
349 Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create(context);
350 cr->set_line_width(1);
351
352 int y = 0;
353 if (labels_changed || label_width - 10 > clipx1) {
354 // draw labels on the left (reflecting the dimension type)
355 double maxwidth = 0;
356 for (int i = 0 ; i < region->Dimensions ; i++) {
357 int nbZones = region->pDimensionDefinitions[i].zones;
358 if (nbZones) {
359 const char* dstr;
360 char dstrbuf[10];
361 switch (region->pDimensionDefinitions[i].dimension) {
362 case gig::dimension_none: dstr=_("none"); break;
363 case gig::dimension_samplechannel: dstr=_("samplechannel");
364 break;
365 case gig::dimension_layer: dstr=_("layer"); break;
366 case gig::dimension_velocity: dstr=_("velocity"); break;
367 case gig::dimension_channelaftertouch:
368 dstr=_("channelaftertouch"); break;
369 case gig::dimension_releasetrigger:
370 dstr=_("releasetrigger"); break;
371 case gig::dimension_keyboard: dstr=_("keyswitching"); break;
372 case gig::dimension_roundrobin: dstr=_("roundrobin"); break;
373 case gig::dimension_random: dstr=_("random"); break;
374 case gig::dimension_smartmidi: dstr=_("smartmidi"); break;
375 case gig::dimension_roundrobinkeyboard:
376 dstr=_("roundrobinkeyboard"); break;
377 case gig::dimension_modwheel: dstr=_("modwheel"); break;
378 case gig::dimension_breath: dstr=_("breath"); break;
379 case gig::dimension_foot: dstr=_("foot"); break;
380 case gig::dimension_portamentotime:
381 dstr=_("portamentotime"); break;
382 case gig::dimension_effect1: dstr=_("effect1"); break;
383 case gig::dimension_effect2: dstr=_("effect2"); break;
384 case gig::dimension_genpurpose1: dstr=_("genpurpose1"); break;
385 case gig::dimension_genpurpose2: dstr=_("genpurpose2"); break;
386 case gig::dimension_genpurpose3: dstr=_("genpurpose3"); break;
387 case gig::dimension_genpurpose4: dstr=_("genpurpose4"); break;
388 case gig::dimension_sustainpedal:
389 dstr=_("sustainpedal"); break;
390 case gig::dimension_portamento: dstr=_("portamento"); break;
391 case gig::dimension_sostenutopedal:
392 dstr=_("sostenutopedal"); break;
393 case gig::dimension_softpedal: dstr=_("softpedal"); break;
394 case gig::dimension_genpurpose5: dstr=_("genpurpose5"); break;
395 case gig::dimension_genpurpose6: dstr=_("genpurpose6"); break;
396 case gig::dimension_genpurpose7: dstr=_("genpurpose7"); break;
397 case gig::dimension_genpurpose8: dstr=_("genpurpose8"); break;
398 case gig::dimension_effect1depth:
399 dstr=_("effect1depth"); break;
400 case gig::dimension_effect2depth:
401 dstr=_("effect2depth"); break;
402 case gig::dimension_effect3depth:
403 dstr=_("effect3depth"); break;
404 case gig::dimension_effect4depth:
405 dstr=_("effect4depth"); break;
406 case gig::dimension_effect5depth:
407 dstr=_("effect5depth"); break;
408 default:
409 sprintf(dstrbuf, "%d",
410 region->pDimensionDefinitions[i].dimension);
411 dstr = dstrbuf;
412 break;
413 }
414
415 // Since bold font yields in larger label width, we first always
416 // set the bold text variant, retrieve its dimensions (as worst
417 // case dimensions of the label) ...
418 layout->set_markup("<b>" + Glib::ustring(dstr) + "</b>");
419 Pango::Rectangle rectangle = layout->get_logical_extents();
420 // ... and then reset the label to regular font style in case
421 // the line is not selected. Otherwise the right hand side
422 // actual dimension zones would jump around on selection change.
423 bool isSelectedLine = (focus_line == i);
424 if (!isSelectedLine)
425 layout->set_markup(dstr);
426
427 double text_w = double(rectangle.get_width()) / Pango::SCALE;
428 if (text_w > maxwidth) maxwidth = text_w;
429
430 if (y + h > clipy1 && y < clipy2 && text_w >= clipx1) {
431 double text_h = double(rectangle.get_height()) /
432 Pango::SCALE;
433 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
434 const Gdk::Color fg = get_style()->get_fg(get_state());
435 #else
436 const Gdk::RGBA fg =
437 get_style_context()->get_color(get_state_flags());
438 #endif
439 Gdk::Cairo::set_source_rgba(cr, fg);
440 cr->move_to(4, int(y + (h - text_h) / 2 + 0.5));
441 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 16) || GTKMM_MAJOR_VERSION < 2
442 pango_cairo_show_layout(cr->cobj(), layout->gobj());
443 #else
444 layout->show_in_cairo_context(cr);
445 #endif
446 }
447 }
448 y += h;
449 }
450 label_width = int(maxwidth + 10);
451 labels_changed = false;
452 }
453 if (label_width >= clipx2) return true;
454
455 // draw dimensions' zones areas
456 y = 0;
457 int bitpos = 0;
458 for (int i = 0 ; i < region->Dimensions ; i++) {
459 int nbZones = region->pDimensionDefinitions[i].zones;
460 if (nbZones) {
461 const gig::dimension_t dimension = region->pDimensionDefinitions[i].dimension;
462
463 if (y >= clipy2) break;
464 if (y + h > clipy1) {
465 // draw focus rectangle around dimension's label and zones
466 if (has_focus() && focus_line == i) {
467 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
468 Gdk::Rectangle farea(0, y, 150, h);
469 get_style()->paint_focus(get_window(), get_state(), farea,
470 *this, "",
471 0, y, label_width, h);
472 #else
473 get_style_context()->render_focus(cr,
474 0, y, label_width, h);
475 #endif
476 }
477
478 // draw top and bottom lines of dimension's zones
479 Gdk::Cairo::set_source_rgba(cr, black);
480 cr->move_to(label_width, y + 0.5);
481 cr->line_to(w, y + 0.5);
482 cr->move_to(w, y + h - 0.5);
483 cr->line_to(label_width, y + h - 0.5);
484 cr->stroke();
485
486 // erase whole dimension's zones area
487 Gdk::Cairo::set_source_rgba(cr, white);
488 cr->rectangle(label_width + 1, y + 1,
489 (w - label_width - 2), h - 2);
490 cr->fill();
491
492 int c = 0;
493 if (maindimregno >= 0) {
494 int mask =
495 ~(((1 << region->pDimensionDefinitions[i].bits) - 1) <<
496 bitpos);
497 c = maindimregno & mask; // mask away this dimension
498 }
499 bool customsplits =
500 ((region->pDimensionDefinitions[i].split_type ==
501 gig::split_type_normal &&
502 region->pDimensionRegions[c]->DimensionUpperLimits[i]) ||
503 (region->pDimensionDefinitions[i].dimension ==
504 gig::dimension_velocity &&
505 region->pDimensionRegions[c]->VelocityUpperLimit));
506
507 // draw dimension zones
508 Gdk::Cairo::set_source_rgba(cr, black);
509 if (customsplits) {
510 cr->move_to(label_width + 0.5, y + 1);
511 cr->line_to(label_width + 0.5, y + h - 1);
512 int prevX = label_width;
513 int prevUpperLimit = -1;
514
515 for (int j = 0 ; j < nbZones ; j++) {
516 // draw dimension zone's borders for custom splits
517 gig::DimensionRegion* d =
518 region->pDimensionRegions[c + (j << bitpos)];
519 int upperLimit = d->DimensionUpperLimits[i];
520 if (!upperLimit) upperLimit = d->VelocityUpperLimit;
521 int v = upperLimit + 1;
522 int x = int((w - label_width - 1) * v / 128.0 + 0.5) +
523 label_width;
524 if (x >= clipx2) break;
525 if (x < clipx1) continue;
526 Gdk::Cairo::set_source_rgba(cr, black);
527 cr->move_to(x + 0.5, y + 1);
528 cr->line_to(x + 0.5, y + h - 1);
529 cr->stroke();
530
531 // draw fill for zone
532 bool isSelectedZone = this->dimzones[dimension].count(j);
533 bool isMainSelection =
534 this->maindimcase.find(dimension) != this->maindimcase.end() &&
535 this->maindimcase[dimension] == j;
536 bool isCheckBoxSelected =
537 modifyalldimregs ||
538 (modifybothchannels &&
539 dimension == gig::dimension_samplechannel);
540 if (isMainSelection)
541 Gdk::Cairo::set_source_rgba(cr, blue);
542 else if (isSelectedZone)
543 cr->set_source(blueHatchedSurfacePattern2);
544 else if (isCheckBoxSelected)
545 cr->set_source(blueHatchedSurfacePattern);
546 else
547 Gdk::Cairo::set_source_rgba(cr, white);
548
549 const int wZone = x - prevX - 1;
550
551 cr->rectangle(prevX + 1, y + 1, wZone, h - 1);
552 cr->fill();
553
554 // draw icons
555 drawIconsFor(dimension, j, cr, prevX, y, wZone, h);
556
557 // draw text showing the beginning of the dimension zone
558 // as numeric value to the user
559 {
560 Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create(context);
561 layout->set_text(Glib::Ascii::dtostr(prevUpperLimit+1));
562 Gdk::Cairo::set_source_rgba(cr, black);
563 // get the text dimensions
564 int text_width, text_height;
565 layout->get_pixel_size(text_width, text_height);
566 // move text to the left end of the dimension zone
567 cr->move_to(prevX + 3, y + (h - text_height) / 2);
568 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 16) || GTKMM_MAJOR_VERSION < 2
569 pango_cairo_show_layout(cr->cobj(), layout->gobj());
570 #else
571 layout->show_in_cairo_context(cr);
572 #endif
573 }
574 // draw text showing the end of the dimension zone
575 // as numeric value to the user
576 {
577 Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create(context);
578 layout->set_text(Glib::Ascii::dtostr(upperLimit));
579 Gdk::Cairo::set_source_rgba(cr, black);
580 // get the text dimensions
581 int text_width, text_height;
582 layout->get_pixel_size(text_width, text_height);
583 // move text to the left end of the dimension zone
584 cr->move_to(x - 3 - text_width, y + (h - text_height) / 2);
585 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 16) || GTKMM_MAJOR_VERSION < 2
586 pango_cairo_show_layout(cr->cobj(), layout->gobj());
587 #else
588 layout->show_in_cairo_context(cr);
589 #endif
590 }
591
592 prevX = x;
593 prevUpperLimit = upperLimit;
594 }
595 } else {
596 int prevX = 0;
597 for (int j = 0 ; j <= nbZones ; j++) {
598 // draw dimension zone's borders for normal splits
599 int x = int((w - label_width - 1) * j /
600 double(nbZones) + 0.5) + label_width;
601 if (x >= clipx2) break;
602 if (x < clipx1) continue;
603 Gdk::Cairo::set_source_rgba(cr, black);
604 cr->move_to(x + 0.5, y + 1);
605 cr->line_to(x + 0.5, y + h - 1);
606 cr->stroke();
607
608 if (j != 0) {
609 const int wZone = x - prevX - 1;
610
611 // draw fill for zone
612 bool isSelectedZone = this->dimzones[dimension].count(j-1);
613 bool isMainSelection =
614 this->maindimcase.find(dimension) != this->maindimcase.end() &&
615 this->maindimcase[dimension] == (j-1);
616 bool isCheckBoxSelected =
617 modifyalldimregs ||
618 (modifybothchannels &&
619 dimension == gig::dimension_samplechannel);
620 if (isMainSelection)
621 Gdk::Cairo::set_source_rgba(cr, blue);
622 else if (isSelectedZone)
623 cr->set_source(blueHatchedSurfacePattern2);
624 else if (isCheckBoxSelected)
625 cr->set_source(blueHatchedSurfacePattern);
626 else
627 Gdk::Cairo::set_source_rgba(cr, white);
628 cr->rectangle(prevX + 1, y + 1, wZone, h - 1);
629 cr->fill();
630
631 // draw icons
632 drawIconsFor(dimension, j - 1, cr, prevX, y, wZone, h);
633
634 // draw text showing the beginning of the dimension zone
635 // as numeric value to the user
636 {
637 Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create(context);
638 layout->set_text(Glib::Ascii::dtostr((j-1) * 128/nbZones));
639 Gdk::Cairo::set_source_rgba(cr, black);
640 // get the text dimensions
641 int text_width, text_height;
642 layout->get_pixel_size(text_width, text_height);
643 // move text to the left end of the dimension zone
644 cr->move_to(prevX + 3, y + (h - text_height) / 2);
645 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 16) || GTKMM_MAJOR_VERSION < 2
646 pango_cairo_show_layout(cr->cobj(), layout->gobj());
647 #else
648 layout->show_in_cairo_context(cr);
649 #endif
650 }
651 // draw text showing the end of the dimension zone
652 // as numeric value to the user
653 {
654 Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create(context);
655 layout->set_text(Glib::Ascii::dtostr(j * 128/nbZones - 1));
656 Gdk::Cairo::set_source_rgba(cr, black);
657 // get the text dimensions
658 int text_width, text_height;
659 layout->get_pixel_size(text_width, text_height);
660 // move text to the left end of the dimension zone
661 cr->move_to(x - 3 - text_width, y + (h - text_height) / 2);
662 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 16) || GTKMM_MAJOR_VERSION < 2
663 pango_cairo_show_layout(cr->cobj(), layout->gobj());
664 #else
665 layout->show_in_cairo_context(cr);
666 #endif
667 }
668 }
669 prevX = x;
670 }
671 }
672 }
673 y += h;
674 }
675 bitpos += region->pDimensionDefinitions[i].bits;
676 }
677
678 return true;
679 }
680
681 void DimRegionChooser::set_region(gig::Region* region)
682 {
683 this->region = region;
684 maindimregno = 0;
685 nbDimensions = 0;
686 if (region) {
687 int bitcount = 0;
688 for (int dim = 0 ; dim < region->Dimensions ; dim++) {
689 if (region->pDimensionDefinitions[dim].bits == 0) continue;
690 nbDimensions++;
691
692 int z = std::min(maindimcase[region->pDimensionDefinitions[dim].dimension],
693 region->pDimensionDefinitions[dim].zones - 1);
694 maindimregno |= (z << bitcount);
695 bitcount += region->pDimensionDefinitions[dim].bits;
696 }
697 }
698 dimregion_selected();
699 set_size_request(800, region ? nbDimensions * h : 0);
700
701 labels_changed = true;
702 queue_resize();
703 queue_draw();
704 }
705
706 void DimRegionChooser::refresh_all() {
707 set_region(region);
708 }
709
710 void DimRegionChooser::get_dimregions(const gig::Region* region, bool stereo,
711 std::set<gig::DimensionRegion*>& dimregs) const
712 {
713 for (int iDimRgn = 0; iDimRgn < 256; ++iDimRgn) {
714 gig::DimensionRegion* dimRgn = region->pDimensionRegions[iDimRgn];
715 if (!dimRgn) continue;
716 bool isValidZone;
717 std::map<gig::dimension_t,int> dimCase = caseOfDimRegion(dimRgn, &isValidZone);
718 if (!isValidZone) continue;
719 for (std::map<gig::dimension_t,int>::const_iterator it = dimCase.begin();
720 it != dimCase.end(); ++it)
721 {
722 if (stereo && it->first == gig::dimension_samplechannel) continue; // is selected
723
724 std::map<gig::dimension_t, std::set<int> >::const_iterator itSelectedDimension =
725 this->dimzones.find(it->first);
726 if (itSelectedDimension != this->dimzones.end() &&
727 itSelectedDimension->second.count(it->second)) continue; // is selected
728
729 goto notSelected;
730 }
731
732 dimregs.insert(dimRgn);
733
734 notSelected:
735 ;
736 }
737 }
738
739 void DimRegionChooser::update_after_resize()
740 {
741 const uint8_t upperLimit = resize.pos - 1;
742 gig::Instrument* instr = (gig::Instrument*)region->GetParent();
743
744 int bitpos = 0;
745 for (int j = 0 ; j < resize.dimension ; j++) {
746 bitpos += region->pDimensionDefinitions[j].bits;
747 }
748
749 const int stereobitpos =
750 (modifybothchannels) ? baseBits(gig::dimension_samplechannel, region) : -1;
751
752 // the velocity dimension must be handled differently than all other
753 // dimension types, because
754 // 1. it is currently the only dimension type which allows different zone
755 // sizes for different cases
756 // 2. for v2 format VelocityUpperLimit has to be set, DimensionUpperLimits for v3
757 if (region->pDimensionDefinitions[resize.dimension].dimension == gig::dimension_velocity) {
758 int mask =
759 ~(((1 << region->pDimensionDefinitions[resize.dimension].bits) - 1) << bitpos);
760 int c = maindimregno & mask; // mask away this dimension
761
762 if (region->pDimensionRegions[c]->DimensionUpperLimits[resize.dimension] == 0) {
763 // the velocity dimension didn't previously have
764 // custom v3 splits, so we initialize all splits with
765 // default values
766 int nbZones = region->pDimensionDefinitions[resize.dimension].zones;
767 for (int j = 0 ; j < nbZones ; j++) {
768 gig::DimensionRegion* d = region->pDimensionRegions[c + (j << bitpos)];
769 d->DimensionUpperLimits[resize.dimension] = int(128.0 * (j + 1) / nbZones - 1);
770 }
771 }
772 if (region->pDimensionRegions[c]->VelocityUpperLimit == 0) {
773 // the velocity dimension didn't previously have
774 // custom v2 splits, so we initialize all splits with
775 // default values
776 int nbZones = region->pDimensionDefinitions[resize.dimension].zones;
777 for (int j = 0 ; j < nbZones ; j++) {
778 gig::DimensionRegion* d = region->pDimensionRegions[c + (j << bitpos)];
779 d->VelocityUpperLimit = int(128.0 * (j + 1) / nbZones - 1);
780 }
781 }
782
783 int index = c + (resize.zone << bitpos);
784 gig::DimensionRegion* d = region->pDimensionRegions[index];
785 // update both v2 and v3 values
786 d->DimensionUpperLimits[resize.dimension] = upperLimit;
787 d->VelocityUpperLimit = upperLimit;
788 if (modifybothchannels && stereobitpos >= 0) { // do the same for the other audio channel's dimregion ...
789 gig::DimensionRegion* d = region->pDimensionRegions[index ^ (1 << stereobitpos)];
790 d->DimensionUpperLimits[resize.dimension] = upperLimit;
791 d->VelocityUpperLimit = upperLimit;
792 }
793
794 if (modifyalldimregs) {
795 gig::Region* rgn = NULL;
796 for (int key = 0; key < 128; ++key) {
797 if (!instr->GetRegion(key) || instr->GetRegion(key) == rgn) continue;
798 rgn = instr->GetRegion(key);
799 if (!modifyallregions && rgn != region) continue; // hack to reduce overall code amount a bit
800 gig::dimension_def_t* dimdef = rgn->GetDimensionDefinition(resize.dimensionDef.dimension);
801 if (!dimdef) continue;
802 if (dimdef->zones != resize.dimensionDef.zones) continue;
803 const int iDim = getDimensionIndex(resize.dimensionDef.dimension, rgn);
804 assert(iDim >= 0 && iDim < rgn->Dimensions);
805
806 // the dimension layout might be completely different in this
807 // region, so we have to recalculate bitpos etc for this region
808 const int bitpos = baseBits(resize.dimensionDef.dimension, rgn);
809 const int stencil = ~(((1 << dimdef->bits) - 1) << bitpos);
810 const int selection = resize.zone << bitpos;
811
812 // primitive and inefficient loop implementation, however due to
813 // this circumstance the loop code is much simpler, and its lack
814 // of runtime efficiency should not be notable in practice
815 for (int idr = 0; idr < 256; ++idr) {
816 const int index = (idr & stencil) | selection;
817 assert(index >= 0 && index < 256);
818 gig::DimensionRegion* dr = rgn->pDimensionRegions[index];
819 if (!dr) continue;
820 dr->DimensionUpperLimits[iDim] = upperLimit;
821 d->VelocityUpperLimit = upperLimit;
822 }
823 }
824 } else if (modifyallregions) { // implies modifyalldimregs is false ...
825 // resolve the precise case we need to modify for all other regions
826 DimensionCase dimCase = dimensionCaseOf(d);
827 // apply the velocity upper limit change to that resolved dim case
828 // of all regions ...
829 gig::Region* rgn = NULL;
830 for (int key = 0; key < 128; ++key) {
831 if (!instr->GetRegion(key) || instr->GetRegion(key) == rgn) continue;
832 rgn = instr->GetRegion(key);
833 gig::dimension_def_t* dimdef = rgn->GetDimensionDefinition(resize.dimensionDef.dimension);
834 if (!dimdef) continue;
835 if (dimdef->zones != resize.dimensionDef.zones) continue;
836 const int iDim = getDimensionIndex(resize.dimensionDef.dimension, rgn);
837 assert(iDim >= 0 && iDim < rgn->Dimensions);
838
839 std::vector<gig::DimensionRegion*> dimrgns = dimensionRegionsMatching(dimCase, rgn);
840 for (int i = 0; i < dimrgns.size(); ++i) {
841 gig::DimensionRegion* dr = dimrgns[i];
842 dr->DimensionUpperLimits[iDim] = upperLimit;
843 dr->VelocityUpperLimit = upperLimit;
844 }
845 }
846 }
847 } else {
848 for (int i = 0 ; i < region->DimensionRegions ; ) {
849 if (region->pDimensionRegions[i]->DimensionUpperLimits[resize.dimension] == 0) {
850 // the dimension didn't previously have custom
851 // limits, so we have to set default limits for
852 // all the dimension regions
853 int nbZones = region->pDimensionDefinitions[resize.dimension].zones;
854
855 for (int j = 0 ; j < nbZones ; j++) {
856 gig::DimensionRegion* d = region->pDimensionRegions[i + (j << bitpos)];
857 d->DimensionUpperLimits[resize.dimension] = int(128.0 * (j + 1) / nbZones - 1);
858 }
859 }
860 int index = i + (resize.zone << bitpos);
861 gig::DimensionRegion* d = region->pDimensionRegions[index];
862 d->DimensionUpperLimits[resize.dimension] = upperLimit;
863 #if 0 // the following is currently not necessary, because ATM the gig format uses for all dimension types except of the veleocity dimension the same zone sizes for all cases
864 if (modifybothchannels && stereobitpos >= 0) { // do the same for the other audio channel's dimregion ...
865 gig::DimensionRegion* d = region->pDimensionRegions[index ^ (1 << stereobitpos)];
866 d->DimensionUpperLimits[resize.dimension] = upperLimit;
867 }
868 #endif
869 int bitpos = 0;
870 int j;
871 for (j = 0 ; j < region->Dimensions ; j++) {
872 if (j != resize.dimension) {
873 int maxzones = 1 << region->pDimensionDefinitions[j].bits;
874 int dimj = (i >> bitpos) & (maxzones - 1);
875 if (dimj + 1 < region->pDimensionDefinitions[j].zones) break;
876 }
877 bitpos += region->pDimensionDefinitions[j].bits;
878 }
879 if (j == region->Dimensions) break;
880 i = (i & ~((1 << bitpos) - 1)) + (1 << bitpos);
881 }
882
883 if (modifyallregions) { // TODO: this code block could be merged with the similar (and more generalized) code block of the velocity dimension above
884 gig::Region* rgn = NULL;
885 for (int key = 0; key < 128; ++key) {
886 if (!instr->GetRegion(key) || instr->GetRegion(key) == rgn) continue;
887 rgn = instr->GetRegion(key);
888 gig::dimension_def_t* dimdef = rgn->GetDimensionDefinition(resize.dimensionDef.dimension);
889 if (!dimdef) continue;
890 if (dimdef->zones != resize.dimensionDef.zones) continue;
891 const int iDim = getDimensionIndex(resize.dimensionDef.dimension, rgn);
892 assert(iDim >= 0 && iDim < rgn->Dimensions);
893
894 // the dimension layout might be completely different in this
895 // region, so we have to recalculate bitpos etc for this region
896 const int bitpos = baseBits(resize.dimensionDef.dimension, rgn);
897 const int stencil = ~(((1 << dimdef->bits) - 1) << bitpos);
898 const int selection = resize.zone << bitpos;
899
900 // this loop implementation is less efficient than the above's
901 // loop implementation (which skips unnecessary dimension regions)
902 // however this code is much simpler, and its lack of runtime
903 // efficiency should not be notable in practice
904 for (int idr = 0; idr < 256; ++idr) {
905 const int index = (idr & stencil) | selection;
906 assert(index >= 0 && index < 256);
907 gig::DimensionRegion* dr = rgn->pDimensionRegions[index];
908 if (!dr) continue;
909 dr->DimensionUpperLimits[iDim] = upperLimit;
910 }
911 }
912 }
913 }
914 }
915
916 bool DimRegionChooser::on_button_release_event(GdkEventButton* event)
917 {
918 if (resize.active) {
919 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
920 get_window()->pointer_ungrab(event->time);
921 #else
922 Glib::wrap(event->device, true)->ungrab(event->time);
923 #endif
924 resize.active = false;
925
926 region_changed();
927
928 if (!is_in_resize_zone(event->x, event->y) && cursor_is_resize) {
929 get_window()->set_cursor();
930 cursor_is_resize = false;
931 }
932 }
933 return true;
934 }
935
936 bool DimRegionChooser::on_button_press_event(GdkEventButton* event)
937 {
938 int w = get_width();
939 if (region && event->y < nbDimensions * h &&
940 event->x >= label_width && event->x < w) {
941
942 if (is_in_resize_zone(event->x, event->y)) {
943 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
944 get_window()->pointer_grab(false,
945 Gdk::BUTTON_RELEASE_MASK |
946 Gdk::POINTER_MOTION_MASK |
947 Gdk::POINTER_MOTION_HINT_MASK,
948 Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW),
949 event->time);
950 #else
951 Glib::wrap(event->device, true)->grab(get_window(),
952 Gdk::OWNERSHIP_NONE,
953 false,
954 Gdk::BUTTON_RELEASE_MASK |
955 Gdk::POINTER_MOTION_MASK |
956 Gdk::POINTER_MOTION_HINT_MASK,
957 Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW),
958 event->time);
959 #endif
960 resize.active = true;
961 } else {
962 int ydim = int(event->y / h);
963 int dim;
964 for (dim = 0 ; dim < region->Dimensions ; dim++) {
965 if (region->pDimensionDefinitions[dim].bits == 0) continue;
966 if (ydim == 0) break;
967 ydim--;
968 }
969 int nbZones = region->pDimensionDefinitions[dim].zones;
970
971 int z = -1;
972 int bitpos = 0;
973 for (int i = 0 ; i < dim ; i++) {
974 bitpos += region->pDimensionDefinitions[i].bits;
975 }
976
977 int i = dim;
978 if (maindimregno < 0) maindimregno = 0;
979 int mask = ~(((1 << region->pDimensionDefinitions[i].bits) - 1) << bitpos);
980 int c = this->maindimregno & mask; // mask away this dimension
981
982 bool customsplits =
983 ((region->pDimensionDefinitions[i].split_type == gig::split_type_normal &&
984 region->pDimensionRegions[c]->DimensionUpperLimits[i]) ||
985 (region->pDimensionDefinitions[i].dimension == gig::dimension_velocity &&
986 region->pDimensionRegions[c]->VelocityUpperLimit));
987 if (customsplits) {
988 int val = int((event->x - label_width) * 128 / (w - label_width - 1));
989
990 if (region->pDimensionRegions[c]->DimensionUpperLimits[i]) {
991 for (z = 0 ; z < nbZones ; z++) {
992 gig::DimensionRegion* d = region->pDimensionRegions[c + (z << bitpos)];
993 if (val <= d->DimensionUpperLimits[i]) break;
994 }
995 } else {
996 for (z = 0 ; z < nbZones ; z++) {
997 gig::DimensionRegion* d = region->pDimensionRegions[c + (z << bitpos)];
998 if (val <= d->VelocityUpperLimit) break;
999 }
1000 }
1001 } else {
1002 z = int((event->x - label_width) * nbZones / (w - label_width - 1));
1003 }
1004
1005 printf("dim=%d z=%d dimensionsource=%d split_type=%d zones=%d zone_size=%f\n", dim, z,
1006 region->pDimensionDefinitions[dim].dimension,
1007 region->pDimensionDefinitions[dim].split_type,
1008 region->pDimensionDefinitions[dim].zones,
1009 region->pDimensionDefinitions[dim].zone_size);
1010 this->maindimcase[region->pDimensionDefinitions[dim].dimension] = z;
1011 this->maindimregno = c | (z << bitpos);
1012 this->maindimtype = region->pDimensionDefinitions[dim].dimension;
1013
1014 if (multiSelectKeyDown) {
1015 if (dimzones[this->maindimtype].count(z)) {
1016 if (dimzones[this->maindimtype].size() > 1) {
1017 dimzones[this->maindimtype].erase(z);
1018 }
1019 } else {
1020 dimzones[this->maindimtype].insert(z);
1021 }
1022 } else {
1023 this->dimzones.clear();
1024 for (std::map<gig::dimension_t,int>::const_iterator it = this->maindimcase.begin();
1025 it != this->maindimcase.end(); ++it)
1026 {
1027 this->dimzones[it->first].insert(it->second);
1028 }
1029 }
1030
1031 focus_line = dim;
1032 if (has_focus()) queue_draw();
1033 else grab_focus();
1034 dimregion_selected();
1035
1036 if (event->button == 3) {
1037 printf("dimregion right click\n");
1038 popup_menu_inside_dimregion->popup(event->button, event->time);
1039 }
1040
1041 queue_draw();
1042 }
1043 }
1044 return true;
1045 }
1046
1047 bool DimRegionChooser::on_motion_notify_event(GdkEventMotion* event)
1048 {
1049 Glib::RefPtr<Gdk::Window> window = get_window();
1050 int x, y;
1051 Gdk::ModifierType state = Gdk::ModifierType(0);
1052 window->get_pointer(x, y, state);
1053
1054 if (resize.active) {
1055 int w = get_width();
1056 int k = int((x - label_width) * 128.0 / (w - label_width - 1) + 0.5);
1057
1058 if (k < resize.min) k = resize.min;
1059 else if (k > resize.max) k = resize.max;
1060
1061 if (k < 2) k = 2; // k is upper limit + 1, upper limit 0 is forbidden
1062
1063 if (k != resize.pos) {
1064 int prevx = int((w - label_width - 1) * resize.pos / 128.0 + 0.5) + label_width;
1065 int x = int((w - label_width - 1) * k / 128.0 + 0.5) + label_width;
1066 int y = resize.dimension * h;
1067 int x1, x2;
1068 if (k > resize.pos) {
1069 x1 = prevx;
1070 x2 = x;
1071 } else {
1072 x1 = x;
1073 x2 = prevx;
1074 }
1075 Gdk::Rectangle rect(x1, y + 1, x2 - x1 + 1, h - 2);
1076
1077 resize.pos = k;
1078 update_after_resize();
1079 get_window()->invalidate_rect(rect, false); // not sufficient ...
1080 queue_draw(); // ... so do a complete redraw instead.
1081 }
1082 } else {
1083 if (is_in_resize_zone(x, y)) {
1084 if (!cursor_is_resize) {
1085 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
1086 window->set_cursor(Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW));
1087 #else
1088 window->set_cursor(Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW));
1089 #endif
1090 cursor_is_resize = true;
1091 }
1092 } else if (cursor_is_resize) {
1093 window->set_cursor();
1094 cursor_is_resize = false;
1095 }
1096 }
1097 return true;
1098 }
1099
1100 bool DimRegionChooser::is_in_resize_zone(double x, double y)
1101 {
1102 int w = get_width();
1103 if (region && y < nbDimensions * h && x >= label_width && x < w) {
1104 int ydim = int(y / h);
1105 int dim;
1106 int bitpos = 0;
1107 for (dim = 0 ; dim < region->Dimensions ; dim++) {
1108 if (region->pDimensionDefinitions[dim].bits == 0) continue;
1109 if (ydim == 0) break;
1110 ydim--;
1111 bitpos += region->pDimensionDefinitions[dim].bits;
1112 }
1113 int nbZones = region->pDimensionDefinitions[dim].zones;
1114
1115 int c = 0;
1116 if (maindimregno >= 0) {
1117 int mask = ~(((1 << region->pDimensionDefinitions[dim].bits) - 1) << bitpos);
1118 c = maindimregno & mask; // mask away this dimension
1119 }
1120 const bool customsplits =
1121 ((region->pDimensionDefinitions[dim].split_type == gig::split_type_normal &&
1122 region->pDimensionRegions[c]->DimensionUpperLimits[dim]) ||
1123 (region->pDimensionDefinitions[dim].dimension == gig::dimension_velocity &&
1124 region->pDimensionRegions[c]->VelocityUpperLimit));
1125
1126 // dimensions of split_type_bit cannot be resized
1127 if (region->pDimensionDefinitions[dim].split_type != gig::split_type_bit) {
1128 int prev_limit = 0;
1129 for (int iZone = 0 ; iZone < nbZones - 1 ; iZone++) {
1130 gig::DimensionRegion* d = region->pDimensionRegions[c + (iZone << bitpos)];
1131 const int upperLimit =
1132 (customsplits) ?
1133 (d->DimensionUpperLimits[dim]) ?
1134 d->DimensionUpperLimits[dim] : d->VelocityUpperLimit
1135 : (iZone+1) * (int)region->pDimensionDefinitions[dim].zone_size - 1;
1136 int limit = upperLimit + 1;
1137 int limitx = int((w - label_width - 1) * limit / 128.0 + 0.5) + label_width;
1138 if (x <= limitx - 2) break;
1139 if (x <= limitx + 2) {
1140 resize.dimension = dim;
1141 resize.dimensionDef = region->pDimensionDefinitions[dim];
1142 resize.zone = iZone;
1143 resize.pos = limit;
1144 resize.min = prev_limit;
1145
1146 int dr = (maindimregno >> bitpos) &
1147 ((1 << region->pDimensionDefinitions[dim].bits) - 1);
1148 resize.selected = dr == iZone ? resize.left :
1149 dr == iZone + 1 ? resize.right : resize.none;
1150
1151 iZone++;
1152 gig::DimensionRegion* d = region->pDimensionRegions[c + (iZone << bitpos)];
1153
1154 const int upperLimit =
1155 (customsplits) ?
1156 (d->DimensionUpperLimits[dim]) ?
1157 d->DimensionUpperLimits[dim] : d->VelocityUpperLimit
1158 : (iZone+1) * (int)region->pDimensionDefinitions[dim].zone_size - 1;
1159
1160 int limit = upperLimit + 1;
1161 resize.max = limit;
1162 return true;
1163 }
1164 prev_limit = limit;
1165 }
1166 }
1167 }
1168 return false;
1169 }
1170
1171 sigc::signal<void>& DimRegionChooser::signal_dimregion_selected()
1172 {
1173 return dimregion_selected;
1174 }
1175
1176 sigc::signal<void>& DimRegionChooser::signal_region_changed()
1177 {
1178 return region_changed;
1179 }
1180
1181 bool DimRegionChooser::on_focus(Gtk::DirectionType direction)
1182 {
1183 // TODO: check that region exists etc, that is, that it's possible
1184 // to set focus
1185 if (direction == Gtk::DIR_TAB_FORWARD ||
1186 direction == Gtk::DIR_DOWN) {
1187 if (!has_focus()) {
1188 focus_line = 0;
1189 grab_focus();
1190 return true;
1191 } else {
1192 if (focus_line + 1 < region->Dimensions) {
1193 focus_line++;
1194 queue_draw();
1195 return true;
1196 } else {
1197 return false;
1198 }
1199 }
1200 } else if (direction == Gtk::DIR_TAB_BACKWARD ||
1201 direction == Gtk::DIR_UP) {
1202 if (!has_focus()) {
1203 focus_line = region->Dimensions - 1;
1204 grab_focus();
1205 return true;
1206 } else {
1207 if (focus_line > 0) {
1208 focus_line--;
1209 queue_draw();
1210 return true;
1211 } else {
1212 return false;
1213 }
1214 }
1215 } else if (!has_focus()) {
1216 // TODO: check that focus_line exists
1217 grab_focus();
1218 return true;
1219 } else {
1220 // TODO: increase or decrease value
1221 }
1222 return false;
1223 }
1224
1225 void DimRegionChooser::split_dimension_zone() {
1226 printf("split_dimension_zone() type=%d, zone=%d\n", maindimtype, maindimcase[maindimtype]);
1227 try {
1228 if (!modifyallregions) {
1229 region->SplitDimensionZone(maindimtype, maindimcase[maindimtype]);
1230 } else {
1231 gig::Instrument* instr = (gig::Instrument*)region->GetParent();
1232 gig::dimension_def_t* pMaindimdef = region->GetDimensionDefinition(maindimtype);
1233 assert(pMaindimdef != NULL);
1234 // retain structure by value since the original region will be
1235 // modified in the loop below as well
1236 gig::dimension_def_t maindimdef = *pMaindimdef;
1237 std::vector<gig::Region*> ignoredAll;
1238 std::vector<gig::Region*> ignoredMinor;
1239 std::vector<gig::Region*> ignoredCritical;
1240 gig::Region* rgn = NULL;
1241 for (int key = 0; key < 128; ++key) {
1242 if (!instr->GetRegion(key) || instr->GetRegion(key) == rgn) continue;
1243 rgn = instr->GetRegion(key);
1244
1245 // ignore all regions which do not exactly match the dimension
1246 // layout of the selected region where this operation was emitted
1247 gig::dimension_def_t* dimdef = rgn->GetDimensionDefinition(maindimtype);
1248 if (!dimdef) {
1249 ignoredAll.push_back(rgn);
1250 ignoredMinor.push_back(rgn);
1251 continue;
1252 }
1253 if (dimdef->zones != maindimdef.zones) {
1254 ignoredAll.push_back(rgn);
1255 ignoredCritical.push_back(rgn);
1256 continue;
1257 }
1258
1259 rgn->SplitDimensionZone(maindimtype, maindimcase[maindimtype]);
1260 }
1261 if (!ignoredAll.empty()) {
1262 Glib::ustring txt;
1263 if (ignoredCritical.empty())
1264 txt = ToString(ignoredMinor.size()) + _(" regions have been ignored since they don't have that dimension type.");
1265 else if (ignoredMinor.empty())
1266 txt = ToString(ignoredCritical.size()) + _(" regions have been ignored due to different amount of dimension zones!");
1267 else
1268 txt = ToString(ignoredCritical.size()) + _(" regions have been ignored due to different amount of dimension zones (and ") +
1269 ToString(ignoredMinor.size()) + _(" regions have been ignored since they don't have that dimension type)!");
1270 Gtk::MessageType type = (ignoredCritical.empty()) ? Gtk::MESSAGE_INFO : Gtk::MESSAGE_WARNING;
1271 Gtk::MessageDialog msg(txt, false, type);
1272 msg.run();
1273 }
1274 }
1275 } catch (RIFF::Exception e) {
1276 Gtk::MessageDialog msg(e.Message, false, Gtk::MESSAGE_ERROR);
1277 msg.run();
1278 } catch (...) {
1279 Glib::ustring txt = _("An unknown exception occurred!");
1280 Gtk::MessageDialog msg(txt, false, Gtk::MESSAGE_ERROR);
1281 msg.run();
1282 }
1283 refresh_all();
1284 }
1285
1286 void DimRegionChooser::delete_dimension_zone() {
1287 printf("delete_dimension_zone() type=%d, zone=%d\n", maindimtype, maindimcase[maindimtype]);
1288 try {
1289 if (!modifyallregions) {
1290 region->DeleteDimensionZone(maindimtype, maindimcase[maindimtype]);
1291 } else {
1292 gig::Instrument* instr = (gig::Instrument*)region->GetParent();
1293 gig::dimension_def_t* pMaindimdef = region->GetDimensionDefinition(maindimtype);
1294 assert(pMaindimdef != NULL);
1295 // retain structure by value since the original region will be
1296 // modified in the loop below as well
1297 gig::dimension_def_t maindimdef = *pMaindimdef;
1298 std::vector<gig::Region*> ignoredAll;
1299 std::vector<gig::Region*> ignoredMinor;
1300 std::vector<gig::Region*> ignoredCritical;
1301 gig::Region* rgn = NULL;
1302 for (int key = 0; key < 128; ++key) {
1303 if (!instr->GetRegion(key) || instr->GetRegion(key) == rgn) continue;
1304 rgn = instr->GetRegion(key);
1305
1306 // ignore all regions which do not exactly match the dimension
1307 // layout of the selected region where this operation was emitted
1308 gig::dimension_def_t* dimdef = rgn->GetDimensionDefinition(maindimtype);
1309 if (!dimdef) {
1310 ignoredAll.push_back(rgn);
1311 ignoredMinor.push_back(rgn);
1312 continue;
1313 }
1314 if (dimdef->zones != maindimdef.zones) {
1315 ignoredAll.push_back(rgn);
1316 ignoredCritical.push_back(rgn);
1317 continue;
1318 }
1319
1320 rgn->DeleteDimensionZone(maindimtype, maindimcase[maindimtype]);
1321 }
1322 if (!ignoredAll.empty()) {
1323 Glib::ustring txt;
1324 if (ignoredCritical.empty())
1325 txt = ToString(ignoredMinor.size()) + _(" regions have been ignored since they don't have that dimension type.");
1326 else if (ignoredMinor.empty())
1327 txt = ToString(ignoredCritical.size()) + _(" regions have been ignored due to different amount of dimension zones!");
1328 else
1329 txt = ToString(ignoredCritical.size()) + _(" regions have been ignored due to different amount of dimension zones (and ") +
1330 ToString(ignoredMinor.size()) + _(" regions have been ignored since they don't have that dimension type)!");
1331 Gtk::MessageType type = (ignoredCritical.empty()) ? Gtk::MESSAGE_INFO : Gtk::MESSAGE_WARNING;
1332 Gtk::MessageDialog msg(txt, false, type);
1333 msg.run();
1334 }
1335 }
1336 } catch (RIFF::Exception e) {
1337 Gtk::MessageDialog msg(e.Message, false, Gtk::MESSAGE_ERROR);
1338 msg.run();
1339 } catch (...) {
1340 Glib::ustring txt = _("An unknown exception occurred!");
1341 Gtk::MessageDialog msg(txt, false, Gtk::MESSAGE_ERROR);
1342 msg.run();
1343 }
1344 refresh_all();
1345 }
1346
1347 // Cmd key on Mac, Ctrl key on all other OSs
1348 static const guint primaryKeyL =
1349 #if defined(__APPLE__)
1350 GDK_KEY_Meta_L;
1351 #else
1352 GDK_KEY_Control_L;
1353 #endif
1354
1355 static const guint primaryKeyR =
1356 #if defined(__APPLE__)
1357 GDK_KEY_Meta_R;
1358 #else
1359 GDK_KEY_Control_R;
1360 #endif
1361
1362 bool DimRegionChooser::onKeyPressed(GdkEventKey* key) {
1363 //printf("key down 0x%x\n", key->keyval);
1364 if (key->keyval == GDK_KEY_Control_L || key->keyval == GDK_KEY_Control_R)
1365 multiSelectKeyDown = true;
1366 if (key->keyval == primaryKeyL || key->keyval == primaryKeyR)
1367 primaryKeyDown = true;
1368 if (key->keyval == GDK_KEY_Shift_L || key->keyval == GDK_KEY_Shift_R)
1369 shiftKeyDown = true;
1370
1371 //FIXME: hmm, for some reason GDKMM does not fire arrow key down events, so we are doing those handlers in the key up handler instead for now
1372 /*if (key->keyval == GDK_KEY_Left)
1373 select_prev_dimzone();
1374 if (key->keyval == GDK_KEY_Right)
1375 select_next_dimzone();
1376 if (key->keyval == GDK_KEY_Up)
1377 select_prev_dimension();
1378 if (key->keyval == GDK_KEY_Down)
1379 select_next_dimension();*/
1380 return false;
1381 }
1382
1383 bool DimRegionChooser::onKeyReleased(GdkEventKey* key) {
1384 //printf("key up 0x%x\n", key->keyval);
1385 if (key->keyval == GDK_KEY_Control_L || key->keyval == GDK_KEY_Control_R)
1386 multiSelectKeyDown = false;
1387 if (key->keyval == primaryKeyL || key->keyval == primaryKeyR)
1388 primaryKeyDown = false;
1389 if (key->keyval == GDK_KEY_Shift_L || key->keyval == GDK_KEY_Shift_R)
1390 shiftKeyDown = false;
1391
1392 if (!has_focus()) return false;
1393
1394 // avoid conflict with Ctrl+Left and Ctrl+Right accelerators on mainwindow
1395 // (which is supposed to switch between regions)
1396 if (primaryKeyDown) return false;
1397
1398 // avoid conflict with Alt+Shift+Left and Alt+Shift+Right accelerators on
1399 // mainwindow
1400 if (shiftKeyDown) return false;
1401
1402 if (key->keyval == GDK_KEY_Left)
1403 select_prev_dimzone();
1404 if (key->keyval == GDK_KEY_Right)
1405 select_next_dimzone();
1406 if (key->keyval == GDK_KEY_Up)
1407 select_prev_dimension();
1408 if (key->keyval == GDK_KEY_Down)
1409 select_next_dimension();
1410
1411 return false;
1412 }
1413
1414 void DimRegionChooser::resetSelectedZones() {
1415 this->dimzones.clear();
1416 if (!region) {
1417 queue_draw(); // redraw required parts
1418 return;
1419 }
1420 if (maindimregno < 0 || maindimregno >= region->DimensionRegions) {
1421 queue_draw(); // redraw required parts
1422 return;
1423 }
1424 if (!region->pDimensionRegions[maindimregno]) {
1425 queue_draw(); // redraw required parts
1426 return;
1427 }
1428 gig::DimensionRegion* dimrgn = region->pDimensionRegions[maindimregno];
1429
1430 bool isValidZone;
1431 this->maindimcase = dimensionCaseOf(dimrgn);
1432
1433 for (std::map<gig::dimension_t,int>::const_iterator it = this->maindimcase.begin();
1434 it != this->maindimcase.end(); ++it)
1435 {
1436 this->dimzones[it->first].insert(it->second);
1437 }
1438
1439 // redraw required parts
1440 queue_draw();
1441 }
1442
1443 bool DimRegionChooser::select_dimregion(gig::DimensionRegion* dimrgn) {
1444 if (!region) return false; //.selection failed
1445
1446 for (int dr = 0; dr < region->DimensionRegions && region->pDimensionRegions[dr]; ++dr) {
1447 if (region->pDimensionRegions[dr] == dimrgn) {
1448 // reset dim region zone selection to the requested specific dim region case
1449 maindimregno = dr;
1450 resetSelectedZones();
1451
1452 // emit signal that dimregion selection has changed, for external entities
1453 dimregion_selected();
1454
1455 return true; // selection success
1456 }
1457 }
1458
1459 return false; //.selection failed
1460 }
1461
1462 void DimRegionChooser::select_next_dimzone(bool add) {
1463 select_dimzone_by_dir(+1, add);
1464 }
1465
1466 void DimRegionChooser::select_prev_dimzone(bool add) {
1467 select_dimzone_by_dir(-1, add);
1468 }
1469
1470 void DimRegionChooser::select_dimzone_by_dir(int dir, bool add) {
1471 if (!region) return;
1472 if (!region->Dimensions) return;
1473 if (focus_line < 0) focus_line = 0;
1474 if (focus_line >= region->Dimensions) focus_line = region->Dimensions - 1;
1475
1476 maindimtype = region->pDimensionDefinitions[focus_line].dimension;
1477 if (maindimtype == gig::dimension_none) {
1478 printf("maindimtype -> none\n");
1479 return;
1480 }
1481
1482 if (maindimcase.empty()) {
1483 maindimcase = dimensionCaseOf(region->pDimensionRegions[maindimregno]);
1484 if (maindimcase.empty()) {
1485 printf("caseOfDimregion(%d) -> empty\n", maindimregno);
1486 return;
1487 }
1488 }
1489
1490 int z = (dir > 0) ? maindimcase[maindimtype] + 1 : maindimcase[maindimtype] - 1;
1491 if (z < 0) z = 0;
1492 if (z >= region->pDimensionDefinitions[focus_line].zones)
1493 z = region->pDimensionDefinitions[focus_line].zones - 1;
1494
1495 maindimcase[maindimtype] = z;
1496
1497 ::gig::DimensionRegion* dr = dimensionRegionMatching(maindimcase, region);
1498 if (!dr) {
1499 printf("select_dimzone_by_dir(%d) -> !dr\n", dir);
1500 return;
1501 }
1502
1503 maindimregno = getDimensionRegionIndex(dr);
1504
1505 if (!add) {
1506 // reset selected dimregion zones
1507 dimzones.clear();
1508 }
1509 for (DimensionCase::const_iterator it = maindimcase.begin();
1510 it != maindimcase.end(); ++it)
1511 {
1512 dimzones[it->first].insert(it->second);
1513 }
1514
1515 dimregion_selected();
1516
1517 // disabled: would overwrite dimregno with wrong value
1518 //refresh_all();
1519 // so requesting just a raw repaint instead:
1520 queue_draw();
1521 }
1522
1523 void DimRegionChooser::select_next_dimension() {
1524 if (!region) return;
1525 focus_line++;
1526 if (focus_line >= region->Dimensions)
1527 focus_line = region->Dimensions - 1;
1528 this->maindimtype = region->pDimensionDefinitions[focus_line].dimension;
1529 queue_draw();
1530 }
1531
1532 void DimRegionChooser::select_prev_dimension() {
1533 if (!region) return;
1534 focus_line--;
1535 if (focus_line < 0)
1536 focus_line = 0;
1537 this->maindimtype = region->pDimensionDefinitions[focus_line].dimension;
1538 queue_draw();
1539 }
1540
1541 gig::DimensionRegion* DimRegionChooser::get_main_dimregion() const {
1542 if (!region) return NULL;
1543 return region->pDimensionRegions[maindimregno];
1544 }

  ViewVC Help
Powered by ViewVC