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

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

  ViewVC Help
Powered by ViewVC