/[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 3305 - (hide annotations) (download)
Mon Jul 10 20:27:44 2017 UTC (6 years, 8 months ago) by schoenebeck
File size: 65089 byte(s)
* Draw icons on individual dimension region zones, similar to
  the icons already been drawn on regions.
* Bumped version (1.0.0.svn56).

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

  ViewVC Help
Powered by ViewVC