/[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 3364 - (show annotations) (download)
Tue Nov 14 18:07:25 2017 UTC (6 years, 5 months ago) by schoenebeck
File size: 70291 byte(s)
* Added experimental support for upcoming GTK(MM)4
  (for now up to GTKMM 3.91.2 while still preserving backward compatibility
  down to GTKMM 2).
* Re-merged r2845 to compile now with and without Gtk "Stock ID" API
  (see also r3158).

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

  ViewVC Help
Powered by ViewVC