/[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 3409 - (show annotations) (download)
Tue Jan 23 16:30:56 2018 UTC (6 years, 2 months ago) by schoenebeck
File size: 70641 byte(s)
* Added new main menu item "View" -> "Tooltips for Beginners" which allows
  to disable tooltips intended for newbies only (default: on).
* Bumped version (1.1.0.svn3).

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 "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 > 22)
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 itSelectedDimension->second.count(it->second)) continue; // is selected
828
829 goto notSelected;
830 }
831
832 dimregs.insert(dimRgn);
833
834 notSelected:
835 ;
836 }
837 }
838
839 void DimRegionChooser::update_after_resize()
840 {
841 const uint8_t upperLimit = resize.pos - 1;
842 gig::Instrument* instr = (gig::Instrument*)region->GetParent();
843
844 int bitpos = 0;
845 for (int j = 0 ; j < resize.dimension ; j++) {
846 bitpos += region->pDimensionDefinitions[j].bits;
847 }
848
849 const int stereobitpos =
850 (modifybothchannels) ? baseBits(gig::dimension_samplechannel, region) : -1;
851
852 // the velocity dimension must be handled differently than all other
853 // dimension types, because
854 // 1. it is currently the only dimension type which allows different zone
855 // sizes for different cases
856 // 2. for v2 format VelocityUpperLimit has to be set, DimensionUpperLimits for v3
857 if (region->pDimensionDefinitions[resize.dimension].dimension == gig::dimension_velocity) {
858 int mask =
859 ~(((1 << region->pDimensionDefinitions[resize.dimension].bits) - 1) << bitpos);
860 int c = maindimregno & mask; // mask away this dimension
861
862 if (region->pDimensionRegions[c]->DimensionUpperLimits[resize.dimension] == 0) {
863 // the velocity dimension didn't previously have
864 // custom v3 splits, so we initialize all splits with
865 // default values
866 int nbZones = region->pDimensionDefinitions[resize.dimension].zones;
867 for (int j = 0 ; j < nbZones ; j++) {
868 gig::DimensionRegion* d = region->pDimensionRegions[c + (j << bitpos)];
869 d->DimensionUpperLimits[resize.dimension] = int(128.0 * (j + 1) / nbZones - 1);
870 }
871 }
872 if (region->pDimensionRegions[c]->VelocityUpperLimit == 0) {
873 // the velocity dimension didn't previously have
874 // custom v2 splits, so we initialize all splits with
875 // default values
876 int nbZones = region->pDimensionDefinitions[resize.dimension].zones;
877 for (int j = 0 ; j < nbZones ; j++) {
878 gig::DimensionRegion* d = region->pDimensionRegions[c + (j << bitpos)];
879 d->VelocityUpperLimit = int(128.0 * (j + 1) / nbZones - 1);
880 }
881 }
882
883 int index = c + (resize.zone << bitpos);
884 gig::DimensionRegion* d = region->pDimensionRegions[index];
885 // update both v2 and v3 values
886 d->DimensionUpperLimits[resize.dimension] = upperLimit;
887 d->VelocityUpperLimit = upperLimit;
888 if (modifybothchannels && stereobitpos >= 0) { // do the same for the other audio channel's dimregion ...
889 gig::DimensionRegion* d = region->pDimensionRegions[index ^ (1 << stereobitpos)];
890 d->DimensionUpperLimits[resize.dimension] = upperLimit;
891 d->VelocityUpperLimit = upperLimit;
892 }
893
894 if (modifyalldimregs) {
895 gig::Region* rgn = NULL;
896 for (int key = 0; key < 128; ++key) {
897 if (!instr->GetRegion(key) || instr->GetRegion(key) == rgn) continue;
898 rgn = instr->GetRegion(key);
899 if (!modifyallregions && rgn != region) continue; // hack to reduce overall code amount a bit
900 gig::dimension_def_t* dimdef = rgn->GetDimensionDefinition(resize.dimensionDef.dimension);
901 if (!dimdef) continue;
902 if (dimdef->zones != resize.dimensionDef.zones) continue;
903 const int iDim = getDimensionIndex(resize.dimensionDef.dimension, rgn);
904 assert(iDim >= 0 && iDim < rgn->Dimensions);
905
906 // the dimension layout might be completely different in this
907 // region, so we have to recalculate bitpos etc for this region
908 const int bitpos = baseBits(resize.dimensionDef.dimension, rgn);
909 const int stencil = ~(((1 << dimdef->bits) - 1) << bitpos);
910 const int selection = resize.zone << bitpos;
911
912 // primitive and inefficient loop implementation, however due to
913 // this circumstance the loop code is much simpler, and its lack
914 // of runtime efficiency should not be notable in practice
915 for (int idr = 0; idr < 256; ++idr) {
916 const int index = (idr & stencil) | selection;
917 assert(index >= 0 && index < 256);
918 gig::DimensionRegion* dr = rgn->pDimensionRegions[index];
919 if (!dr) continue;
920 dr->DimensionUpperLimits[iDim] = upperLimit;
921 d->VelocityUpperLimit = upperLimit;
922 }
923 }
924 } else if (modifyallregions) { // implies modifyalldimregs is false ...
925 // resolve the precise case we need to modify for all other regions
926 DimensionCase dimCase = dimensionCaseOf(d);
927 // apply the velocity upper limit change to that resolved dim case
928 // of all regions ...
929 gig::Region* rgn = NULL;
930 for (int key = 0; key < 128; ++key) {
931 if (!instr->GetRegion(key) || instr->GetRegion(key) == rgn) continue;
932 rgn = instr->GetRegion(key);
933 gig::dimension_def_t* dimdef = rgn->GetDimensionDefinition(resize.dimensionDef.dimension);
934 if (!dimdef) continue;
935 if (dimdef->zones != resize.dimensionDef.zones) continue;
936 const int iDim = getDimensionIndex(resize.dimensionDef.dimension, rgn);
937 assert(iDim >= 0 && iDim < rgn->Dimensions);
938
939 std::vector<gig::DimensionRegion*> dimrgns = dimensionRegionsMatching(dimCase, rgn);
940 for (int i = 0; i < dimrgns.size(); ++i) {
941 gig::DimensionRegion* dr = dimrgns[i];
942 dr->DimensionUpperLimits[iDim] = upperLimit;
943 dr->VelocityUpperLimit = upperLimit;
944 }
945 }
946 }
947 } else {
948 for (int i = 0 ; i < region->DimensionRegions ; ) {
949 if (region->pDimensionRegions[i]->DimensionUpperLimits[resize.dimension] == 0) {
950 // the dimension didn't previously have custom
951 // limits, so we have to set default limits for
952 // all the dimension regions
953 int nbZones = region->pDimensionDefinitions[resize.dimension].zones;
954
955 for (int j = 0 ; j < nbZones ; j++) {
956 gig::DimensionRegion* d = region->pDimensionRegions[i + (j << bitpos)];
957 d->DimensionUpperLimits[resize.dimension] = int(128.0 * (j + 1) / nbZones - 1);
958 }
959 }
960 int index = i + (resize.zone << bitpos);
961 gig::DimensionRegion* d = region->pDimensionRegions[index];
962 d->DimensionUpperLimits[resize.dimension] = upperLimit;
963 #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
964 if (modifybothchannels && stereobitpos >= 0) { // do the same for the other audio channel's dimregion ...
965 gig::DimensionRegion* d = region->pDimensionRegions[index ^ (1 << stereobitpos)];
966 d->DimensionUpperLimits[resize.dimension] = upperLimit;
967 }
968 #endif
969 int bitpos = 0;
970 int j;
971 for (j = 0 ; j < region->Dimensions ; j++) {
972 if (j != resize.dimension) {
973 int maxzones = 1 << region->pDimensionDefinitions[j].bits;
974 int dimj = (i >> bitpos) & (maxzones - 1);
975 if (dimj + 1 < region->pDimensionDefinitions[j].zones) break;
976 }
977 bitpos += region->pDimensionDefinitions[j].bits;
978 }
979 if (j == region->Dimensions) break;
980 i = (i & ~((1 << bitpos) - 1)) + (1 << bitpos);
981 }
982
983 if (modifyallregions) { // TODO: this code block could be merged with the similar (and more generalized) code block of the velocity dimension above
984 gig::Region* rgn = NULL;
985 for (int key = 0; key < 128; ++key) {
986 if (!instr->GetRegion(key) || instr->GetRegion(key) == rgn) continue;
987 rgn = instr->GetRegion(key);
988 gig::dimension_def_t* dimdef = rgn->GetDimensionDefinition(resize.dimensionDef.dimension);
989 if (!dimdef) continue;
990 if (dimdef->zones != resize.dimensionDef.zones) continue;
991 const int iDim = getDimensionIndex(resize.dimensionDef.dimension, rgn);
992 assert(iDim >= 0 && iDim < rgn->Dimensions);
993
994 // the dimension layout might be completely different in this
995 // region, so we have to recalculate bitpos etc for this region
996 const int bitpos = baseBits(resize.dimensionDef.dimension, rgn);
997 const int stencil = ~(((1 << dimdef->bits) - 1) << bitpos);
998 const int selection = resize.zone << bitpos;
999
1000 // this loop implementation is less efficient than the above's
1001 // loop implementation (which skips unnecessary dimension regions)
1002 // however this code is much simpler, and its lack of runtime
1003 // efficiency should not be notable in practice
1004 for (int idr = 0; idr < 256; ++idr) {
1005 const int index = (idr & stencil) | selection;
1006 assert(index >= 0 && index < 256);
1007 gig::DimensionRegion* dr = rgn->pDimensionRegions[index];
1008 if (!dr) continue;
1009 dr->DimensionUpperLimits[iDim] = upperLimit;
1010 }
1011 }
1012 }
1013 }
1014 }
1015
1016 bool DimRegionChooser::on_button_release_event(GdkEventButton* event)
1017 {
1018 if (resize.active) {
1019 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
1020 get_window()->pointer_ungrab(event->time);
1021 #else
1022 # if GTKMM_MAJOR_VERSION < 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION < 20)
1023 Glib::wrap(event->device, true)->ungrab(event->time);
1024 # else
1025 gdk_device_ungrab(Glib::wrap(event->device, true)->gobj(), event->time);
1026 # endif
1027 #endif
1028 resize.active = false;
1029
1030 region_changed();
1031
1032 if (!is_in_resize_zone(event->x, event->y) && cursor_is_resize) {
1033 get_window()->set_cursor();
1034 cursor_is_resize = false;
1035 }
1036 }
1037 return true;
1038 }
1039
1040 bool DimRegionChooser::on_button_press_event(GdkEventButton* event)
1041 {
1042 int w = get_width();
1043 if (region && event->y < nbDimensions * h &&
1044 event->x >= label_width && event->x < w) {
1045
1046 if (is_in_resize_zone(event->x, event->y)) {
1047 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
1048 get_window()->pointer_grab(false,
1049 Gdk::BUTTON_RELEASE_MASK |
1050 Gdk::POINTER_MOTION_MASK |
1051 Gdk::POINTER_MOTION_HINT_MASK,
1052 Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW),
1053 event->time);
1054 #else
1055 # if GTKMM_MAJOR_VERSION < 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION < 20)
1056 Glib::wrap(event->device, true)->grab(get_window(),
1057 Gdk::OWNERSHIP_NONE,
1058 false,
1059 Gdk::BUTTON_RELEASE_MASK |
1060 Gdk::POINTER_MOTION_MASK |
1061 Gdk::POINTER_MOTION_HINT_MASK,
1062 Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW),
1063 event->time);
1064 # else
1065 gdk_device_grab(
1066 Glib::wrap(event->device, true)->gobj(),
1067 get_window()->gobj(),
1068 GDK_OWNERSHIP_NONE,
1069 false,
1070 GdkEventMask(GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK |
1071 GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON1_MOTION_MASK),
1072 Gdk::Cursor::create(
1073 Glib::wrap(event->device, true)->get_seat()->get_display(),
1074 Gdk::SB_H_DOUBLE_ARROW
1075 )->gobj(),
1076 event->time
1077 );
1078 # endif
1079 #endif
1080 resize.active = true;
1081 } else {
1082 int ydim = int(event->y / h);
1083 int dim;
1084 for (dim = 0 ; dim < region->Dimensions ; dim++) {
1085 if (region->pDimensionDefinitions[dim].bits == 0) continue;
1086 if (ydim == 0) break;
1087 ydim--;
1088 }
1089 int nbZones = region->pDimensionDefinitions[dim].zones;
1090
1091 int z = -1;
1092 int bitpos = 0;
1093 for (int i = 0 ; i < dim ; i++) {
1094 bitpos += region->pDimensionDefinitions[i].bits;
1095 }
1096
1097 int i = dim;
1098 if (maindimregno < 0) maindimregno = 0;
1099 int mask = ~(((1 << region->pDimensionDefinitions[i].bits) - 1) << bitpos);
1100 int c = this->maindimregno & mask; // mask away this dimension
1101
1102 bool customsplits =
1103 ((region->pDimensionDefinitions[i].split_type == gig::split_type_normal &&
1104 region->pDimensionRegions[c]->DimensionUpperLimits[i]) ||
1105 (region->pDimensionDefinitions[i].dimension == gig::dimension_velocity &&
1106 region->pDimensionRegions[c]->VelocityUpperLimit));
1107 if (customsplits) {
1108 int val = int((event->x - label_width) * 128 / (w - label_width - 1));
1109
1110 if (region->pDimensionRegions[c]->DimensionUpperLimits[i]) {
1111 for (z = 0 ; z < nbZones ; z++) {
1112 gig::DimensionRegion* d = region->pDimensionRegions[c + (z << bitpos)];
1113 if (val <= d->DimensionUpperLimits[i]) break;
1114 }
1115 } else {
1116 for (z = 0 ; z < nbZones ; z++) {
1117 gig::DimensionRegion* d = region->pDimensionRegions[c + (z << bitpos)];
1118 if (val <= d->VelocityUpperLimit) break;
1119 }
1120 }
1121 } else {
1122 z = int((event->x - label_width) * nbZones / (w - label_width - 1));
1123 }
1124
1125 printf("dim=%d z=%d dimensionsource=%d split_type=%d zones=%d zone_size=%f\n", dim, z,
1126 region->pDimensionDefinitions[dim].dimension,
1127 region->pDimensionDefinitions[dim].split_type,
1128 region->pDimensionDefinitions[dim].zones,
1129 region->pDimensionDefinitions[dim].zone_size);
1130 this->maindimcase[region->pDimensionDefinitions[dim].dimension] = z;
1131 this->maindimregno = c | (z << bitpos);
1132 this->maindimtype = region->pDimensionDefinitions[dim].dimension;
1133
1134 if (multiSelectKeyDown) {
1135 if (dimzones[this->maindimtype].count(z)) {
1136 if (dimzones[this->maindimtype].size() > 1) {
1137 dimzones[this->maindimtype].erase(z);
1138 }
1139 } else {
1140 dimzones[this->maindimtype].insert(z);
1141 }
1142 } else {
1143 this->dimzones.clear();
1144 for (std::map<gig::dimension_t,int>::const_iterator it = this->maindimcase.begin();
1145 it != this->maindimcase.end(); ++it)
1146 {
1147 this->dimzones[it->first].insert(it->second);
1148 }
1149 }
1150
1151 focus_line = dim;
1152 if (has_focus()) queue_draw();
1153 else grab_focus();
1154 dimregion_selected();
1155
1156 if (event->button == 3) {
1157 printf("dimregion right click\n");
1158 popup_menu_inside_dimregion->popup(event->button, event->time);
1159 }
1160
1161 queue_draw();
1162 }
1163 }
1164 return true;
1165 }
1166
1167 bool DimRegionChooser::on_motion_notify_event(GdkEventMotion* event)
1168 {
1169 Glib::RefPtr<Gdk::Window> window = get_window();
1170 int x, y;
1171 #if HAS_GDKMM_SEAT
1172 x = event->x;
1173 y = event->y;
1174 Gdk::ModifierType state = Gdk::ModifierType(event->state);
1175 #else
1176 Gdk::ModifierType state = Gdk::ModifierType(0);
1177 window->get_pointer(x, y, state);
1178 #endif
1179
1180 if (resize.active) {
1181 int w = get_width();
1182 int k = int((x - label_width) * 128.0 / (w - label_width - 1) + 0.5);
1183
1184 if (k < resize.min) k = resize.min;
1185 else if (k > resize.max) k = resize.max;
1186
1187 if (k < 2) k = 2; // k is upper limit + 1, upper limit 0 is forbidden
1188
1189 if (k != resize.pos) {
1190 int prevx = int((w - label_width - 1) * resize.pos / 128.0 + 0.5) + label_width;
1191 int x = int((w - label_width - 1) * k / 128.0 + 0.5) + label_width;
1192 int y = resize.dimension * h;
1193 int x1, x2;
1194 if (k > resize.pos) {
1195 x1 = prevx;
1196 x2 = x;
1197 } else {
1198 x1 = x;
1199 x2 = prevx;
1200 }
1201 Gdk::Rectangle rect(x1, y + 1, x2 - x1 + 1, h - 2);
1202
1203 resize.pos = k;
1204 update_after_resize();
1205 get_window()->invalidate_rect(rect, false); // not sufficient ...
1206 queue_draw(); // ... so do a complete redraw instead.
1207 }
1208 } else {
1209 if (is_in_resize_zone(x, y)) {
1210 if (!cursor_is_resize) {
1211 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
1212 window->set_cursor(Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW));
1213 #else
1214 window->set_cursor(
1215 # if GTKMM_MAJOR_VERSION < 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION < 20)
1216 Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW)
1217 # else
1218 Gdk::Cursor::create(
1219 Glib::wrap(event->device, true)->get_seat()->get_display(),
1220 Gdk::SB_H_DOUBLE_ARROW
1221 )
1222 # endif
1223 );
1224 #endif
1225 cursor_is_resize = true;
1226 }
1227 } else if (cursor_is_resize) {
1228 window->set_cursor();
1229 cursor_is_resize = false;
1230 }
1231 }
1232 return true;
1233 }
1234
1235 bool DimRegionChooser::is_in_resize_zone(double x, double y)
1236 {
1237 int w = get_width();
1238 if (region && y < nbDimensions * h && x >= label_width && x < w) {
1239 int ydim = int(y / h);
1240 int dim;
1241 int bitpos = 0;
1242 for (dim = 0 ; dim < region->Dimensions ; dim++) {
1243 if (region->pDimensionDefinitions[dim].bits == 0) continue;
1244 if (ydim == 0) break;
1245 ydim--;
1246 bitpos += region->pDimensionDefinitions[dim].bits;
1247 }
1248 int nbZones = region->pDimensionDefinitions[dim].zones;
1249
1250 int c = 0;
1251 if (maindimregno >= 0) {
1252 int mask = ~(((1 << region->pDimensionDefinitions[dim].bits) - 1) << bitpos);
1253 c = maindimregno & mask; // mask away this dimension
1254 }
1255 const bool customsplits =
1256 ((region->pDimensionDefinitions[dim].split_type == gig::split_type_normal &&
1257 region->pDimensionRegions[c]->DimensionUpperLimits[dim]) ||
1258 (region->pDimensionDefinitions[dim].dimension == gig::dimension_velocity &&
1259 region->pDimensionRegions[c]->VelocityUpperLimit));
1260
1261 // dimensions of split_type_bit cannot be resized
1262 if (region->pDimensionDefinitions[dim].split_type != gig::split_type_bit) {
1263 int prev_limit = 0;
1264 for (int iZone = 0 ; iZone < nbZones - 1 ; iZone++) {
1265 gig::DimensionRegion* d = region->pDimensionRegions[c + (iZone << bitpos)];
1266 const int upperLimit =
1267 (customsplits) ?
1268 (d->DimensionUpperLimits[dim]) ?
1269 d->DimensionUpperLimits[dim] : d->VelocityUpperLimit
1270 : (iZone+1) * (int)region->pDimensionDefinitions[dim].zone_size - 1;
1271 int limit = upperLimit + 1;
1272 int limitx = int((w - label_width - 1) * limit / 128.0 + 0.5) + label_width;
1273 if (x <= limitx - 2) break;
1274 if (x <= limitx + 2) {
1275 resize.dimension = dim;
1276 resize.dimensionDef = region->pDimensionDefinitions[dim];
1277 resize.zone = iZone;
1278 resize.pos = limit;
1279 resize.min = prev_limit;
1280
1281 int dr = (maindimregno >> bitpos) &
1282 ((1 << region->pDimensionDefinitions[dim].bits) - 1);
1283 resize.selected = dr == iZone ? resize.left :
1284 dr == iZone + 1 ? resize.right : resize.none;
1285
1286 iZone++;
1287 gig::DimensionRegion* d = region->pDimensionRegions[c + (iZone << bitpos)];
1288
1289 const int upperLimit =
1290 (customsplits) ?
1291 (d->DimensionUpperLimits[dim]) ?
1292 d->DimensionUpperLimits[dim] : d->VelocityUpperLimit
1293 : (iZone+1) * (int)region->pDimensionDefinitions[dim].zone_size - 1;
1294
1295 int limit = upperLimit + 1;
1296 resize.max = limit;
1297 return true;
1298 }
1299 prev_limit = limit;
1300 }
1301 }
1302 }
1303 return false;
1304 }
1305
1306 sigc::signal<void>& DimRegionChooser::signal_dimregion_selected()
1307 {
1308 return dimregion_selected;
1309 }
1310
1311 sigc::signal<void>& DimRegionChooser::signal_region_changed()
1312 {
1313 return region_changed;
1314 }
1315
1316 bool DimRegionChooser::on_focus(Gtk::DirectionType direction)
1317 {
1318 // TODO: check that region exists etc, that is, that it's possible
1319 // to set focus
1320 if (direction == Gtk::DIR_TAB_FORWARD ||
1321 direction == Gtk::DIR_DOWN) {
1322 if (!has_focus()) {
1323 focus_line = 0;
1324 grab_focus();
1325 return true;
1326 } else {
1327 if (focus_line + 1 < region->Dimensions) {
1328 focus_line++;
1329 queue_draw();
1330 return true;
1331 } else {
1332 return false;
1333 }
1334 }
1335 } else if (direction == Gtk::DIR_TAB_BACKWARD ||
1336 direction == Gtk::DIR_UP) {
1337 if (!has_focus()) {
1338 focus_line = region->Dimensions - 1;
1339 grab_focus();
1340 return true;
1341 } else {
1342 if (focus_line > 0) {
1343 focus_line--;
1344 queue_draw();
1345 return true;
1346 } else {
1347 return false;
1348 }
1349 }
1350 } else if (!has_focus()) {
1351 // TODO: check that focus_line exists
1352 grab_focus();
1353 return true;
1354 } else {
1355 // TODO: increase or decrease value
1356 }
1357 return false;
1358 }
1359
1360 void DimRegionChooser::split_dimension_zone() {
1361 printf("split_dimension_zone() type=%d, zone=%d\n", maindimtype, maindimcase[maindimtype]);
1362 try {
1363 if (!modifyallregions) {
1364 region->SplitDimensionZone(maindimtype, maindimcase[maindimtype]);
1365 } else {
1366 gig::Instrument* instr = (gig::Instrument*)region->GetParent();
1367 gig::dimension_def_t* pMaindimdef = region->GetDimensionDefinition(maindimtype);
1368 assert(pMaindimdef != NULL);
1369 // retain structure by value since the original region will be
1370 // modified in the loop below as well
1371 gig::dimension_def_t maindimdef = *pMaindimdef;
1372 std::vector<gig::Region*> ignoredAll;
1373 std::vector<gig::Region*> ignoredMinor;
1374 std::vector<gig::Region*> ignoredCritical;
1375 gig::Region* rgn = NULL;
1376 for (int key = 0; key < 128; ++key) {
1377 if (!instr->GetRegion(key) || instr->GetRegion(key) == rgn) continue;
1378 rgn = instr->GetRegion(key);
1379
1380 // ignore all regions which do not exactly match the dimension
1381 // layout of the selected region where this operation was emitted
1382 gig::dimension_def_t* dimdef = rgn->GetDimensionDefinition(maindimtype);
1383 if (!dimdef) {
1384 ignoredAll.push_back(rgn);
1385 ignoredMinor.push_back(rgn);
1386 continue;
1387 }
1388 if (dimdef->zones != maindimdef.zones) {
1389 ignoredAll.push_back(rgn);
1390 ignoredCritical.push_back(rgn);
1391 continue;
1392 }
1393
1394 rgn->SplitDimensionZone(maindimtype, maindimcase[maindimtype]);
1395 }
1396 if (!ignoredAll.empty()) {
1397 Glib::ustring txt;
1398 if (ignoredCritical.empty())
1399 txt = ToString(ignoredMinor.size()) + _(" regions have been ignored since they don't have that dimension type.");
1400 else if (ignoredMinor.empty())
1401 txt = ToString(ignoredCritical.size()) + _(" regions have been ignored due to different amount of dimension zones!");
1402 else
1403 txt = ToString(ignoredCritical.size()) + _(" regions have been ignored due to different amount of dimension zones (and ") +
1404 ToString(ignoredMinor.size()) + _(" regions have been ignored since they don't have that dimension type)!");
1405 Gtk::MessageType type = (ignoredCritical.empty()) ? Gtk::MESSAGE_INFO : Gtk::MESSAGE_WARNING;
1406 Gtk::MessageDialog msg(txt, false, type);
1407 msg.run();
1408 }
1409 }
1410 } catch (RIFF::Exception e) {
1411 Gtk::MessageDialog msg(e.Message, false, Gtk::MESSAGE_ERROR);
1412 msg.run();
1413 } catch (...) {
1414 Glib::ustring txt = _("An unknown exception occurred!");
1415 Gtk::MessageDialog msg(txt, false, Gtk::MESSAGE_ERROR);
1416 msg.run();
1417 }
1418 refresh_all();
1419 }
1420
1421 void DimRegionChooser::delete_dimension_zone() {
1422 printf("delete_dimension_zone() type=%d, zone=%d\n", maindimtype, maindimcase[maindimtype]);
1423 try {
1424 if (!modifyallregions) {
1425 region->DeleteDimensionZone(maindimtype, maindimcase[maindimtype]);
1426 } else {
1427 gig::Instrument* instr = (gig::Instrument*)region->GetParent();
1428 gig::dimension_def_t* pMaindimdef = region->GetDimensionDefinition(maindimtype);
1429 assert(pMaindimdef != NULL);
1430 // retain structure by value since the original region will be
1431 // modified in the loop below as well
1432 gig::dimension_def_t maindimdef = *pMaindimdef;
1433 std::vector<gig::Region*> ignoredAll;
1434 std::vector<gig::Region*> ignoredMinor;
1435 std::vector<gig::Region*> ignoredCritical;
1436 gig::Region* rgn = NULL;
1437 for (int key = 0; key < 128; ++key) {
1438 if (!instr->GetRegion(key) || instr->GetRegion(key) == rgn) continue;
1439 rgn = instr->GetRegion(key);
1440
1441 // ignore all regions which do not exactly match the dimension
1442 // layout of the selected region where this operation was emitted
1443 gig::dimension_def_t* dimdef = rgn->GetDimensionDefinition(maindimtype);
1444 if (!dimdef) {
1445 ignoredAll.push_back(rgn);
1446 ignoredMinor.push_back(rgn);
1447 continue;
1448 }
1449 if (dimdef->zones != maindimdef.zones) {
1450 ignoredAll.push_back(rgn);
1451 ignoredCritical.push_back(rgn);
1452 continue;
1453 }
1454
1455 rgn->DeleteDimensionZone(maindimtype, maindimcase[maindimtype]);
1456 }
1457 if (!ignoredAll.empty()) {
1458 Glib::ustring txt;
1459 if (ignoredCritical.empty())
1460 txt = ToString(ignoredMinor.size()) + _(" regions have been ignored since they don't have that dimension type.");
1461 else if (ignoredMinor.empty())
1462 txt = ToString(ignoredCritical.size()) + _(" regions have been ignored due to different amount of dimension zones!");
1463 else
1464 txt = ToString(ignoredCritical.size()) + _(" regions have been ignored due to different amount of dimension zones (and ") +
1465 ToString(ignoredMinor.size()) + _(" regions have been ignored since they don't have that dimension type)!");
1466 Gtk::MessageType type = (ignoredCritical.empty()) ? Gtk::MESSAGE_INFO : Gtk::MESSAGE_WARNING;
1467 Gtk::MessageDialog msg(txt, false, type);
1468 msg.run();
1469 }
1470 }
1471 } catch (RIFF::Exception e) {
1472 Gtk::MessageDialog msg(e.Message, false, Gtk::MESSAGE_ERROR);
1473 msg.run();
1474 } catch (...) {
1475 Glib::ustring txt = _("An unknown exception occurred!");
1476 Gtk::MessageDialog msg(txt, false, Gtk::MESSAGE_ERROR);
1477 msg.run();
1478 }
1479 refresh_all();
1480 }
1481
1482 // Cmd key on Mac, Ctrl key on all other OSs
1483 static const guint primaryKeyL =
1484 #if defined(__APPLE__)
1485 GDK_KEY_Meta_L;
1486 #else
1487 GDK_KEY_Control_L;
1488 #endif
1489
1490 static const guint primaryKeyR =
1491 #if defined(__APPLE__)
1492 GDK_KEY_Meta_R;
1493 #else
1494 GDK_KEY_Control_R;
1495 #endif
1496
1497 #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
1498 bool DimRegionChooser::onKeyPressed(Gdk::EventKey& _key) {
1499 GdkEventKey* key = _key.gobj();
1500 #else
1501 bool DimRegionChooser::onKeyPressed(GdkEventKey* key) {
1502 #endif
1503 //printf("key down 0x%x\n", key->keyval);
1504 if (key->keyval == GDK_KEY_Control_L || key->keyval == GDK_KEY_Control_R)
1505 multiSelectKeyDown = true;
1506 if (key->keyval == primaryKeyL || key->keyval == primaryKeyR)
1507 primaryKeyDown = true;
1508 if (key->keyval == GDK_KEY_Shift_L || key->keyval == GDK_KEY_Shift_R)
1509 shiftKeyDown = true;
1510
1511 //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
1512 /*if (key->keyval == GDK_KEY_Left)
1513 select_prev_dimzone();
1514 if (key->keyval == GDK_KEY_Right)
1515 select_next_dimzone();
1516 if (key->keyval == GDK_KEY_Up)
1517 select_prev_dimension();
1518 if (key->keyval == GDK_KEY_Down)
1519 select_next_dimension();*/
1520 return false;
1521 }
1522
1523 #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
1524 bool DimRegionChooser::onKeyReleased(Gdk::EventKey& _key) {
1525 GdkEventKey* key = _key.gobj();
1526 #else
1527 bool DimRegionChooser::onKeyReleased(GdkEventKey* key) {
1528 #endif
1529 //printf("key up 0x%x\n", key->keyval);
1530 if (key->keyval == GDK_KEY_Control_L || key->keyval == GDK_KEY_Control_R)
1531 multiSelectKeyDown = false;
1532 if (key->keyval == primaryKeyL || key->keyval == primaryKeyR)
1533 primaryKeyDown = false;
1534 if (key->keyval == GDK_KEY_Shift_L || key->keyval == GDK_KEY_Shift_R)
1535 shiftKeyDown = false;
1536
1537 if (!has_focus()) return false;
1538
1539 // avoid conflict with Ctrl+Left and Ctrl+Right accelerators on mainwindow
1540 // (which is supposed to switch between regions)
1541 if (primaryKeyDown) return false;
1542
1543 // avoid conflict with Alt+Shift+Left and Alt+Shift+Right accelerators on
1544 // mainwindow
1545 if (shiftKeyDown) return false;
1546
1547 if (key->keyval == GDK_KEY_Left)
1548 select_prev_dimzone();
1549 if (key->keyval == GDK_KEY_Right)
1550 select_next_dimzone();
1551 if (key->keyval == GDK_KEY_Up)
1552 select_prev_dimension();
1553 if (key->keyval == GDK_KEY_Down)
1554 select_next_dimension();
1555
1556 return false;
1557 }
1558
1559 void DimRegionChooser::resetSelectedZones() {
1560 this->dimzones.clear();
1561 if (!region) {
1562 queue_draw(); // redraw required parts
1563 return;
1564 }
1565 if (maindimregno < 0 || maindimregno >= region->DimensionRegions) {
1566 queue_draw(); // redraw required parts
1567 return;
1568 }
1569 if (!region->pDimensionRegions[maindimregno]) {
1570 queue_draw(); // redraw required parts
1571 return;
1572 }
1573 gig::DimensionRegion* dimrgn = region->pDimensionRegions[maindimregno];
1574
1575 bool isValidZone;
1576 this->maindimcase = dimensionCaseOf(dimrgn);
1577
1578 for (std::map<gig::dimension_t,int>::const_iterator it = this->maindimcase.begin();
1579 it != this->maindimcase.end(); ++it)
1580 {
1581 this->dimzones[it->first].insert(it->second);
1582 }
1583
1584 // redraw required parts
1585 queue_draw();
1586 }
1587
1588 bool DimRegionChooser::select_dimregion(gig::DimensionRegion* dimrgn) {
1589 if (!region) return false; //.selection failed
1590
1591 for (int dr = 0; dr < region->DimensionRegions && region->pDimensionRegions[dr]; ++dr) {
1592 if (region->pDimensionRegions[dr] == dimrgn) {
1593 // reset dim region zone selection to the requested specific dim region case
1594 maindimregno = dr;
1595 resetSelectedZones();
1596
1597 // emit signal that dimregion selection has changed, for external entities
1598 dimregion_selected();
1599
1600 return true; // selection success
1601 }
1602 }
1603
1604 return false; //.selection failed
1605 }
1606
1607 void DimRegionChooser::select_next_dimzone(bool add) {
1608 select_dimzone_by_dir(+1, add);
1609 }
1610
1611 void DimRegionChooser::select_prev_dimzone(bool add) {
1612 select_dimzone_by_dir(-1, add);
1613 }
1614
1615 void DimRegionChooser::select_dimzone_by_dir(int dir, bool add) {
1616 if (!region) return;
1617 if (!region->Dimensions) return;
1618 if (focus_line < 0) focus_line = 0;
1619 if (focus_line >= region->Dimensions) focus_line = region->Dimensions - 1;
1620
1621 maindimtype = region->pDimensionDefinitions[focus_line].dimension;
1622 if (maindimtype == gig::dimension_none) {
1623 printf("maindimtype -> none\n");
1624 return;
1625 }
1626
1627 // commented out: re-evaluate maindimcase, since it might not been reset from a previous instrument which causes errors if it got different dimension types
1628 //if (maindimcase.empty()) {
1629 maindimcase = dimensionCaseOf(region->pDimensionRegions[maindimregno]);
1630 if (maindimcase.empty()) {
1631 printf("caseOfDimregion(%d) -> empty\n", maindimregno);
1632 return;
1633 }
1634 //}
1635
1636 int z = (dir > 0) ? maindimcase[maindimtype] + 1 : maindimcase[maindimtype] - 1;
1637 if (z < 0) z = 0;
1638 if (z >= region->pDimensionDefinitions[focus_line].zones)
1639 z = region->pDimensionDefinitions[focus_line].zones - 1;
1640
1641 maindimcase[maindimtype] = z;
1642
1643 ::gig::DimensionRegion* dr = dimensionRegionMatching(maindimcase, region);
1644 if (!dr) {
1645 printf("select_dimzone_by_dir(%d) -> !dr\n", dir);
1646 return;
1647 }
1648
1649 maindimregno = getDimensionRegionIndex(dr);
1650
1651 if (!add) {
1652 // reset selected dimregion zones
1653 dimzones.clear();
1654 }
1655 for (DimensionCase::const_iterator it = maindimcase.begin();
1656 it != maindimcase.end(); ++it)
1657 {
1658 dimzones[it->first].insert(it->second);
1659 }
1660
1661 dimregion_selected();
1662
1663 // disabled: would overwrite dimregno with wrong value
1664 //refresh_all();
1665 // so requesting just a raw repaint instead:
1666 queue_draw();
1667 }
1668
1669 void DimRegionChooser::select_next_dimension() {
1670 if (!region) return;
1671 focus_line++;
1672 if (focus_line >= region->Dimensions)
1673 focus_line = region->Dimensions - 1;
1674 this->maindimtype = region->pDimensionDefinitions[focus_line].dimension;
1675 queue_draw();
1676 }
1677
1678 void DimRegionChooser::select_prev_dimension() {
1679 if (!region) return;
1680 focus_line--;
1681 if (focus_line < 0)
1682 focus_line = 0;
1683 this->maindimtype = region->pDimensionDefinitions[focus_line].dimension;
1684 queue_draw();
1685 }
1686
1687 gig::DimensionRegion* DimRegionChooser::get_main_dimregion() const {
1688 if (!region) return NULL;
1689 return region->pDimensionRegions[maindimregno];
1690 }

  ViewVC Help
Powered by ViewVC