/[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 3089 - (show annotations) (download)
Sun Jan 15 19:18:39 2017 UTC (7 years, 2 months ago) by schoenebeck
File size: 46979 byte(s)
* Implemented resizing multiple dimension region zones at once, which is
  controlled by the infamous checkbox trio "all regions",
  "all dimension splits" and "both channels".
* Bumped version (1.0.0.svn25).

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 <gtkmm/box.h>
21 #include "dimregionchooser.h"
22 #include <cairomm/context.h>
23 #include <gdkmm/cursor.h>
24 #include <gdkmm/general.h>
25 #include <glibmm/stringutils.h>
26 #include <glibmm/ustring.h>
27 #include <gtkmm/messagedialog.h>
28 #include <assert.h>
29
30 #include "global.h"
31
32 // taken from gdk/gdkkeysyms.h
33 // (define on demand, to avoid unnecessary dev lib package build dependency)
34 #ifndef GDK_KEY_Control_L
35 # define GDK_KEY_Control_L 0xffe3
36 #endif
37 #ifndef GDK_KEY_Control_R
38 # define GDK_KEY_Control_R 0xffe4
39 #endif
40
41 static std::map<gig::dimension_t,int> caseOfDimRegion(gig::DimensionRegion* dr, bool* isValidZone) {
42 std::map<gig::dimension_t,int> dimCase;
43 if (!dr) {
44 *isValidZone = false;
45 return dimCase;
46 }
47
48 gig::Region* rgn = (gig::Region*) dr->GetParent();
49
50 // find the dimension region index of the passed dimension region
51 int drIndex;
52 for (drIndex = 0; drIndex < 256; ++drIndex)
53 if (rgn->pDimensionRegions[drIndex] == dr)
54 break;
55
56 // not found in region, something's horribly wrong
57 if (drIndex == 256) {
58 fprintf(stderr, "DimRegionChooser: ERROR: index of dim region not found!\n");
59 *isValidZone = false;
60 return std::map<gig::dimension_t,int>();
61 }
62
63 for (int d = 0, baseBits = 0; d < rgn->Dimensions; ++d) {
64 const int bits = rgn->pDimensionDefinitions[d].bits;
65 dimCase[rgn->pDimensionDefinitions[d].dimension] =
66 (drIndex >> baseBits) & ((1 << bits) - 1);
67 baseBits += bits;
68 // there are also DimensionRegion objects of unused zones, skip them
69 if (dimCase[rgn->pDimensionDefinitions[d].dimension] >= rgn->pDimensionDefinitions[d].zones) {
70 *isValidZone = false;
71 return std::map<gig::dimension_t,int>();
72 }
73 }
74
75 *isValidZone = true;
76 return dimCase;
77 }
78
79 DimRegionChooser::DimRegionChooser(Gtk::Window& window) :
80 red("#8070ff"),
81 black("black"),
82 white("white")
83 {
84 instrument = 0;
85 region = 0;
86 maindimregno = -1;
87 focus_line = 0;
88 resize.active = false;
89 cursor_is_resize = false;
90 h = 24;
91 multiSelectKeyDown = false;
92 modifybothchannels = modifyalldimregs = modifybothchannels = false;
93 set_can_focus();
94
95 actionGroup = Gtk::ActionGroup::create();
96 actionGroup->add(
97 Gtk::Action::create("SplitDimZone", _("Split Dimensions Zone")),
98 sigc::mem_fun(*this, &DimRegionChooser::split_dimension_zone)
99 );
100 actionGroup->add(
101 Gtk::Action::create("DeleteDimZone", _("Delete Dimension Zone")),
102 sigc::mem_fun(*this, &DimRegionChooser::delete_dimension_zone)
103 );
104
105 uiManager = Gtk::UIManager::create();
106 uiManager->insert_action_group(actionGroup);
107 Glib::ustring ui_info =
108 "<ui>"
109 " <popup name='PopupMenuInsideDimRegion'>"
110 " <menuitem action='SplitDimZone'/>"
111 " <menuitem action='DeleteDimZone'/>"
112 " </popup>"
113 // " <popup name='PopupMenuOutsideDimRegion'>"
114 // " <menuitem action='Add'/>"
115 // " </popup>"
116 "</ui>";
117 uiManager->add_ui_from_string(ui_info);
118
119 popup_menu_inside_dimregion = dynamic_cast<Gtk::Menu*>(
120 uiManager->get_widget("/PopupMenuInsideDimRegion"));
121 // popup_menu_outside_dimregion = dynamic_cast<Gtk::Menu*>(
122 // uiManager->get_widget("/PopupMenuOutsideDimRegion"));
123
124 add_events(Gdk::BUTTON_PRESS_MASK | Gdk::POINTER_MOTION_MASK |
125 Gdk::POINTER_MOTION_HINT_MASK);
126
127 labels_changed = true;
128
129 set_tooltip_text(_(
130 "Right click here for options on altering dimension zones. Press and "
131 "hold CTRL key for selecting multiple dimension zones simultaniously."
132 ));
133
134 window.signal_key_press_event().connect(
135 sigc::mem_fun(*this, &DimRegionChooser::onKeyPressed)
136 );
137 window.signal_key_release_event().connect(
138 sigc::mem_fun(*this, &DimRegionChooser::onKeyReleased)
139 );
140 }
141
142 DimRegionChooser::~DimRegionChooser()
143 {
144 }
145
146 void DimRegionChooser::setModifyBothChannels(bool b) {
147 modifybothchannels = b;
148 }
149
150 void DimRegionChooser::setModifyAllDimensionRegions(bool b) {
151 modifyalldimregs = b;
152 }
153
154 void DimRegionChooser::setModifyAllRegions(bool b) {
155 modifyallregions = b;
156 }
157
158 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
159 bool DimRegionChooser::on_expose_event(GdkEventExpose* e)
160 {
161 double clipx1 = e->area.x;
162 double clipx2 = e->area.x + e->area.width;
163 double clipy1 = e->area.y;
164 double clipy2 = e->area.y + e->area.height;
165
166 const Cairo::RefPtr<Cairo::Context>& cr =
167 get_window()->create_cairo_context();
168 #else
169 bool DimRegionChooser::on_draw(const Cairo::RefPtr<Cairo::Context>& cr)
170 {
171 double clipx1, clipx2, clipy1, clipy2;
172 cr->get_clip_extents(clipx1, clipy1, clipx2, clipy2);
173 #endif
174
175 if (!region) return true;
176
177 // This is where we draw on the window
178 int w = get_width();
179 Glib::RefPtr<Pango::Context> context = get_pango_context();
180
181 Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create(context);
182 cr->set_line_width(1);
183
184 int y = 0;
185 if (labels_changed || label_width - 10 > clipx1) {
186 // draw labels on the left (reflecting the dimension type)
187 double maxwidth = 0;
188 for (int i = 0 ; i < region->Dimensions ; i++) {
189 int nbZones = region->pDimensionDefinitions[i].zones;
190 if (nbZones) {
191 const char* dstr;
192 char dstrbuf[10];
193 switch (region->pDimensionDefinitions[i].dimension) {
194 case gig::dimension_none: dstr=_("none"); break;
195 case gig::dimension_samplechannel: dstr=_("samplechannel");
196 break;
197 case gig::dimension_layer: dstr=_("layer"); break;
198 case gig::dimension_velocity: dstr=_("velocity"); break;
199 case gig::dimension_channelaftertouch:
200 dstr=_("channelaftertouch"); break;
201 case gig::dimension_releasetrigger:
202 dstr=_("releasetrigger"); break;
203 case gig::dimension_keyboard: dstr=_("keyswitching"); break;
204 case gig::dimension_roundrobin: dstr=_("roundrobin"); break;
205 case gig::dimension_random: dstr=_("random"); break;
206 case gig::dimension_smartmidi: dstr=_("smartmidi"); break;
207 case gig::dimension_roundrobinkeyboard:
208 dstr=_("roundrobinkeyboard"); break;
209 case gig::dimension_modwheel: dstr=_("modwheel"); break;
210 case gig::dimension_breath: dstr=_("breath"); break;
211 case gig::dimension_foot: dstr=_("foot"); break;
212 case gig::dimension_portamentotime:
213 dstr=_("portamentotime"); break;
214 case gig::dimension_effect1: dstr=_("effect1"); break;
215 case gig::dimension_effect2: dstr=_("effect2"); break;
216 case gig::dimension_genpurpose1: dstr=_("genpurpose1"); break;
217 case gig::dimension_genpurpose2: dstr=_("genpurpose2"); break;
218 case gig::dimension_genpurpose3: dstr=_("genpurpose3"); break;
219 case gig::dimension_genpurpose4: dstr=_("genpurpose4"); break;
220 case gig::dimension_sustainpedal:
221 dstr=_("sustainpedal"); break;
222 case gig::dimension_portamento: dstr=_("portamento"); break;
223 case gig::dimension_sostenutopedal:
224 dstr=_("sostenutopedal"); break;
225 case gig::dimension_softpedal: dstr=_("softpedal"); break;
226 case gig::dimension_genpurpose5: dstr=_("genpurpose5"); break;
227 case gig::dimension_genpurpose6: dstr=_("genpurpose6"); break;
228 case gig::dimension_genpurpose7: dstr=_("genpurpose7"); break;
229 case gig::dimension_genpurpose8: dstr=_("genpurpose8"); break;
230 case gig::dimension_effect1depth:
231 dstr=_("effect1depth"); break;
232 case gig::dimension_effect2depth:
233 dstr=_("effect2depth"); break;
234 case gig::dimension_effect3depth:
235 dstr=_("effect3depth"); break;
236 case gig::dimension_effect4depth:
237 dstr=_("effect4depth"); break;
238 case gig::dimension_effect5depth:
239 dstr=_("effect5depth"); break;
240 default:
241 sprintf(dstrbuf, "%d",
242 region->pDimensionDefinitions[i].dimension);
243 dstr = dstrbuf;
244 break;
245 }
246 layout->set_text(dstr);
247
248 Pango::Rectangle rectangle = layout->get_logical_extents();
249 double text_w = double(rectangle.get_width()) / Pango::SCALE;
250 if (text_w > maxwidth) maxwidth = text_w;
251
252 if (y + h > clipy1 && y < clipy2 && text_w >= clipx1) {
253 double text_h = double(rectangle.get_height()) /
254 Pango::SCALE;
255 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
256 const Gdk::Color fg = get_style()->get_fg(get_state());
257 #else
258 const Gdk::RGBA fg =
259 get_style_context()->get_color(get_state_flags());
260 #endif
261 Gdk::Cairo::set_source_rgba(cr, fg);
262 cr->move_to(4, int(y + (h - text_h) / 2 + 0.5));
263 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 16) || GTKMM_MAJOR_VERSION < 2
264 pango_cairo_show_layout(cr->cobj(), layout->gobj());
265 #else
266 layout->show_in_cairo_context(cr);
267 #endif
268 }
269 }
270 y += h;
271 }
272 label_width = int(maxwidth + 10);
273 labels_changed = false;
274 }
275 if (label_width >= clipx2) return true;
276
277 // draw dimensions' zones areas
278 y = 0;
279 int bitpos = 0;
280 for (int i = 0 ; i < region->Dimensions ; i++) {
281 int nbZones = region->pDimensionDefinitions[i].zones;
282 if (nbZones) {
283 const gig::dimension_t dimension = region->pDimensionDefinitions[i].dimension;
284
285 if (y >= clipy2) break;
286 if (y + h > clipy1) {
287 // draw focus rectangle around dimension's label and zones
288 if (has_focus() && focus_line == i) {
289 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
290 Gdk::Rectangle farea(0, y, 150, h);
291 get_style()->paint_focus(get_window(), get_state(), farea,
292 *this, "",
293 0, y, label_width, h);
294 #else
295 get_style_context()->render_focus(cr,
296 0, y, label_width, h);
297 #endif
298 }
299
300 // draw top and bottom lines of dimension's zones
301 Gdk::Cairo::set_source_rgba(cr, black);
302 cr->move_to(label_width, y + 0.5);
303 cr->line_to(w, y + 0.5);
304 cr->move_to(w, y + h - 0.5);
305 cr->line_to(label_width, y + h - 0.5);
306 cr->stroke();
307
308 // erase whole dimension's zones area
309 Gdk::Cairo::set_source_rgba(cr, white);
310 cr->rectangle(label_width + 1, y + 1,
311 (w - label_width - 2), h - 2);
312 cr->fill();
313
314 int c = 0;
315 if (maindimregno >= 0) {
316 int mask =
317 ~(((1 << region->pDimensionDefinitions[i].bits) - 1) <<
318 bitpos);
319 c = maindimregno & mask; // mask away this dimension
320 }
321 bool customsplits =
322 ((region->pDimensionDefinitions[i].split_type ==
323 gig::split_type_normal &&
324 region->pDimensionRegions[c]->DimensionUpperLimits[i]) ||
325 (region->pDimensionDefinitions[i].dimension ==
326 gig::dimension_velocity &&
327 region->pDimensionRegions[c]->VelocityUpperLimit));
328
329 // draw dimension zones
330 Gdk::Cairo::set_source_rgba(cr, black);
331 if (customsplits) {
332 cr->move_to(label_width + 0.5, y + 1);
333 cr->line_to(label_width + 0.5, y + h - 1);
334 int prevX = label_width;
335 int prevUpperLimit = -1;
336
337 for (int j = 0 ; j < nbZones ; j++) {
338 // draw dimension zone's borders for custom splits
339 gig::DimensionRegion* d =
340 region->pDimensionRegions[c + (j << bitpos)];
341 int upperLimit = d->DimensionUpperLimits[i];
342 if (!upperLimit) upperLimit = d->VelocityUpperLimit;
343 int v = upperLimit + 1;
344 int x = int((w - label_width - 1) * v / 128.0 + 0.5) +
345 label_width;
346 if (x >= clipx2) break;
347 if (x < clipx1) continue;
348 Gdk::Cairo::set_source_rgba(cr, black);
349 cr->move_to(x + 0.5, y + 1);
350 cr->line_to(x + 0.5, y + h - 1);
351 cr->stroke();
352
353 // draw fill for zone
354 bool isSelectedZone = this->dimzones[dimension].count(j);
355 Gdk::Cairo::set_source_rgba(cr, isSelectedZone ? red : white);
356 cr->rectangle(prevX + 1, y + 1, x - prevX - 1, h - 1);
357 cr->fill();
358
359 // draw text showing the beginning of the dimension zone
360 // as numeric value to the user
361 {
362 Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create(context);
363 layout->set_text(Glib::Ascii::dtostr(prevUpperLimit+1));
364 Gdk::Cairo::set_source_rgba(cr, black);
365 // get the text dimensions
366 int text_width, text_height;
367 layout->get_pixel_size(text_width, text_height);
368 // move text to the left end of the dimension zone
369 cr->move_to(prevX + 3, y + (h - text_height) / 2);
370 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 16) || GTKMM_MAJOR_VERSION < 2
371 pango_cairo_show_layout(cr->cobj(), layout->gobj());
372 #else
373 layout->show_in_cairo_context(cr);
374 #endif
375 }
376 // draw text showing the end of the dimension zone
377 // as numeric value to the user
378 {
379 Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create(context);
380 layout->set_text(Glib::Ascii::dtostr(upperLimit));
381 Gdk::Cairo::set_source_rgba(cr, black);
382 // get the text dimensions
383 int text_width, text_height;
384 layout->get_pixel_size(text_width, text_height);
385 // move text to the left end of the dimension zone
386 cr->move_to(x - 3 - text_width, y + (h - text_height) / 2);
387 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 16) || GTKMM_MAJOR_VERSION < 2
388 pango_cairo_show_layout(cr->cobj(), layout->gobj());
389 #else
390 layout->show_in_cairo_context(cr);
391 #endif
392 }
393
394 prevX = x;
395 prevUpperLimit = upperLimit;
396 }
397 } else {
398 int prevX = 0;
399 for (int j = 0 ; j <= nbZones ; j++) {
400 // draw dimension zone's borders for normal splits
401 int x = int((w - label_width - 1) * j /
402 double(nbZones) + 0.5) + label_width;
403 if (x >= clipx2) break;
404 if (x < clipx1) continue;
405 Gdk::Cairo::set_source_rgba(cr, black);
406 cr->move_to(x + 0.5, y + 1);
407 cr->line_to(x + 0.5, y + h - 1);
408 cr->stroke();
409
410 if (j != 0) {
411 // draw fill for zone
412 bool isSelectedZone = this->dimzones[dimension].count(j-1);
413 Gdk::Cairo::set_source_rgba(cr, isSelectedZone ? red : white);
414 cr->rectangle(prevX + 1, y + 1, x - prevX - 1, h - 1);
415 cr->fill();
416
417 // draw text showing the beginning of the dimension zone
418 // as numeric value to the user
419 {
420 Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create(context);
421 layout->set_text(Glib::Ascii::dtostr((j-1) * 128/nbZones));
422 Gdk::Cairo::set_source_rgba(cr, black);
423 // get the text dimensions
424 int text_width, text_height;
425 layout->get_pixel_size(text_width, text_height);
426 // move text to the left end of the dimension zone
427 cr->move_to(prevX + 3, y + (h - text_height) / 2);
428 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 16) || GTKMM_MAJOR_VERSION < 2
429 pango_cairo_show_layout(cr->cobj(), layout->gobj());
430 #else
431 layout->show_in_cairo_context(cr);
432 #endif
433 }
434 // draw text showing the end of the dimension zone
435 // as numeric value to the user
436 {
437 Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create(context);
438 layout->set_text(Glib::Ascii::dtostr(j * 128/nbZones - 1));
439 Gdk::Cairo::set_source_rgba(cr, black);
440 // get the text dimensions
441 int text_width, text_height;
442 layout->get_pixel_size(text_width, text_height);
443 // move text to the left end of the dimension zone
444 cr->move_to(x - 3 - text_width, y + (h - text_height) / 2);
445 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 16) || GTKMM_MAJOR_VERSION < 2
446 pango_cairo_show_layout(cr->cobj(), layout->gobj());
447 #else
448 layout->show_in_cairo_context(cr);
449 #endif
450 }
451 }
452 prevX = x;
453 }
454 }
455 }
456 y += h;
457 }
458 bitpos += region->pDimensionDefinitions[i].bits;
459 }
460
461 return true;
462 }
463
464 void DimRegionChooser::set_region(gig::Region* region)
465 {
466 this->region = region;
467 maindimregno = 0;
468 nbDimensions = 0;
469 if (region) {
470 int bitcount = 0;
471 for (int dim = 0 ; dim < region->Dimensions ; dim++) {
472 if (region->pDimensionDefinitions[dim].bits == 0) continue;
473 nbDimensions++;
474
475 int z = std::min(maindimcase[region->pDimensionDefinitions[dim].dimension],
476 region->pDimensionDefinitions[dim].zones - 1);
477 maindimregno |= (z << bitcount);
478 bitcount += region->pDimensionDefinitions[dim].bits;
479 }
480 }
481 dimregion_selected();
482 set_size_request(800, region ? nbDimensions * h : 0);
483
484 labels_changed = true;
485 queue_resize();
486 queue_draw();
487 }
488
489 void DimRegionChooser::refresh_all() {
490 set_region(region);
491 }
492
493 void DimRegionChooser::get_dimregions(const gig::Region* region, bool stereo,
494 std::set<gig::DimensionRegion*>& dimregs) const
495 {
496 for (int iDimRgn = 0; iDimRgn < 256; ++iDimRgn) {
497 gig::DimensionRegion* dimRgn = region->pDimensionRegions[iDimRgn];
498 if (!dimRgn) continue;
499 bool isValidZone;
500 std::map<gig::dimension_t,int> dimCase = caseOfDimRegion(dimRgn, &isValidZone);
501 if (!isValidZone) continue;
502 for (std::map<gig::dimension_t,int>::const_iterator it = dimCase.begin();
503 it != dimCase.end(); ++it)
504 {
505 if (stereo && it->first == gig::dimension_samplechannel) continue; // is selected
506
507 std::map<gig::dimension_t, std::set<int> >::const_iterator itSelectedDimension =
508 this->dimzones.find(it->first);
509 if (itSelectedDimension != this->dimzones.end() &&
510 itSelectedDimension->second.count(it->second)) continue; // is selected
511
512 goto notSelected;
513 }
514
515 dimregs.insert(dimRgn);
516
517 notSelected:
518 ;
519 }
520 }
521
522 void DimRegionChooser::update_after_resize()
523 {
524 const uint8_t upperLimit = resize.pos - 1;
525 gig::Instrument* instr = (gig::Instrument*)region->GetParent();
526
527 int bitpos = 0;
528 for (int j = 0 ; j < resize.dimension ; j++) {
529 bitpos += region->pDimensionDefinitions[j].bits;
530 }
531
532 const int stereobitpos =
533 (modifybothchannels) ? baseBits(gig::dimension_samplechannel, region) : -1;
534
535 // the velocity dimension must be handled differently than all other
536 // dimension types, because
537 // 1. it is currently the only dimension type which allows different zone
538 // sizes for different cases
539 // 2. for v2 format VelocityUpperLimit has to be set, DimensionUpperLimits for v3
540 if (region->pDimensionDefinitions[resize.dimension].dimension == gig::dimension_velocity) {
541 int mask =
542 ~(((1 << region->pDimensionDefinitions[resize.dimension].bits) - 1) << bitpos);
543 int c = maindimregno & mask; // mask away this dimension
544
545 if (region->pDimensionRegions[c]->DimensionUpperLimits[resize.dimension] == 0) {
546 // the velocity dimension didn't previously have
547 // custom v3 splits, so we initialize all splits with
548 // default values
549 int nbZones = region->pDimensionDefinitions[resize.dimension].zones;
550 for (int j = 0 ; j < nbZones ; j++) {
551 gig::DimensionRegion* d = region->pDimensionRegions[c + (j << bitpos)];
552 d->DimensionUpperLimits[resize.dimension] = int(128.0 * (j + 1) / nbZones - 1);
553 }
554 }
555 if (region->pDimensionRegions[c]->VelocityUpperLimit == 0) {
556 // the velocity dimension didn't previously have
557 // custom v2 splits, so we initialize all splits with
558 // default values
559 int nbZones = region->pDimensionDefinitions[resize.dimension].zones;
560 for (int j = 0 ; j < nbZones ; j++) {
561 gig::DimensionRegion* d = region->pDimensionRegions[c + (j << bitpos)];
562 d->VelocityUpperLimit = int(128.0 * (j + 1) / nbZones - 1);
563 }
564 }
565
566 int index = c + (resize.zone << bitpos);
567 gig::DimensionRegion* d = region->pDimensionRegions[index];
568 // update both v2 and v3 values
569 d->DimensionUpperLimits[resize.dimension] = upperLimit;
570 d->VelocityUpperLimit = upperLimit;
571 if (modifybothchannels && stereobitpos >= 0) { // do the same for the other audio channel's dimregion ...
572 gig::DimensionRegion* d = region->pDimensionRegions[index ^ (1 << stereobitpos)];
573 d->DimensionUpperLimits[resize.dimension] = upperLimit;
574 d->VelocityUpperLimit = upperLimit;
575 }
576
577 if (modifyalldimregs) {
578 gig::Region* rgn = NULL;
579 for (int key = 0; key < 128; ++key) {
580 if (!instr->GetRegion(key) || instr->GetRegion(key) == rgn) continue;
581 rgn = instr->GetRegion(key);
582 if (!modifyallregions && rgn != region) continue; // hack to reduce overall code amount a bit
583 gig::dimension_def_t* dimdef = rgn->GetDimensionDefinition(resize.dimensionDef.dimension);
584 if (!dimdef) continue;
585 if (dimdef->zones != resize.dimensionDef.zones) continue;
586 const int iDim = getDimensionIndex(resize.dimensionDef.dimension, rgn);
587 assert(iDim >= 0 && iDim < rgn->Dimensions);
588
589 // the dimension layout might be completely different in this
590 // region, so we have to recalculate bitpos etc for this region
591 const int bitpos = baseBits(resize.dimensionDef.dimension, rgn);
592 const int stencil = ~(((1 << dimdef->bits) - 1) << bitpos);
593 const int selection = resize.zone << bitpos;
594
595 // primitive and inefficient loop implementation, however due to
596 // this circumstance the loop code is much simpler, and its lack
597 // of runtime efficiency should not be notable in practice
598 for (int idr = 0; idr < 256; ++idr) {
599 const int index = (idr & stencil) | selection;
600 assert(index >= 0 && index < 256);
601 gig::DimensionRegion* dr = rgn->pDimensionRegions[index];
602 if (!dr) continue;
603 dr->DimensionUpperLimits[iDim] = upperLimit;
604 d->VelocityUpperLimit = upperLimit;
605 }
606 }
607 } else if (modifyallregions) { // implies modifyalldimregs is false ...
608 // resolve the precise case we need to modify for all other regions
609 DimensionCase dimCase = dimensionCaseOf(d);
610 // apply the velocity upper limit change to that resolved dim case
611 // of all regions ...
612 gig::Region* rgn = NULL;
613 for (int key = 0; key < 128; ++key) {
614 if (!instr->GetRegion(key) || instr->GetRegion(key) == rgn) continue;
615 rgn = instr->GetRegion(key);
616 gig::dimension_def_t* dimdef = rgn->GetDimensionDefinition(resize.dimensionDef.dimension);
617 if (!dimdef) continue;
618 if (dimdef->zones != resize.dimensionDef.zones) continue;
619 const int iDim = getDimensionIndex(resize.dimensionDef.dimension, rgn);
620 assert(iDim >= 0 && iDim < rgn->Dimensions);
621
622 std::vector<gig::DimensionRegion*> dimrgns = dimensionRegionsMatching(dimCase, rgn);
623 for (int i = 0; i < dimrgns.size(); ++i) {
624 gig::DimensionRegion* dr = dimrgns[i];
625 dr->DimensionUpperLimits[iDim] = upperLimit;
626 dr->VelocityUpperLimit = upperLimit;
627 }
628 }
629 }
630 } else {
631 for (int i = 0 ; i < region->DimensionRegions ; ) {
632 if (region->pDimensionRegions[i]->DimensionUpperLimits[resize.dimension] == 0) {
633 // the dimension didn't previously have custom
634 // limits, so we have to set default limits for
635 // all the dimension regions
636 int nbZones = region->pDimensionDefinitions[resize.dimension].zones;
637
638 for (int j = 0 ; j < nbZones ; j++) {
639 gig::DimensionRegion* d = region->pDimensionRegions[i + (j << bitpos)];
640 d->DimensionUpperLimits[resize.dimension] = int(128.0 * (j + 1) / nbZones - 1);
641 }
642 }
643 int index = i + (resize.zone << bitpos);
644 gig::DimensionRegion* d = region->pDimensionRegions[index];
645 d->DimensionUpperLimits[resize.dimension] = upperLimit;
646 #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
647 if (modifybothchannels && stereobitpos >= 0) { // do the same for the other audio channel's dimregion ...
648 gig::DimensionRegion* d = region->pDimensionRegions[index ^ (1 << stereobitpos)];
649 d->DimensionUpperLimits[resize.dimension] = upperLimit;
650 }
651 #endif
652 int bitpos = 0;
653 int j;
654 for (j = 0 ; j < region->Dimensions ; j++) {
655 if (j != resize.dimension) {
656 int maxzones = 1 << region->pDimensionDefinitions[j].bits;
657 int dimj = (i >> bitpos) & (maxzones - 1);
658 if (dimj + 1 < region->pDimensionDefinitions[j].zones) break;
659 }
660 bitpos += region->pDimensionDefinitions[j].bits;
661 }
662 if (j == region->Dimensions) break;
663 i = (i & ~((1 << bitpos) - 1)) + (1 << bitpos);
664 }
665
666 if (modifyallregions) { // TODO: this code block could be merged with the similar (and more generalized) code block of the velocity dimension above
667 gig::Region* rgn = NULL;
668 for (int key = 0; key < 128; ++key) {
669 if (!instr->GetRegion(key) || instr->GetRegion(key) == rgn) continue;
670 rgn = instr->GetRegion(key);
671 gig::dimension_def_t* dimdef = rgn->GetDimensionDefinition(resize.dimensionDef.dimension);
672 if (!dimdef) continue;
673 if (dimdef->zones != resize.dimensionDef.zones) continue;
674 const int iDim = getDimensionIndex(resize.dimensionDef.dimension, rgn);
675 assert(iDim >= 0 && iDim < rgn->Dimensions);
676
677 // the dimension layout might be completely different in this
678 // region, so we have to recalculate bitpos etc for this region
679 const int bitpos = baseBits(resize.dimensionDef.dimension, rgn);
680 const int stencil = ~(((1 << dimdef->bits) - 1) << bitpos);
681 const int selection = resize.zone << bitpos;
682
683 // this loop implementation is less efficient than the above's
684 // loop implementation (which skips unnecessary dimension regions)
685 // however this code is much simpler, and its lack of runtime
686 // efficiency should not be notable in practice
687 for (int idr = 0; idr < 256; ++idr) {
688 const int index = (idr & stencil) | selection;
689 assert(index >= 0 && index < 256);
690 gig::DimensionRegion* dr = rgn->pDimensionRegions[index];
691 if (!dr) continue;
692 dr->DimensionUpperLimits[iDim] = upperLimit;
693 }
694 }
695 }
696 }
697 }
698
699 bool DimRegionChooser::on_button_release_event(GdkEventButton* event)
700 {
701 if (resize.active) {
702 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
703 get_window()->pointer_ungrab(event->time);
704 #else
705 Glib::wrap(event->device, true)->ungrab(event->time);
706 #endif
707 resize.active = false;
708
709 region_changed();
710
711 if (!is_in_resize_zone(event->x, event->y) && cursor_is_resize) {
712 get_window()->set_cursor();
713 cursor_is_resize = false;
714 }
715 }
716 return true;
717 }
718
719 bool DimRegionChooser::on_button_press_event(GdkEventButton* event)
720 {
721 int w = get_width();
722 if (region && event->y < nbDimensions * h &&
723 event->x >= label_width && event->x < w) {
724
725 if (is_in_resize_zone(event->x, event->y)) {
726 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
727 get_window()->pointer_grab(false,
728 Gdk::BUTTON_RELEASE_MASK |
729 Gdk::POINTER_MOTION_MASK |
730 Gdk::POINTER_MOTION_HINT_MASK,
731 Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW),
732 event->time);
733 #else
734 Glib::wrap(event->device, true)->grab(get_window(),
735 Gdk::OWNERSHIP_NONE,
736 false,
737 Gdk::BUTTON_RELEASE_MASK |
738 Gdk::POINTER_MOTION_MASK |
739 Gdk::POINTER_MOTION_HINT_MASK,
740 Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW),
741 event->time);
742 #endif
743 resize.active = true;
744 } else {
745 int ydim = int(event->y / h);
746 int dim;
747 for (dim = 0 ; dim < region->Dimensions ; dim++) {
748 if (region->pDimensionDefinitions[dim].bits == 0) continue;
749 if (ydim == 0) break;
750 ydim--;
751 }
752 int nbZones = region->pDimensionDefinitions[dim].zones;
753
754 int z = -1;
755 int bitpos = 0;
756 for (int i = 0 ; i < dim ; i++) {
757 bitpos += region->pDimensionDefinitions[i].bits;
758 }
759
760 int i = dim;
761 if (maindimregno < 0) maindimregno = 0;
762 int mask = ~(((1 << region->pDimensionDefinitions[i].bits) - 1) << bitpos);
763 int c = this->maindimregno & mask; // mask away this dimension
764
765 bool customsplits =
766 ((region->pDimensionDefinitions[i].split_type == gig::split_type_normal &&
767 region->pDimensionRegions[c]->DimensionUpperLimits[i]) ||
768 (region->pDimensionDefinitions[i].dimension == gig::dimension_velocity &&
769 region->pDimensionRegions[c]->VelocityUpperLimit));
770 if (customsplits) {
771 int val = int((event->x - label_width) * 128 / (w - label_width - 1));
772
773 if (region->pDimensionRegions[c]->DimensionUpperLimits[i]) {
774 for (z = 0 ; z < nbZones ; z++) {
775 gig::DimensionRegion* d = region->pDimensionRegions[c + (z << bitpos)];
776 if (val <= d->DimensionUpperLimits[i]) break;
777 }
778 } else {
779 for (z = 0 ; z < nbZones ; z++) {
780 gig::DimensionRegion* d = region->pDimensionRegions[c + (z << bitpos)];
781 if (val <= d->VelocityUpperLimit) break;
782 }
783 }
784 } else {
785 z = int((event->x - label_width) * nbZones / (w - label_width - 1));
786 }
787
788 printf("dim=%d z=%d dimensionsource=%d split_type=%d zones=%d zone_size=%f\n", dim, z,
789 region->pDimensionDefinitions[dim].dimension,
790 region->pDimensionDefinitions[dim].split_type,
791 region->pDimensionDefinitions[dim].zones,
792 region->pDimensionDefinitions[dim].zone_size);
793 this->maindimcase[region->pDimensionDefinitions[dim].dimension] = z;
794 this->maindimregno = c | (z << bitpos);
795 this->maindimtype = region->pDimensionDefinitions[dim].dimension;
796
797 if (multiSelectKeyDown) {
798 if (dimzones[this->maindimtype].count(z)) {
799 if (dimzones[this->maindimtype].size() > 1) {
800 dimzones[this->maindimtype].erase(z);
801 }
802 } else {
803 dimzones[this->maindimtype].insert(z);
804 }
805 } else {
806 this->dimzones.clear();
807 for (std::map<gig::dimension_t,int>::const_iterator it = this->maindimcase.begin();
808 it != this->maindimcase.end(); ++it)
809 {
810 this->dimzones[it->first].insert(it->second);
811 }
812 }
813
814 focus_line = dim;
815 if (has_focus()) queue_draw();
816 else grab_focus();
817 dimregion_selected();
818
819 if (event->button == 3) {
820 printf("dimregion right click\n");
821 popup_menu_inside_dimregion->popup(event->button, event->time);
822 }
823
824 queue_draw();
825 }
826 }
827 return true;
828 }
829
830 bool DimRegionChooser::on_motion_notify_event(GdkEventMotion* event)
831 {
832 Glib::RefPtr<Gdk::Window> window = get_window();
833 int x, y;
834 Gdk::ModifierType state = Gdk::ModifierType(0);
835 window->get_pointer(x, y, state);
836
837 if (resize.active) {
838 int w = get_width();
839 int k = int((x - label_width) * 128.0 / (w - label_width - 1) + 0.5);
840
841 if (k < resize.min) k = resize.min;
842 else if (k > resize.max) k = resize.max;
843
844 if (k < 2) k = 2; // k is upper limit + 1, upper limit 0 is forbidden
845
846 if (k != resize.pos) {
847 int prevx = int((w - label_width - 1) * resize.pos / 128.0 + 0.5) + label_width;
848 int x = int((w - label_width - 1) * k / 128.0 + 0.5) + label_width;
849 int y = resize.dimension * h;
850 int x1, x2;
851 if (k > resize.pos) {
852 x1 = prevx;
853 x2 = x;
854 } else {
855 x1 = x;
856 x2 = prevx;
857 }
858 Gdk::Rectangle rect(x1, y + 1, x2 - x1 + 1, h - 2);
859
860 resize.pos = k;
861 update_after_resize();
862 get_window()->invalidate_rect(rect, false); // not sufficient ...
863 queue_draw(); // ... so do a complete redraw instead.
864 }
865 } else {
866 if (is_in_resize_zone(x, y)) {
867 if (!cursor_is_resize) {
868 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
869 window->set_cursor(Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW));
870 #else
871 window->set_cursor(Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW));
872 #endif
873 cursor_is_resize = true;
874 }
875 } else if (cursor_is_resize) {
876 window->set_cursor();
877 cursor_is_resize = false;
878 }
879 }
880 return true;
881 }
882
883 bool DimRegionChooser::is_in_resize_zone(double x, double y)
884 {
885 int w = get_width();
886 if (region && y < nbDimensions * h && x >= label_width && x < w) {
887 int ydim = int(y / h);
888 int dim;
889 int bitpos = 0;
890 for (dim = 0 ; dim < region->Dimensions ; dim++) {
891 if (region->pDimensionDefinitions[dim].bits == 0) continue;
892 if (ydim == 0) break;
893 ydim--;
894 bitpos += region->pDimensionDefinitions[dim].bits;
895 }
896 int nbZones = region->pDimensionDefinitions[dim].zones;
897
898 int c = 0;
899 if (maindimregno >= 0) {
900 int mask = ~(((1 << region->pDimensionDefinitions[dim].bits) - 1) << bitpos);
901 c = maindimregno & mask; // mask away this dimension
902 }
903 const bool customsplits =
904 ((region->pDimensionDefinitions[dim].split_type == gig::split_type_normal &&
905 region->pDimensionRegions[c]->DimensionUpperLimits[dim]) ||
906 (region->pDimensionDefinitions[dim].dimension == gig::dimension_velocity &&
907 region->pDimensionRegions[c]->VelocityUpperLimit));
908
909 // dimensions of split_type_bit cannot be resized
910 if (region->pDimensionDefinitions[dim].split_type != gig::split_type_bit) {
911 int prev_limit = 0;
912 for (int iZone = 0 ; iZone < nbZones - 1 ; iZone++) {
913 gig::DimensionRegion* d = region->pDimensionRegions[c + (iZone << bitpos)];
914 const int upperLimit =
915 (customsplits) ?
916 (d->DimensionUpperLimits[dim]) ?
917 d->DimensionUpperLimits[dim] : d->VelocityUpperLimit
918 : (iZone+1) * (int)region->pDimensionDefinitions[dim].zone_size - 1;
919 int limit = upperLimit + 1;
920 int limitx = int((w - label_width - 1) * limit / 128.0 + 0.5) + label_width;
921 if (x <= limitx - 2) break;
922 if (x <= limitx + 2) {
923 resize.dimension = dim;
924 resize.dimensionDef = region->pDimensionDefinitions[dim];
925 resize.zone = iZone;
926 resize.pos = limit;
927 resize.min = prev_limit;
928
929 int dr = (maindimregno >> bitpos) &
930 ((1 << region->pDimensionDefinitions[dim].bits) - 1);
931 resize.selected = dr == iZone ? resize.left :
932 dr == iZone + 1 ? resize.right : resize.none;
933
934 iZone++;
935 gig::DimensionRegion* d = region->pDimensionRegions[c + (iZone << bitpos)];
936
937 const int upperLimit =
938 (customsplits) ?
939 (d->DimensionUpperLimits[dim]) ?
940 d->DimensionUpperLimits[dim] : d->VelocityUpperLimit
941 : (iZone+1) * (int)region->pDimensionDefinitions[dim].zone_size - 1;
942
943 int limit = upperLimit + 1;
944 resize.max = limit;
945 return true;
946 }
947 prev_limit = limit;
948 }
949 }
950 }
951 return false;
952 }
953
954 sigc::signal<void>& DimRegionChooser::signal_dimregion_selected()
955 {
956 return dimregion_selected;
957 }
958
959 sigc::signal<void>& DimRegionChooser::signal_region_changed()
960 {
961 return region_changed;
962 }
963
964 bool DimRegionChooser::on_focus(Gtk::DirectionType direction)
965 {
966 // TODO: check that region exists etc, that is, that it's possible
967 // to set focus
968 if (direction == Gtk::DIR_TAB_FORWARD ||
969 direction == Gtk::DIR_DOWN) {
970 if (!has_focus()) {
971 focus_line = 0;
972 grab_focus();
973 return true;
974 } else {
975 if (focus_line + 1 < region->Dimensions) {
976 focus_line++;
977 queue_draw();
978 return true;
979 } else {
980 return false;
981 }
982 }
983 } else if (direction == Gtk::DIR_TAB_BACKWARD ||
984 direction == Gtk::DIR_UP) {
985 if (!has_focus()) {
986 focus_line = region->Dimensions - 1;
987 grab_focus();
988 return true;
989 } else {
990 if (focus_line > 0) {
991 focus_line--;
992 queue_draw();
993 return true;
994 } else {
995 return false;
996 }
997 }
998 } else if (!has_focus()) {
999 // TODO: check that focus_line exists
1000 grab_focus();
1001 return true;
1002 } else {
1003 // TODO: increase or decrease value
1004 }
1005 return false;
1006 }
1007
1008 void DimRegionChooser::split_dimension_zone() {
1009 printf("split_dimension_zone() type=%d, zone=%d\n", maindimtype, maindimcase[maindimtype]);
1010 try {
1011 region->SplitDimensionZone(maindimtype, maindimcase[maindimtype]);
1012 } catch (RIFF::Exception e) {
1013 Gtk::MessageDialog msg(e.Message, false, Gtk::MESSAGE_ERROR);
1014 msg.run();
1015 } catch (...) {
1016 Glib::ustring txt = _("An unknown exception occurred!");
1017 Gtk::MessageDialog msg(txt, false, Gtk::MESSAGE_ERROR);
1018 msg.run();
1019 }
1020 refresh_all();
1021 }
1022
1023 void DimRegionChooser::delete_dimension_zone() {
1024 printf("delete_dimension_zone() type=%d, zone=%d\n", maindimtype, maindimcase[maindimtype]);
1025 try {
1026 region->DeleteDimensionZone(maindimtype, maindimcase[maindimtype]);
1027 } catch (RIFF::Exception e) {
1028 Gtk::MessageDialog msg(e.Message, false, Gtk::MESSAGE_ERROR);
1029 msg.run();
1030 } catch (...) {
1031 Glib::ustring txt = _("An unknown exception occurred!");
1032 Gtk::MessageDialog msg(txt, false, Gtk::MESSAGE_ERROR);
1033 msg.run();
1034 }
1035 refresh_all();
1036 }
1037
1038 bool DimRegionChooser::onKeyPressed(GdkEventKey* key) {
1039 //printf("key down\n");
1040 if (key->keyval == GDK_KEY_Control_L || key->keyval == GDK_KEY_Control_R)
1041 multiSelectKeyDown = true;
1042 return false;
1043 }
1044
1045 bool DimRegionChooser::onKeyReleased(GdkEventKey* key) {
1046 //printf("key up\n");
1047 if (key->keyval == GDK_KEY_Control_L || key->keyval == GDK_KEY_Control_R)
1048 multiSelectKeyDown = false;
1049 return false;
1050 }
1051
1052 void DimRegionChooser::resetSelectedZones() {
1053 this->dimzones.clear();
1054 if (!region) {
1055 queue_draw(); // redraw required parts
1056 return;
1057 }
1058 if (maindimregno < 0 || maindimregno >= region->DimensionRegions) {
1059 queue_draw(); // redraw required parts
1060 return;
1061 }
1062 if (!region->pDimensionRegions[maindimregno]) {
1063 queue_draw(); // redraw required parts
1064 return;
1065 }
1066 gig::DimensionRegion* dimrgn = region->pDimensionRegions[maindimregno];
1067
1068 bool isValidZone;
1069 this->maindimcase = caseOfDimRegion(dimrgn, &isValidZone);
1070 if (!isValidZone) {
1071 queue_draw(); // redraw required parts
1072 return;
1073 }
1074
1075 for (std::map<gig::dimension_t,int>::const_iterator it = this->maindimcase.begin();
1076 it != this->maindimcase.end(); ++it)
1077 {
1078 this->dimzones[it->first].insert(it->second);
1079 }
1080
1081 // redraw required parts
1082 queue_draw();
1083 }
1084
1085 bool DimRegionChooser::select_dimregion(gig::DimensionRegion* dimrgn) {
1086 if (!region) return false; //.selection failed
1087
1088 for (int dr = 0; dr < region->DimensionRegions && region->pDimensionRegions[dr]; ++dr) {
1089 if (region->pDimensionRegions[dr] == dimrgn) {
1090 // reset dim region zone selection to the requested specific dim region case
1091 maindimregno = dr;
1092 resetSelectedZones();
1093
1094 // emit signal that dimregion selection has changed, for external entities
1095 dimregion_selected();
1096
1097 return true; // selection success
1098 }
1099 }
1100
1101 return false; //.selection failed
1102 }
1103
1104 gig::DimensionRegion* DimRegionChooser::get_main_dimregion() const {
1105 if (!region) return NULL;
1106 return region->pDimensionRegions[maindimregno];
1107 }

  ViewVC Help
Powered by ViewVC