/[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 3202 - (hide annotations) (download)
Mon May 22 18:58:46 2017 UTC (6 years, 10 months ago) by persson
File size: 61774 byte(s)
* fixed building with G_DISABLE_DEPRECATED

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

  ViewVC Help
Powered by ViewVC