/[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 3286 - (show annotations) (download)
Thu Jun 22 10:54:10 2017 UTC (6 years, 10 months ago) by schoenebeck
File size: 63050 byte(s)
* Script Editor: strike through code blocks filtered out by the
  preprocessor.
* Visual refinements of hatched patterns.
* Bumped version (1.0.0.svn53).

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

  ViewVC Help
Powered by ViewVC