/[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 3703 - (show annotations) (download)
Wed Jan 8 15:49:24 2020 UTC (4 years, 3 months ago) by schoenebeck
File size: 71083 byte(s)
* Fix: dimregion edit actions were ignored if user did not click (ever) on
  any dimension zone yet.

* Bumped version (1.1.1.svn10).

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

  ViewVC Help
Powered by ViewVC