/[svn]/gigedit/trunk/src/gigedit/dimregionchooser.cpp
ViewVC logotype

Annotation of /gigedit/trunk/src/gigedit/dimregionchooser.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3147 - (hide annotations) (download)
Wed May 3 21:23:16 2017 UTC (6 years, 11 months ago) by schoenebeck
File size: 61749 byte(s)
* Dimension Region Chooser: Mark all zones auto selected by check box trio
  with gray hatched pattern.
* Bumped version (1.0.0.svn33).

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

  ViewVC Help
Powered by ViewVC