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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2562 - (show annotations) (download)
Mon May 19 18:06:57 2014 UTC (5 years, 10 months ago) by schoenebeck
File size: 23257 byte(s)
* 'Dimension Manager' dialog: added support for viewing and editing
  dimensions of all regions simultaniously.

1 /*
2 * Copyright (C) 2006-2014 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 "dimensionmanager.h"
21
22 #include <gtkmm/stock.h>
23 #include <gtkmm/messagedialog.h>
24 #include <gtkmm/dialog.h>
25 #include <gtkmm/comboboxtext.h>
26 #include <gtkmm/spinbutton.h>
27 #include <gtkmm/table.h>
28
29 #include "global.h"
30 #include "compat.h"
31
32 // returns a human readable name of the given dimension type
33 Glib::ustring dimTypeAsString(gig::dimension_t d) {
34 char buf[32];
35 switch (d) {
36 case gig::dimension_none:
37 return _("None");
38 case gig::dimension_samplechannel:
39 return _("Sample Channel");
40 case gig::dimension_layer:
41 return _("Layer");
42 case gig::dimension_velocity:
43 return _("Velocity");
44 case gig::dimension_channelaftertouch:
45 return _("Aftertouch");
46 case gig::dimension_releasetrigger:
47 return _("Release Trigger");
48 case gig::dimension_keyboard:
49 return _("Keyswitching");
50 case gig::dimension_roundrobin:
51 return _("Round Robin");
52 case gig::dimension_random:
53 return _("Random Generator");
54 case gig::dimension_smartmidi:
55 return _("Smart MIDI");
56 case gig::dimension_roundrobinkeyboard:
57 return _("Keyboard Round Robin");
58 case gig::dimension_modwheel:
59 return _("Modulation Wheel");
60 case gig::dimension_breath:
61 return _("Breath Ctrl.");
62 case gig::dimension_foot:
63 return _("Foot Ctrl.");
64 case gig::dimension_portamentotime:
65 return _("Portamento Time Ctrl.");
66 case gig::dimension_effect1:
67 return _("Effect Ctrl. 1");
68 case gig::dimension_effect2:
69 return _("Effect Ctrl. 2");
70 case gig::dimension_genpurpose1:
71 return _("General Purpose Ctrl. 1");
72 case gig::dimension_genpurpose2:
73 return _("General Purpose Ctrl. 2");
74 case gig::dimension_genpurpose3:
75 return _("General Purpose Ctrl. 3");
76 case gig::dimension_genpurpose4:
77 return _("General Purpose Ctrl. 4");
78 case gig::dimension_sustainpedal:
79 return _("Sustain Pedal");
80 case gig::dimension_portamento:
81 return _("Portamento Ctrl.");
82 case gig::dimension_sostenutopedal:
83 return _("Sostenuto Pedal");
84 case gig::dimension_softpedal:
85 return _("Soft Pedal");
86 case gig::dimension_genpurpose5:
87 return _("General Purpose Ctrl. 5");
88 case gig::dimension_genpurpose6:
89 return _("General Purpose Ctrl. 6");
90 case gig::dimension_genpurpose7:
91 return _("General Purpose Ctrl. 7");
92 case gig::dimension_genpurpose8:
93 return _("General Purpose Ctrl. 8");
94 case gig::dimension_effect1depth:
95 return _("Effect 1 Depth");
96 case gig::dimension_effect2depth:
97 return _("Effect 2 Depth");
98 case gig::dimension_effect3depth:
99 return _("Effect 3 Depth");
100 case gig::dimension_effect4depth:
101 return _("Effect 4 Depth");
102 case gig::dimension_effect5depth:
103 return _("Effect 5 Depth");
104 default:
105 sprintf(buf, "Unknown Type (0x%x) !!!", d);
106 return buf;
107 }
108 }
109
110 // returns a human readable description of the given dimension
111 static Glib::ustring __dimDescriptionAsString(gig::dimension_t d) {
112 switch (d) {
113 case gig::dimension_none:
114 return _("Dimension not in use");
115 case gig::dimension_samplechannel:
116 return _("If used sample has more than one channel (thus is not mono)");
117 case gig::dimension_layer:
118 return _("For layering of up to 8 instruments (and eventually crossfading of 2 or 4 layers");
119 case gig::dimension_velocity:
120 return _("Key Velocity (this is the only dimension in gig2 where the ranges can exactly be defined)");
121 case gig::dimension_channelaftertouch:
122 return _("Channel Key Pressure");
123 case gig::dimension_releasetrigger:
124 return _("Special dimension for triggering samples on releasing a key");
125 case gig::dimension_keyboard:
126 return _("Dimension for keyswitching (keyboard)");
127 case gig::dimension_roundrobin:
128 return _("Different samples triggered each time a note is played, dimension regions selected in sequence");
129 case gig::dimension_random:
130 return _("Different samples triggered each time a note is played, random order");
131 case gig::dimension_smartmidi:
132 return _("For MIDI tools like legato and repetition mode");
133 case gig::dimension_roundrobinkeyboard:
134 return _("Different samples triggered each time a note is played, any key advances the counter");
135 case gig::dimension_modwheel:
136 return _("MIDI Controller 1");
137 case gig::dimension_breath:
138 return _("MIDI Controller 2");
139 case gig::dimension_foot:
140 return _("MIDI Controller 4");
141 case gig::dimension_portamentotime:
142 return _("MIDI Controller 5");
143 case gig::dimension_effect1:
144 return _("MIDI Controller 12");
145 case gig::dimension_effect2:
146 return _("MIDI Controller 13");
147 case gig::dimension_genpurpose1:
148 return _("Slider, MIDI Controller 16");
149 case gig::dimension_genpurpose2:
150 return _("Slider, MIDI Controller 17");
151 case gig::dimension_genpurpose3:
152 return _("Slider, MIDI Controller 18");
153 case gig::dimension_genpurpose4:
154 return _("Slider, MIDI Controller 19");
155 case gig::dimension_sustainpedal:
156 return _("MIDI Controller 64");
157 case gig::dimension_portamento:
158 return _("MIDI Controller 65");
159 case gig::dimension_sostenutopedal:
160 return _("MIDI Controller 66");
161 case gig::dimension_softpedal:
162 return _("MIDI Controller 67");
163 case gig::dimension_genpurpose5:
164 return _("Button, MIDI Controller 80");
165 case gig::dimension_genpurpose6:
166 return _("Button, MIDI Controller 81");
167 case gig::dimension_genpurpose7:
168 return _("Button, MIDI Controller 82");
169 case gig::dimension_genpurpose8:
170 return _("Button, MIDI Controller 83");
171 case gig::dimension_effect1depth:
172 return _("MIDI Controller 91");
173 case gig::dimension_effect2depth:
174 return _("MIDI Controller 92");
175 case gig::dimension_effect3depth:
176 return _("MIDI Controller 93");
177 case gig::dimension_effect4depth:
178 return _("MIDI Controller 94");
179 case gig::dimension_effect5depth:
180 return _("MIDI Controller 95");
181 default:
182 return _("Please report this !!!");
183 }
184 }
185
186 DimTypeCellRenderer::DimTypeCellRenderer() :
187 Glib::ObjectBase(typeid(DimTypeCellRenderer)),
188 Gtk::CellRendererText(),
189 m_propertyDimType(*this, "gigdimension_t", gig::dimension_none),
190 m_propertyUsageCount(*this, "intusagecount", 0),
191 m_propertyTotalRegions(*this, "inttotalregions", 0)
192 {
193 propertyDimType().signal_changed().connect(
194 sigc::mem_fun(*this, &DimTypeCellRenderer::typeChanged)
195 );
196 propertyUsageCount().signal_changed().connect(
197 sigc::mem_fun(*this, &DimTypeCellRenderer::statsChanged)
198 );
199 propertyTotalRegions().signal_changed().connect(
200 sigc::mem_fun(*this, &DimTypeCellRenderer::statsChanged)
201 );
202 }
203
204 void DimTypeCellRenderer::typeChanged() {
205 gig::dimension_t type = propertyDimType();
206 Glib::ustring s = dimTypeAsString(type);
207 property_text() = s;
208 }
209
210 void DimTypeCellRenderer::statsChanged() {
211 int usageCount = propertyUsageCount();
212 int totalRegions = propertyTotalRegions();
213 bool bDimensionExistsOnAllRegions = (usageCount == totalRegions);
214 property_foreground() = ((bDimensionExistsOnAllRegions) ? "black" : "gray");
215 }
216
217 IntSetCellRenderer::IntSetCellRenderer() :
218 Glib::ObjectBase(typeid(IntSetCellRenderer)),
219 Gtk::CellRendererText(),
220 m_propertyValue(*this, "stdintset", std::set<int>())
221 {
222 propertyValue().signal_changed().connect(
223 sigc::mem_fun(*this, &IntSetCellRenderer::valueChanged)
224 );
225 }
226
227 void IntSetCellRenderer::valueChanged() {
228 Glib::ustring s;
229 std::set<int> v = propertyValue();
230 for (std::set<int>::const_iterator it = v.begin(); it != v.end(); ++it) {
231 s += ToString(*it);
232 if (*it != *v.rbegin()) s += "|";
233 }
234 property_text() = s;
235 property_foreground() = (v.size() > 1) ? "gray" : "black";
236 }
237
238 DimensionManager::DimensionManager() :
239 addButton(Gtk::Stock::ADD), removeButton(Gtk::Stock::REMOVE),
240 allRegionsCheckBox(_("All Regions"))
241 {
242 set_title(_("Dimensions of selected Region"));
243 add(vbox);
244 scrolledWindow.add(treeView);
245 vbox.pack_start(scrolledWindow);
246 scrolledWindow.show();
247 vbox.pack_start(buttonBox, Gtk::PACK_SHRINK);
248 buttonBox.set_layout(Gtk::BUTTONBOX_END);
249 buttonBox.set_border_width(5);
250 buttonBox.show();
251 buttonBox.pack_start(allRegionsCheckBox, Gtk::PACK_EXPAND_PADDING);
252 buttonBox.pack_start(addButton, Gtk::PACK_SHRINK);
253 buttonBox.pack_start(removeButton, Gtk::PACK_SHRINK);
254 addButton.show();
255 removeButton.show();
256 allRegionsCheckBox.set_tooltip_text(
257 _("Enable this if you want to edit dimensions of all regions simultaniously.")
258 );
259
260 // setup the table
261 refTableModel = Gtk::ListStore::create(tableModel);
262 treeView.set_model(refTableModel);
263 treeView.append_column(_("Dimension Type"), m_cellRendererDimType);
264 treeView.append_column(_("Bits"), m_cellRendererIntSet);
265 treeView.append_column(_("Zones"), m_cellRendererIntSet);
266 treeView.append_column(_("Description"), tableModel.m_description);
267 treeView.get_column(0)->add_attribute(m_cellRendererDimType.propertyDimType(), tableModel.m_type);
268 treeView.get_column(0)->add_attribute(m_cellRendererDimType.propertyUsageCount(), tableModel.m_usageCount);
269 treeView.get_column(0)->add_attribute(m_cellRendererDimType.propertyTotalRegions(), tableModel.m_totalRegions);
270 treeView.get_column(1)->add_attribute(m_cellRendererIntSet.propertyValue(), tableModel.m_bits);
271 treeView.get_column(2)->add_attribute(m_cellRendererIntSet.propertyValue(), tableModel.m_zones);
272 treeView.show();
273
274 addButton.signal_clicked().connect(
275 sigc::mem_fun(*this, &DimensionManager::addDimension)
276 );
277
278 removeButton.signal_clicked().connect(
279 sigc::mem_fun(*this, &DimensionManager::removeDimension)
280 );
281 allRegionsCheckBox.signal_toggled().connect(
282 sigc::mem_fun(*this, &DimensionManager::onAllRegionsCheckBoxToggled)
283 );
284
285 show_all_children();
286
287 resize(460,300);
288 }
289
290 bool DimensionManager::allRegions() const {
291 return allRegionsCheckBox.get_active();
292 }
293
294 void DimensionManager::onAllRegionsCheckBoxToggled() {
295 set_title(
296 allRegions() ? _("Dimensions of all Regions") : _("Dimensions of selected Region")
297 );
298 treeView.set_tooltip_text(
299 allRegions()
300 ? _("Dimensions and numbers in gray indicates a difference among the individual regions.")
301 : _("You are currently only viewing dimensions of the currently selected region.")
302 );
303 refreshManager();
304 }
305
306 // following two data types are just used in DimensionManager::refresManager(),
307 // due to the maps template nature however, they must be declared at global
308 // space to avoid compilation errors
309 struct _DimDef {
310 std::set<int> bits;
311 std::set<int> zones;
312 int usageCount;
313 };
314 typedef std::map<gig::dimension_t, _DimDef> _Dimensions;
315
316 // update all GUI elements according to current gig::Region informations
317 void DimensionManager::refreshManager() {
318 set_sensitive(false);
319 refTableModel->clear();
320 if (allRegions()) {
321 if (region) {
322 _Dimensions dims;
323 gig::Instrument* instr = (gig::Instrument*)region->GetParent();
324 int iRegionsCount = 0;
325 for (gig::Region* rgn = instr->GetFirstRegion(); rgn; rgn = instr->GetNextRegion(), ++iRegionsCount) {
326 for (uint i = 0; i < rgn->Dimensions; i++) {
327 gig::dimension_def_t* dim = &rgn->pDimensionDefinitions[i];
328 dims[dim->dimension].bits.insert(dim->bits);
329 dims[dim->dimension].zones.insert(dim->zones);
330 dims[dim->dimension].usageCount++;
331 }
332 }
333 for (_Dimensions::const_iterator it = dims.begin(); it != dims.end(); ++it) {
334 Gtk::TreeModel::Row row = *(refTableModel->append());
335 row[tableModel.m_type] = it->first;
336 row[tableModel.m_bits] = it->second.bits;
337 row[tableModel.m_zones] = it->second.zones;
338 row[tableModel.m_description] = __dimDescriptionAsString(it->first);
339 row[tableModel.m_usageCount] = it->second.usageCount;
340 row[tableModel.m_totalRegions] = iRegionsCount;
341 }
342 }
343 } else {
344 if (region) {
345 for (uint i = 0; i < region->Dimensions; i++) {
346 gig::dimension_def_t* dim = &region->pDimensionDefinitions[i];
347 Gtk::TreeModel::Row row = *(refTableModel->append());
348 std::set<int> vBits;
349 vBits.insert(dim->bits);
350 row[tableModel.m_bits] = vBits;
351 std::set<int> vZones;
352 vZones.insert(dim->zones);
353 row[tableModel.m_zones] = vZones;
354 row[tableModel.m_description] = __dimDescriptionAsString(dim->dimension);
355 row[tableModel.m_type] = dim->dimension;
356 row[tableModel.m_usageCount] = 1;
357 row[tableModel.m_totalRegions] = 1;
358 }
359 }
360 }
361 set_sensitive(region);
362 }
363
364 void DimensionManager::show(gig::Region* region) {
365 this->region = region;
366 refreshManager();
367 Gtk::Window::show();
368 deiconify();
369 }
370
371 void DimensionManager::set_region(gig::Region* region) {
372 this->region = region;
373 refreshManager();
374 }
375
376 void DimensionManager::addDimension() {
377 Gtk::Dialog dialog(_("New Dimension"), true /*modal*/);
378 // add dimension type combo box to the dialog
379 Glib::RefPtr<Gtk::ListStore> refComboModel = Gtk::ListStore::create(comboModel);
380 for (int i = 0x01; i < 0xff; i++) {
381 Glib::ustring sType =
382 dimTypeAsString(static_cast<gig::dimension_t>(i));
383 if (sType.find("Unknown") != 0) {
384 Gtk::TreeModel::Row row = *(refComboModel->append());
385 row[comboModel.m_type_id] = i;
386 row[comboModel.m_type_name] = sType;
387 }
388 }
389 Gtk::Table table(2, 2);
390 Gtk::Label labelDimType(_("Dimension:"), Gtk::ALIGN_START);
391 Gtk::ComboBox comboDimType;
392 comboDimType.set_model(refComboModel);
393 comboDimType.pack_start(comboModel.m_type_id);
394 comboDimType.pack_start(comboModel.m_type_name);
395 Gtk::Label labelZones(_("Zones:"), Gtk::ALIGN_START);
396 table.attach(labelDimType, 0, 1, 0, 1);
397 table.attach(comboDimType, 1, 2, 0, 1);
398 table.attach(labelZones, 0, 1, 1, 2);
399 dialog.get_vbox()->pack_start(table);
400
401 // number of zones: use a combo box with fix values for gig
402 // v2 and a spin button for v3
403 Gtk::ComboBoxText comboZones;
404 Gtk::SpinButton spinZones;
405 bool version2 = false;
406 if (region) {
407 gig::File* file = (gig::File*)region->GetParent()->GetParent();
408 version2 = file->pVersion && file->pVersion->major == 2;
409 }
410 if (version2) {
411 for (int i = 1; i <= 5; i++) {
412 char buf[3];
413 sprintf(buf, "%d", 1 << i);
414 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 24) || GTKMM_MAJOR_VERSION < 2
415 comboZones.append_text(buf);
416 #else
417 comboZones.append(buf);
418 #endif
419 }
420 table.attach(comboZones, 1, 2, 1, 2);
421 } else {
422 spinZones.set_increments(1, 8);
423 spinZones.set_numeric(true);
424 spinZones.set_range(2, 128);
425 spinZones.set_value(2);
426 table.attach(spinZones, 1, 2, 1, 2);
427 }
428
429 dialog.add_button(Gtk::Stock::OK, 0);
430 dialog.add_button(Gtk::Stock::CANCEL, 1);
431 dialog.show_all_children();
432
433 if (!dialog.run()) { // OK selected ...
434 Gtk::TreeModel::iterator iterType = comboDimType.get_active();
435 if (!iterType) return;
436 Gtk::TreeModel::Row rowType = *iterType;
437 if (!rowType) return;
438 int iTypeID = rowType[comboModel.m_type_id];
439 gig::dimension_t type = static_cast<gig::dimension_t>(iTypeID);
440 gig::dimension_def_t dim;
441 dim.dimension = type;
442
443 if (version2) {
444 if (comboZones.get_active_row_number() < 0) return;
445 dim.bits = comboZones.get_active_row_number() + 1;
446 dim.zones = 1 << dim.bits;
447 } else {
448 dim.zones = spinZones.get_value_as_int();
449 dim.bits = zoneCountToBits(dim.zones);
450 }
451
452 // assemble the list of regions where the selected dimension shall be
453 // added to
454 std::vector<gig::Region*> vRegions;
455 if (allRegions()) {
456 gig::Instrument* instr = (gig::Instrument*)region->GetParent();
457 for (gig::Region* rgn = instr->GetFirstRegion(); rgn; rgn = instr->GetNextRegion()) {
458 if (!rgn->GetDimensionDefinition(type)) vRegions.push_back(rgn);
459 }
460 } else vRegions.push_back(region);
461
462 std::set<Glib::ustring> errors;
463
464 for (uint iRgn = 0; iRgn < vRegions.size(); ++iRgn) {
465 gig::Region* region = vRegions[iRgn];
466 try {
467 printf(
468 "Adding dimension (type=0x%x, bits=%d, zones=%d)\n",
469 dim.dimension, dim.bits, dim.zones
470 );
471 // notify everybody that we're going to update the region
472 region_to_be_changed_signal.emit(region);
473 // add the new dimension to the region
474 // (implicitly creates new dimension regions)
475 region->AddDimension(&dim);
476 // let everybody know there was a change
477 region_changed_signal.emit(region);
478 } catch (RIFF::Exception e) {
479 // notify that the changes are over (i.e. to avoid dead locks)
480 region_changed_signal.emit(region);
481 Glib::ustring txt = _("Could not add dimension: ") + e.Message;
482 if (vRegions.size() == 1) {
483 // show error message directly
484 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
485 msg.run();
486 } else {
487 // remember error, they are shown after all regions have been processed
488 errors.insert(txt);
489 }
490 }
491 }
492 // update all GUI elements
493 refreshManager();
494
495 if (!errors.empty()) {
496 Glib::ustring txt = _(
497 "The following errors occurred while trying to create the dimension on all regions:"
498 );
499 txt += "\n\n";
500 for (std::set<Glib::ustring>::const_iterator it = errors.begin();
501 it != errors.end(); ++it)
502 {
503 txt += "-> " + *it + "\n";
504 }
505 txt += "\n";
506 txt += _(
507 "You might also want to check the console for further warnings and "
508 "error messages."
509 );
510 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
511 msg.run();
512 }
513 }
514 }
515
516 void DimensionManager::removeDimension() {
517 Glib::RefPtr<Gtk::TreeSelection> sel = treeView.get_selection();
518 Gtk::TreeModel::iterator it = sel->get_selected();
519 if (it) {
520 Gtk::TreeModel::Row row = *it;
521 gig::dimension_t type = row[tableModel.m_type];
522
523 // assemble the list of regions where the selected dimension shall be
524 // added to
525 std::vector<gig::Region*> vRegions;
526 if (allRegions()) {
527 gig::Instrument* instr = (gig::Instrument*)region->GetParent();
528 for (gig::Region* rgn = instr->GetFirstRegion(); rgn; rgn = instr->GetNextRegion()) {
529 if (rgn->GetDimensionDefinition(type)) vRegions.push_back(rgn);
530 }
531 } else vRegions.push_back(region);
532
533 std::set<Glib::ustring> errors;
534
535 for (uint iRgn = 0; iRgn < vRegions.size(); ++iRgn) {
536 gig::Region* region = vRegions[iRgn];
537 gig::dimension_def_t* dim = region->GetDimensionDefinition(type);
538 try {
539 // notify everybody that we're going to update the region
540 region_to_be_changed_signal.emit(region);
541 // remove selected dimension
542 region->DeleteDimension(dim);
543 // let everybody know there was a change
544 region_changed_signal.emit(region);
545 } catch (RIFF::Exception e) {
546 // notify that the changes are over (i.e. to avoid dead locks)
547 region_changed_signal.emit(region);
548 Glib::ustring txt = _("Could not remove dimension: ") + e.Message;
549 if (vRegions.size() == 1) {
550 // show error message directly
551 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
552 msg.run();
553 } else {
554 // remember error, they are shown after all regions have been processed
555 errors.insert(txt);
556 }
557 }
558 }
559 // update all GUI elements
560 refreshManager();
561
562 if (!errors.empty()) {
563 Glib::ustring txt = _(
564 "The following errors occurred while trying to remove the dimension from all regions:"
565 );
566 txt += "\n\n";
567 for (std::set<Glib::ustring>::const_iterator it = errors.begin();
568 it != errors.end(); ++it)
569 {
570 txt += "-> " + *it + "\n";
571 }
572 txt += "\n";
573 txt += _(
574 "You might also want to check the console for further warnings and "
575 "error messages."
576 );
577 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
578 msg.run();
579 }
580 }
581 }

  ViewVC Help
Powered by ViewVC