/[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 3134 - (hide annotations) (download)
Fri Apr 28 12:41:12 2017 UTC (6 years, 11 months ago) by schoenebeck
File size: 59567 byte(s)
* Implemented selecting multiple dimension region zones by keyboard with
  keyboard accelerators Alt+Shift+Left and Alt+Shift+Right.
* Bumped version (1.0.0.svn31).

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

  ViewVC Help
Powered by ViewVC