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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2844 - (show annotations) (download)
Sun Sep 20 08:49:40 2015 UTC (8 years, 6 months ago) by persson
File size: 40446 byte(s)
* allow building with gtkmm 2 and G/GDK/GTK_DISABLE_DEPRECATED

1 /*
2 * Copyright (C) 2006-2015 Andreas Persson
3 *
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 #include <gtkmm/box.h>
21 #include "dimregionchooser.h"
22 #include <cairomm/context.h>
23 #include <gdkmm/cursor.h>
24 #include <gdkmm/general.h>
25 #include <glibmm/stringutils.h>
26 #include <gtkmm/stock.h>
27 #include <glibmm/ustring.h>
28 #include <gtkmm/messagedialog.h>
29
30 #include "global.h"
31
32 // taken from gdk/gdkkeysyms.h
33 // (define on demand, to avoid unnecessary dev lib package build dependency)
34 #ifndef GDK_KEY_Control_L
35 # define GDK_KEY_Control_L 0xffe3
36 #endif
37 #ifndef GDK_KEY_Control_R
38 # define GDK_KEY_Control_R 0xffe4
39 #endif
40
41 static std::map<gig::dimension_t,int> caseOfDimRegion(gig::DimensionRegion* dr, bool* isValidZone) {
42 std::map<gig::dimension_t,int> dimCase;
43 if (!dr) {
44 *isValidZone = false;
45 return dimCase;
46 }
47
48 gig::Region* rgn = (gig::Region*) dr->GetParent();
49
50 // find the dimension region index of the passed dimension region
51 int drIndex;
52 for (drIndex = 0; drIndex < 256; ++drIndex)
53 if (rgn->pDimensionRegions[drIndex] == dr)
54 break;
55
56 // not found in region, something's horribly wrong
57 if (drIndex == 256) {
58 fprintf(stderr, "DimRegionChooser: ERROR: index of dim region not found!\n");
59 *isValidZone = false;
60 return std::map<gig::dimension_t,int>();
61 }
62
63 for (int d = 0, baseBits = 0; d < rgn->Dimensions; ++d) {
64 const int bits = rgn->pDimensionDefinitions[d].bits;
65 dimCase[rgn->pDimensionDefinitions[d].dimension] =
66 (drIndex >> baseBits) & ((1 << bits) - 1);
67 baseBits += bits;
68 // there are also DimensionRegion objects of unused zones, skip them
69 if (dimCase[rgn->pDimensionDefinitions[d].dimension] >= rgn->pDimensionDefinitions[d].zones) {
70 *isValidZone = false;
71 return std::map<gig::dimension_t,int>();
72 }
73 }
74
75 *isValidZone = true;
76 return dimCase;
77 }
78
79 DimRegionChooser::DimRegionChooser(Gtk::Window& window) :
80 red("#8070ff"),
81 black("black"),
82 white("white")
83 {
84 instrument = 0;
85 region = 0;
86 maindimregno = -1;
87 focus_line = 0;
88 resize.active = false;
89 cursor_is_resize = false;
90 h = 24;
91 multiSelectKeyDown = false;
92 set_can_focus();
93
94 actionGroup = Gtk::ActionGroup::create();
95 actionGroup->add(
96 Gtk::Action::create("SplitDimZone", _("Split Dimensions Zone")),
97 sigc::mem_fun(*this, &DimRegionChooser::split_dimension_zone)
98 );
99 actionGroup->add(
100 Gtk::Action::create("DeleteDimZone", _("Delete Dimension Zone")),
101 sigc::mem_fun(*this, &DimRegionChooser::delete_dimension_zone)
102 );
103
104 uiManager = Gtk::UIManager::create();
105 uiManager->insert_action_group(actionGroup);
106 Glib::ustring ui_info =
107 "<ui>"
108 " <popup name='PopupMenuInsideDimRegion'>"
109 " <menuitem action='SplitDimZone'/>"
110 " <menuitem action='DeleteDimZone'/>"
111 " </popup>"
112 // " <popup name='PopupMenuOutsideDimRegion'>"
113 // " <menuitem action='Add'/>"
114 // " </popup>"
115 "</ui>";
116 uiManager->add_ui_from_string(ui_info);
117
118 popup_menu_inside_dimregion = dynamic_cast<Gtk::Menu*>(
119 uiManager->get_widget("/PopupMenuInsideDimRegion"));
120 // popup_menu_outside_dimregion = dynamic_cast<Gtk::Menu*>(
121 // uiManager->get_widget("/PopupMenuOutsideDimRegion"));
122
123 add_events(Gdk::BUTTON_PRESS_MASK | Gdk::POINTER_MOTION_MASK |
124 Gdk::POINTER_MOTION_HINT_MASK);
125
126 labels_changed = true;
127
128 set_tooltip_text(_(
129 "Right click here for options on altering dimension zones. Press and "
130 "hold CTRL key for selecting multiple dimension zones simultaniously."
131 ));
132
133 window.signal_key_press_event().connect(
134 sigc::mem_fun(*this, &DimRegionChooser::onKeyPressed)
135 );
136 window.signal_key_release_event().connect(
137 sigc::mem_fun(*this, &DimRegionChooser::onKeyReleased)
138 );
139 }
140
141 DimRegionChooser::~DimRegionChooser()
142 {
143 }
144
145 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
146 bool DimRegionChooser::on_expose_event(GdkEventExpose* e)
147 {
148 double clipx1 = e->area.x;
149 double clipx2 = e->area.x + e->area.width;
150 double clipy1 = e->area.y;
151 double clipy2 = e->area.y + e->area.height;
152
153 const Cairo::RefPtr<Cairo::Context>& cr =
154 get_window()->create_cairo_context();
155 #else
156 bool DimRegionChooser::on_draw(const Cairo::RefPtr<Cairo::Context>& cr)
157 {
158 double clipx1, clipx2, clipy1, clipy2;
159 cr->get_clip_extents(clipx1, clipy1, clipx2, clipy2);
160 #endif
161
162 if (!region) return true;
163
164 // This is where we draw on the window
165 int w = get_width();
166 Glib::RefPtr<Pango::Context> context = get_pango_context();
167
168 Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create(context);
169 cr->set_line_width(1);
170
171 int y = 0;
172 if (labels_changed || label_width - 10 > clipx1) {
173 // draw labels on the left (reflecting the dimension type)
174 double maxwidth = 0;
175 for (int i = 0 ; i < region->Dimensions ; i++) {
176 int nbZones = region->pDimensionDefinitions[i].zones;
177 if (nbZones) {
178 const char* dstr;
179 char dstrbuf[10];
180 switch (region->pDimensionDefinitions[i].dimension) {
181 case gig::dimension_none: dstr=_("none"); break;
182 case gig::dimension_samplechannel: dstr=_("samplechannel");
183 break;
184 case gig::dimension_layer: dstr=_("layer"); break;
185 case gig::dimension_velocity: dstr=_("velocity"); break;
186 case gig::dimension_channelaftertouch:
187 dstr=_("channelaftertouch"); break;
188 case gig::dimension_releasetrigger:
189 dstr=_("releasetrigger"); break;
190 case gig::dimension_keyboard: dstr=_("keyswitching"); break;
191 case gig::dimension_roundrobin: dstr=_("roundrobin"); break;
192 case gig::dimension_random: dstr=_("random"); break;
193 case gig::dimension_smartmidi: dstr=_("smartmidi"); break;
194 case gig::dimension_roundrobinkeyboard:
195 dstr=_("roundrobinkeyboard"); break;
196 case gig::dimension_modwheel: dstr=_("modwheel"); break;
197 case gig::dimension_breath: dstr=_("breath"); break;
198 case gig::dimension_foot: dstr=_("foot"); break;
199 case gig::dimension_portamentotime:
200 dstr=_("portamentotime"); break;
201 case gig::dimension_effect1: dstr=_("effect1"); break;
202 case gig::dimension_effect2: dstr=_("effect2"); break;
203 case gig::dimension_genpurpose1: dstr=_("genpurpose1"); break;
204 case gig::dimension_genpurpose2: dstr=_("genpurpose2"); break;
205 case gig::dimension_genpurpose3: dstr=_("genpurpose3"); break;
206 case gig::dimension_genpurpose4: dstr=_("genpurpose4"); break;
207 case gig::dimension_sustainpedal:
208 dstr=_("sustainpedal"); break;
209 case gig::dimension_portamento: dstr=_("portamento"); break;
210 case gig::dimension_sostenutopedal:
211 dstr=_("sostenutopedal"); break;
212 case gig::dimension_softpedal: dstr=_("softpedal"); break;
213 case gig::dimension_genpurpose5: dstr=_("genpurpose5"); break;
214 case gig::dimension_genpurpose6: dstr=_("genpurpose6"); break;
215 case gig::dimension_genpurpose7: dstr=_("genpurpose7"); break;
216 case gig::dimension_genpurpose8: dstr=_("genpurpose8"); break;
217 case gig::dimension_effect1depth:
218 dstr=_("effect1depth"); break;
219 case gig::dimension_effect2depth:
220 dstr=_("effect2depth"); break;
221 case gig::dimension_effect3depth:
222 dstr=_("effect3depth"); break;
223 case gig::dimension_effect4depth:
224 dstr=_("effect4depth"); break;
225 case gig::dimension_effect5depth:
226 dstr=_("effect5depth"); break;
227 default:
228 sprintf(dstrbuf, "%d",
229 region->pDimensionDefinitions[i].dimension);
230 dstr = dstrbuf;
231 break;
232 }
233 layout->set_text(dstr);
234
235 Pango::Rectangle rectangle = layout->get_logical_extents();
236 double text_w = double(rectangle.get_width()) / Pango::SCALE;
237 if (text_w > maxwidth) maxwidth = text_w;
238
239 if (y + h > clipy1 && y < clipy2 && text_w >= clipx1) {
240 double text_h = double(rectangle.get_height()) /
241 Pango::SCALE;
242 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
243 const Gdk::Color fg = get_style()->get_fg(get_state());
244 #else
245 const Gdk::RGBA fg =
246 get_style_context()->get_color(get_state_flags());
247 #endif
248 Gdk::Cairo::set_source_rgba(cr, fg);
249 cr->move_to(4, int(y + (h - text_h) / 2 + 0.5));
250 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 16) || GTKMM_MAJOR_VERSION < 2
251 pango_cairo_show_layout(cr->cobj(), layout->gobj());
252 #else
253 layout->show_in_cairo_context(cr);
254 #endif
255 }
256 }
257 y += h;
258 }
259 label_width = int(maxwidth + 10);
260 labels_changed = false;
261 }
262 if (label_width >= clipx2) return true;
263
264 // draw dimensions' zones areas
265 y = 0;
266 int bitpos = 0;
267 for (int i = 0 ; i < region->Dimensions ; i++) {
268 int nbZones = region->pDimensionDefinitions[i].zones;
269 if (nbZones) {
270 const gig::dimension_t dimension = region->pDimensionDefinitions[i].dimension;
271
272 if (y >= clipy2) break;
273 if (y + h > clipy1) {
274 // draw focus rectangle around dimension's label and zones
275 if (has_focus() && focus_line == i) {
276 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
277 Gdk::Rectangle farea(0, y, 150, h);
278 get_style()->paint_focus(get_window(), get_state(), farea,
279 *this, "",
280 0, y, label_width, h);
281 #else
282 get_style_context()->render_focus(cr,
283 0, y, label_width, h);
284 #endif
285 }
286
287 // draw top and bottom lines of dimension's zones
288 Gdk::Cairo::set_source_rgba(cr, black);
289 cr->move_to(label_width, y + 0.5);
290 cr->line_to(w, y + 0.5);
291 cr->move_to(w, y + h - 0.5);
292 cr->line_to(label_width, y + h - 0.5);
293 cr->stroke();
294
295 // erase whole dimension's zones area
296 Gdk::Cairo::set_source_rgba(cr, white);
297 cr->rectangle(label_width + 1, y + 1,
298 (w - label_width - 2), h - 2);
299 cr->fill();
300
301 int c = 0;
302 if (maindimregno >= 0) {
303 int mask =
304 ~(((1 << region->pDimensionDefinitions[i].bits) - 1) <<
305 bitpos);
306 c = maindimregno & mask; // mask away this dimension
307 }
308 bool customsplits =
309 ((region->pDimensionDefinitions[i].split_type ==
310 gig::split_type_normal &&
311 region->pDimensionRegions[c]->DimensionUpperLimits[i]) ||
312 (region->pDimensionDefinitions[i].dimension ==
313 gig::dimension_velocity &&
314 region->pDimensionRegions[c]->VelocityUpperLimit));
315
316 // draw dimension zones
317 Gdk::Cairo::set_source_rgba(cr, black);
318 if (customsplits) {
319 cr->move_to(label_width + 0.5, y + 1);
320 cr->line_to(label_width + 0.5, y + h - 1);
321 int prevX = label_width;
322 int prevUpperLimit = -1;
323
324 for (int j = 0 ; j < nbZones ; j++) {
325 // draw dimension zone's borders for custom splits
326 gig::DimensionRegion* d =
327 region->pDimensionRegions[c + (j << bitpos)];
328 int upperLimit = d->DimensionUpperLimits[i];
329 if (!upperLimit) upperLimit = d->VelocityUpperLimit;
330 int v = upperLimit + 1;
331 int x = int((w - label_width - 1) * v / 128.0 + 0.5) +
332 label_width;
333 if (x >= clipx2) break;
334 if (x < clipx1) continue;
335 Gdk::Cairo::set_source_rgba(cr, black);
336 cr->move_to(x + 0.5, y + 1);
337 cr->line_to(x + 0.5, y + h - 1);
338 cr->stroke();
339
340 // draw fill for zone
341 bool isSelectedZone = this->dimzones[dimension].count(j);
342 Gdk::Cairo::set_source_rgba(cr, isSelectedZone ? red : white);
343 cr->rectangle(prevX + 1, y + 1, x - prevX - 1, h - 1);
344 cr->fill();
345
346 // draw text showing the beginning of the dimension zone
347 // as numeric value to the user
348 {
349 Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create(context);
350 layout->set_text(Glib::Ascii::dtostr(prevUpperLimit+1));
351 Gdk::Cairo::set_source_rgba(cr, black);
352 // get the text dimensions
353 int text_width, text_height;
354 layout->get_pixel_size(text_width, text_height);
355 // move text to the left end of the dimension zone
356 cr->move_to(prevX + 3, y + (h - text_height) / 2);
357 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 16) || GTKMM_MAJOR_VERSION < 2
358 pango_cairo_show_layout(cr->cobj(), layout->gobj());
359 #else
360 layout->show_in_cairo_context(cr);
361 #endif
362 }
363 // draw text showing the end of the dimension zone
364 // as numeric value to the user
365 {
366 Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create(context);
367 layout->set_text(Glib::Ascii::dtostr(upperLimit));
368 Gdk::Cairo::set_source_rgba(cr, black);
369 // get the text dimensions
370 int text_width, text_height;
371 layout->get_pixel_size(text_width, text_height);
372 // move text to the left end of the dimension zone
373 cr->move_to(x - 3 - text_width, y + (h - text_height) / 2);
374 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 16) || GTKMM_MAJOR_VERSION < 2
375 pango_cairo_show_layout(cr->cobj(), layout->gobj());
376 #else
377 layout->show_in_cairo_context(cr);
378 #endif
379 }
380
381 prevX = x;
382 prevUpperLimit = upperLimit;
383 }
384 } else {
385 int prevX = 0;
386 for (int j = 0 ; j <= nbZones ; j++) {
387 // draw dimension zone's borders for normal splits
388 int x = int((w - label_width - 1) * j /
389 double(nbZones) + 0.5) + label_width;
390 if (x >= clipx2) break;
391 if (x < clipx1) continue;
392 Gdk::Cairo::set_source_rgba(cr, black);
393 cr->move_to(x + 0.5, y + 1);
394 cr->line_to(x + 0.5, y + h - 1);
395 cr->stroke();
396
397 if (j != 0) {
398 // draw fill for zone
399 bool isSelectedZone = this->dimzones[dimension].count(j-1);
400 Gdk::Cairo::set_source_rgba(cr, isSelectedZone ? red : white);
401 cr->rectangle(prevX + 1, y + 1, x - prevX - 1, h - 1);
402 cr->fill();
403
404 // draw text showing the beginning of the dimension zone
405 // as numeric value to the user
406 {
407 Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create(context);
408 layout->set_text(Glib::Ascii::dtostr((j-1) * 128/nbZones));
409 Gdk::Cairo::set_source_rgba(cr, black);
410 // get the text dimensions
411 int text_width, text_height;
412 layout->get_pixel_size(text_width, text_height);
413 // move text to the left end of the dimension zone
414 cr->move_to(prevX + 3, y + (h - text_height) / 2);
415 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 16) || GTKMM_MAJOR_VERSION < 2
416 pango_cairo_show_layout(cr->cobj(), layout->gobj());
417 #else
418 layout->show_in_cairo_context(cr);
419 #endif
420 }
421 // draw text showing the end of the dimension zone
422 // as numeric value to the user
423 {
424 Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create(context);
425 layout->set_text(Glib::Ascii::dtostr(j * 128/nbZones - 1));
426 Gdk::Cairo::set_source_rgba(cr, black);
427 // get the text dimensions
428 int text_width, text_height;
429 layout->get_pixel_size(text_width, text_height);
430 // move text to the left end of the dimension zone
431 cr->move_to(x - 3 - text_width, y + (h - text_height) / 2);
432 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 16) || GTKMM_MAJOR_VERSION < 2
433 pango_cairo_show_layout(cr->cobj(), layout->gobj());
434 #else
435 layout->show_in_cairo_context(cr);
436 #endif
437 }
438 }
439 prevX = x;
440 }
441 }
442 }
443 y += h;
444 }
445 bitpos += region->pDimensionDefinitions[i].bits;
446 }
447
448 return true;
449 }
450
451 void DimRegionChooser::set_region(gig::Region* region)
452 {
453 this->region = region;
454 maindimregno = 0;
455 nbDimensions = 0;
456 if (region) {
457 int bitcount = 0;
458 for (int dim = 0 ; dim < region->Dimensions ; dim++) {
459 if (region->pDimensionDefinitions[dim].bits == 0) continue;
460 nbDimensions++;
461
462 int z = std::min(maindimcase[region->pDimensionDefinitions[dim].dimension],
463 region->pDimensionDefinitions[dim].zones - 1);
464 maindimregno |= (z << bitcount);
465 bitcount += region->pDimensionDefinitions[dim].bits;
466 }
467 }
468 dimregion_selected();
469 set_size_request(800, region ? nbDimensions * h : 0);
470
471 labels_changed = true;
472 queue_resize();
473 queue_draw();
474 }
475
476 void DimRegionChooser::refresh_all() {
477 set_region(region);
478 }
479
480 void DimRegionChooser::get_dimregions(const gig::Region* region, bool stereo,
481 std::set<gig::DimensionRegion*>& dimregs) const
482 {
483 for (int iDimRgn = 0; iDimRgn < 256; ++iDimRgn) {
484 gig::DimensionRegion* dimRgn = region->pDimensionRegions[iDimRgn];
485 if (!dimRgn) continue;
486 bool isValidZone;
487 std::map<gig::dimension_t,int> dimCase = caseOfDimRegion(dimRgn, &isValidZone);
488 if (!isValidZone) continue;
489 for (std::map<gig::dimension_t,int>::const_iterator it = dimCase.begin();
490 it != dimCase.end(); ++it)
491 {
492 if (stereo && it->first == gig::dimension_samplechannel) continue; // is selected
493
494 std::map<gig::dimension_t, std::set<int> >::const_iterator itSelectedDimension =
495 this->dimzones.find(it->first);
496 if (itSelectedDimension != this->dimzones.end() &&
497 itSelectedDimension->second.count(it->second)) continue; // is selected
498
499 goto notSelected;
500 }
501
502 dimregs.insert(dimRgn);
503
504 notSelected:
505 ;
506 }
507 }
508
509 void DimRegionChooser::update_after_resize()
510 {
511 if (region->pDimensionDefinitions[resize.dimension].dimension == gig::dimension_velocity) {
512
513 int bitpos = 0;
514 for (int j = 0 ; j < resize.dimension ; j++) {
515 bitpos += region->pDimensionDefinitions[j].bits;
516 }
517 int mask =
518 ~(((1 << region->pDimensionDefinitions[resize.dimension].bits) - 1) << bitpos);
519 int c = maindimregno & mask; // mask away this dimension
520
521 if (region->pDimensionRegions[c]->DimensionUpperLimits[resize.dimension] == 0) {
522 // the velocity dimension didn't previously have
523 // custom v3 splits, so we initialize all splits with
524 // default values
525 int nbZones = region->pDimensionDefinitions[resize.dimension].zones;
526 for (int j = 0 ; j < nbZones ; j++) {
527 gig::DimensionRegion* d = region->pDimensionRegions[c + (j << bitpos)];
528 d->DimensionUpperLimits[resize.dimension] = int(128.0 * (j + 1) / nbZones - 1);
529 }
530 }
531 if (region->pDimensionRegions[c]->VelocityUpperLimit == 0) {
532 // the velocity dimension didn't previously have
533 // custom v2 splits, so we initialize all splits with
534 // default values
535 int nbZones = region->pDimensionDefinitions[resize.dimension].zones;
536 for (int j = 0 ; j < nbZones ; j++) {
537 gig::DimensionRegion* d = region->pDimensionRegions[c + (j << bitpos)];
538 d->VelocityUpperLimit = int(128.0 * (j + 1) / nbZones - 1);
539 }
540 }
541
542 gig::DimensionRegion* d = region->pDimensionRegions[c + resize.offset];
543 // update both v2 and v3 values
544 d->DimensionUpperLimits[resize.dimension] = resize.pos - 1;
545 d->VelocityUpperLimit = resize.pos - 1;
546
547 } else {
548 for (int i = 0 ; i < region->DimensionRegions ; ) {
549
550 if (region->pDimensionRegions[i]->DimensionUpperLimits[resize.dimension] == 0) {
551 // the dimension didn't previously have custom
552 // limits, so we have to set default limits for
553 // all the dimension regions
554 int bitpos = 0;
555 for (int j = 0 ; j < resize.dimension ; j++) {
556 bitpos += region->pDimensionDefinitions[j].bits;
557 }
558 int nbZones = region->pDimensionDefinitions[resize.dimension].zones;
559
560 for (int j = 0 ; j < nbZones ; j++) {
561 gig::DimensionRegion* d = region->pDimensionRegions[i + (j << bitpos)];
562 d->DimensionUpperLimits[resize.dimension] = int(128.0 * (j + 1) / nbZones - 1);
563 }
564 }
565 gig::DimensionRegion* d = region->pDimensionRegions[i + resize.offset];
566 d->DimensionUpperLimits[resize.dimension] = resize.pos - 1;
567
568 int bitpos = 0;
569 int j;
570 for (j = 0 ; j < region->Dimensions ; j++) {
571 if (j != resize.dimension) {
572 int maxzones = 1 << region->pDimensionDefinitions[j].bits;
573 int dimj = (i >> bitpos) & (maxzones - 1);
574 if (dimj + 1 < region->pDimensionDefinitions[j].zones) break;
575 }
576 bitpos += region->pDimensionDefinitions[j].bits;
577 }
578 if (j == region->Dimensions) break;
579 i = (i & ~((1 << bitpos) - 1)) + (1 << bitpos);
580 }
581 }
582 }
583
584 bool DimRegionChooser::on_button_release_event(GdkEventButton* event)
585 {
586 if (resize.active) {
587 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
588 get_window()->pointer_ungrab(event->time);
589 #else
590 Glib::wrap(event->device, true)->ungrab(event->time);
591 #endif
592 resize.active = false;
593
594 region_changed();
595
596 if (!is_in_resize_zone(event->x, event->y) && cursor_is_resize) {
597 get_window()->set_cursor();
598 cursor_is_resize = false;
599 }
600 }
601 return true;
602 }
603
604 bool DimRegionChooser::on_button_press_event(GdkEventButton* event)
605 {
606 int w = get_width();
607 if (region && event->y < nbDimensions * h &&
608 event->x >= label_width && event->x < w) {
609
610 if (is_in_resize_zone(event->x, event->y)) {
611 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
612 get_window()->pointer_grab(false,
613 Gdk::BUTTON_RELEASE_MASK |
614 Gdk::POINTER_MOTION_MASK |
615 Gdk::POINTER_MOTION_HINT_MASK,
616 Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW),
617 event->time);
618 #else
619 Glib::wrap(event->device, true)->grab(get_window(),
620 Gdk::OWNERSHIP_NONE,
621 false,
622 Gdk::BUTTON_RELEASE_MASK |
623 Gdk::POINTER_MOTION_MASK |
624 Gdk::POINTER_MOTION_HINT_MASK,
625 Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW),
626 event->time);
627 #endif
628 resize.active = true;
629 } else {
630 int ydim = int(event->y / h);
631 int dim;
632 for (dim = 0 ; dim < region->Dimensions ; dim++) {
633 if (region->pDimensionDefinitions[dim].bits == 0) continue;
634 if (ydim == 0) break;
635 ydim--;
636 }
637 int nbZones = region->pDimensionDefinitions[dim].zones;
638
639 int z = -1;
640 int bitpos = 0;
641 for (int i = 0 ; i < dim ; i++) {
642 bitpos += region->pDimensionDefinitions[i].bits;
643 }
644
645 int i = dim;
646 if (maindimregno < 0) maindimregno = 0;
647 int mask = ~(((1 << region->pDimensionDefinitions[i].bits) - 1) << bitpos);
648 int c = this->maindimregno & mask; // mask away this dimension
649
650 bool customsplits =
651 ((region->pDimensionDefinitions[i].split_type == gig::split_type_normal &&
652 region->pDimensionRegions[c]->DimensionUpperLimits[i]) ||
653 (region->pDimensionDefinitions[i].dimension == gig::dimension_velocity &&
654 region->pDimensionRegions[c]->VelocityUpperLimit));
655 if (customsplits) {
656 int val = int((event->x - label_width) * 128 / (w - label_width - 1));
657
658 if (region->pDimensionRegions[c]->DimensionUpperLimits[i]) {
659 for (z = 0 ; z < nbZones ; z++) {
660 gig::DimensionRegion* d = region->pDimensionRegions[c + (z << bitpos)];
661 if (val <= d->DimensionUpperLimits[i]) break;
662 }
663 } else {
664 for (z = 0 ; z < nbZones ; z++) {
665 gig::DimensionRegion* d = region->pDimensionRegions[c + (z << bitpos)];
666 if (val <= d->VelocityUpperLimit) break;
667 }
668 }
669 } else {
670 z = int((event->x - label_width) * nbZones / (w - label_width - 1));
671 }
672
673 printf("dim=%d z=%d dimensionsource=%d split_type=%d zones=%d zone_size=%f\n", dim, z,
674 region->pDimensionDefinitions[dim].dimension,
675 region->pDimensionDefinitions[dim].split_type,
676 region->pDimensionDefinitions[dim].zones,
677 region->pDimensionDefinitions[dim].zone_size);
678 this->maindimcase[region->pDimensionDefinitions[dim].dimension] = z;
679 this->maindimregno = c | (z << bitpos);
680 this->maindimtype = region->pDimensionDefinitions[dim].dimension;
681
682 if (multiSelectKeyDown) {
683 if (dimzones[this->maindimtype].count(z)) {
684 if (dimzones[this->maindimtype].size() > 1) {
685 dimzones[this->maindimtype].erase(z);
686 }
687 } else {
688 dimzones[this->maindimtype].insert(z);
689 }
690 } else {
691 this->dimzones.clear();
692 for (std::map<gig::dimension_t,int>::const_iterator it = this->maindimcase.begin();
693 it != this->maindimcase.end(); ++it)
694 {
695 this->dimzones[it->first].insert(it->second);
696 }
697 }
698
699 focus_line = dim;
700 if (has_focus()) queue_draw();
701 else grab_focus();
702 dimregion_selected();
703
704 if (event->button == 3) {
705 printf("dimregion right click\n");
706 popup_menu_inside_dimregion->popup(event->button, event->time);
707 }
708
709 queue_draw();
710 }
711 }
712 return true;
713 }
714
715 bool DimRegionChooser::on_motion_notify_event(GdkEventMotion* event)
716 {
717 Glib::RefPtr<Gdk::Window> window = get_window();
718 int x, y;
719 Gdk::ModifierType state = Gdk::ModifierType(0);
720 window->get_pointer(x, y, state);
721
722 if (resize.active) {
723 int w = get_width();
724 int k = int((x - label_width) * 128.0 / (w - label_width - 1) + 0.5);
725
726 if (k < resize.min) k = resize.min;
727 else if (k > resize.max) k = resize.max;
728
729 if (k < 2) k = 2; // k is upper limit + 1, upper limit 0 is forbidden
730
731 if (k != resize.pos) {
732 int prevx = int((w - label_width - 1) * resize.pos / 128.0 + 0.5) + label_width;
733 int x = int((w - label_width - 1) * k / 128.0 + 0.5) + label_width;
734 int y = resize.dimension * h;
735 int x1, x2;
736 if (k > resize.pos) {
737 x1 = prevx;
738 x2 = x;
739 } else {
740 x1 = x;
741 x2 = prevx;
742 }
743 Gdk::Rectangle rect(x1, y + 1, x2 - x1 + 1, h - 2);
744
745 resize.pos = k;
746 update_after_resize();
747 get_window()->invalidate_rect(rect, false); // not sufficient ...
748 queue_draw(); // ... so do a complete redraw instead.
749 }
750 } else {
751 if (is_in_resize_zone(x, y)) {
752 if (!cursor_is_resize) {
753 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
754 window->set_cursor(Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW));
755 #else
756 window->set_cursor(Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW));
757 #endif
758 cursor_is_resize = true;
759 }
760 } else if (cursor_is_resize) {
761 window->set_cursor();
762 cursor_is_resize = false;
763 }
764 }
765 return true;
766 }
767
768 bool DimRegionChooser::is_in_resize_zone(double x, double y)
769 {
770 int w = get_width();
771 if (region && y < nbDimensions * h && x >= label_width && x < w) {
772 int ydim = int(y / h);
773 int dim;
774 int bitpos = 0;
775 for (dim = 0 ; dim < region->Dimensions ; dim++) {
776 if (region->pDimensionDefinitions[dim].bits == 0) continue;
777 if (ydim == 0) break;
778 ydim--;
779 bitpos += region->pDimensionDefinitions[dim].bits;
780 }
781 int nbZones = region->pDimensionDefinitions[dim].zones;
782
783 int c = 0;
784 if (maindimregno >= 0) {
785 int mask = ~(((1 << region->pDimensionDefinitions[dim].bits) - 1) << bitpos);
786 c = maindimregno & mask; // mask away this dimension
787 }
788 const bool customsplits =
789 ((region->pDimensionDefinitions[dim].split_type == gig::split_type_normal &&
790 region->pDimensionRegions[c]->DimensionUpperLimits[dim]) ||
791 (region->pDimensionDefinitions[dim].dimension == gig::dimension_velocity &&
792 region->pDimensionRegions[c]->VelocityUpperLimit));
793
794 // dimensions of split_type_bit cannot be resized
795 if (region->pDimensionDefinitions[dim].split_type != gig::split_type_bit) {
796 int prev_limit = 0;
797 for (int iZone = 0 ; iZone < nbZones - 1 ; iZone++) {
798 gig::DimensionRegion* d = region->pDimensionRegions[c + (iZone << bitpos)];
799 const int upperLimit =
800 (customsplits) ?
801 (d->DimensionUpperLimits[dim]) ?
802 d->DimensionUpperLimits[dim] : d->VelocityUpperLimit
803 : (iZone+1) * (int)region->pDimensionDefinitions[dim].zone_size - 1;
804 int limit = upperLimit + 1;
805 int limitx = int((w - label_width - 1) * limit / 128.0 + 0.5) + label_width;
806 if (x <= limitx - 2) break;
807 if (x <= limitx + 2) {
808 resize.dimension = dim;
809 resize.offset = iZone << bitpos;
810 resize.pos = limit;
811 resize.min = prev_limit;
812
813 int dr = (maindimregno >> bitpos) &
814 ((1 << region->pDimensionDefinitions[dim].bits) - 1);
815 resize.selected = dr == iZone ? resize.left :
816 dr == iZone + 1 ? resize.right : resize.none;
817
818 iZone++;
819 gig::DimensionRegion* d = region->pDimensionRegions[c + (iZone << bitpos)];
820
821 const int upperLimit =
822 (customsplits) ?
823 (d->DimensionUpperLimits[dim]) ?
824 d->DimensionUpperLimits[dim] : d->VelocityUpperLimit
825 : (iZone+1) * (int)region->pDimensionDefinitions[dim].zone_size - 1;
826
827 int limit = upperLimit + 1;
828 resize.max = limit;
829 return true;
830 }
831 prev_limit = limit;
832 }
833 }
834 }
835 return false;
836 }
837
838 sigc::signal<void>& DimRegionChooser::signal_dimregion_selected()
839 {
840 return dimregion_selected;
841 }
842
843 sigc::signal<void>& DimRegionChooser::signal_region_changed()
844 {
845 return region_changed;
846 }
847
848 bool DimRegionChooser::on_focus(Gtk::DirectionType direction)
849 {
850 // TODO: check that region exists etc, that is, that it's possible
851 // to set focus
852 if (direction == Gtk::DIR_TAB_FORWARD ||
853 direction == Gtk::DIR_DOWN) {
854 if (!has_focus()) {
855 focus_line = 0;
856 grab_focus();
857 return true;
858 } else {
859 if (focus_line + 1 < region->Dimensions) {
860 focus_line++;
861 queue_draw();
862 return true;
863 } else {
864 return false;
865 }
866 }
867 } else if (direction == Gtk::DIR_TAB_BACKWARD ||
868 direction == Gtk::DIR_UP) {
869 if (!has_focus()) {
870 focus_line = region->Dimensions - 1;
871 grab_focus();
872 return true;
873 } else {
874 if (focus_line > 0) {
875 focus_line--;
876 queue_draw();
877 return true;
878 } else {
879 return false;
880 }
881 }
882 } else if (!has_focus()) {
883 // TODO: check that focus_line exists
884 grab_focus();
885 return true;
886 } else {
887 // TODO: increase or decrease value
888 }
889 return false;
890 }
891
892 void DimRegionChooser::split_dimension_zone() {
893 printf("split_dimension_zone() type=%d, zone=%d\n", maindimtype, maindimcase[maindimtype]);
894 try {
895 region->SplitDimensionZone(maindimtype, maindimcase[maindimtype]);
896 } catch (RIFF::Exception e) {
897 Gtk::MessageDialog msg(e.Message, false, Gtk::MESSAGE_ERROR);
898 msg.run();
899 } catch (...) {
900 Glib::ustring txt = _("An unknown exception occurred!");
901 Gtk::MessageDialog msg(txt, false, Gtk::MESSAGE_ERROR);
902 msg.run();
903 }
904 refresh_all();
905 }
906
907 void DimRegionChooser::delete_dimension_zone() {
908 printf("delete_dimension_zone() type=%d, zone=%d\n", maindimtype, maindimcase[maindimtype]);
909 try {
910 region->DeleteDimensionZone(maindimtype, maindimcase[maindimtype]);
911 } catch (RIFF::Exception e) {
912 Gtk::MessageDialog msg(e.Message, false, Gtk::MESSAGE_ERROR);
913 msg.run();
914 } catch (...) {
915 Glib::ustring txt = _("An unknown exception occurred!");
916 Gtk::MessageDialog msg(txt, false, Gtk::MESSAGE_ERROR);
917 msg.run();
918 }
919 refresh_all();
920 }
921
922 bool DimRegionChooser::onKeyPressed(GdkEventKey* key) {
923 //printf("key down\n");
924 if (key->keyval == GDK_KEY_Control_L || key->keyval == GDK_KEY_Control_R)
925 multiSelectKeyDown = true;
926 return false;
927 }
928
929 bool DimRegionChooser::onKeyReleased(GdkEventKey* key) {
930 //printf("key up\n");
931 if (key->keyval == GDK_KEY_Control_L || key->keyval == GDK_KEY_Control_R)
932 multiSelectKeyDown = false;
933 return false;
934 }
935
936 void DimRegionChooser::resetSelectedZones() {
937 this->dimzones.clear();
938 if (!region) {
939 queue_draw(); // redraw required parts
940 return;
941 }
942 if (maindimregno < 0 || maindimregno >= region->DimensionRegions) {
943 queue_draw(); // redraw required parts
944 return;
945 }
946 if (!region->pDimensionRegions[maindimregno]) {
947 queue_draw(); // redraw required parts
948 return;
949 }
950 gig::DimensionRegion* dimrgn = region->pDimensionRegions[maindimregno];
951
952 bool isValidZone;
953 this->maindimcase = caseOfDimRegion(dimrgn, &isValidZone);
954 if (!isValidZone) {
955 queue_draw(); // redraw required parts
956 return;
957 }
958
959 for (std::map<gig::dimension_t,int>::const_iterator it = this->maindimcase.begin();
960 it != this->maindimcase.end(); ++it)
961 {
962 this->dimzones[it->first].insert(it->second);
963 }
964
965 // redraw required parts
966 queue_draw();
967 }
968
969 bool DimRegionChooser::select_dimregion(gig::DimensionRegion* dimrgn) {
970 if (!region) return false; //.selection failed
971
972 for (int dr = 0; dr < region->DimensionRegions && region->pDimensionRegions[dr]; ++dr) {
973 if (region->pDimensionRegions[dr] == dimrgn) {
974 // reset dim region zone selection to the requested specific dim region case
975 maindimregno = dr;
976 resetSelectedZones();
977
978 // emit signal that dimregion selection has changed, for external entities
979 dimregion_selected();
980
981 return true; // selection success
982 }
983 }
984
985 return false; //.selection failed
986 }
987
988 gig::DimensionRegion* DimRegionChooser::get_main_dimregion() const {
989 if (!region) return NULL;
990 return region->pDimensionRegions[maindimregno];
991 }

  ViewVC Help
Powered by ViewVC