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

Diff of /gigedit/trunk/src/gigedit/mainwindow.cpp

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1303 by persson, Sun Aug 26 09:29:52 2007 UTC revision 1661 by schoenebeck, Sun Feb 3 14:10:47 2008 UTC
# Line 1  Line 1 
1  /*  /*
2   * Copyright (C) 2006, 2007 Andreas Persson   * Copyright (C) 2006 - 2008 Andreas Persson
3   *   *
4   * This program is free software; you can redistribute it and/or   * This program is free software; you can redistribute it and/or
5   * modify it under the terms of the GNU General Public License as   * modify it under the terms of the GNU General Public License as
# Line 17  Line 17 
17   * 02110-1301 USA.   * 02110-1301 USA.
18   */   */
19    
 #include <libintl.h>  
20  #include <iostream>  #include <iostream>
21    
22  #include <gtkmm/filechooserdialog.h>  #include <gtkmm/filechooserdialog.h>
# Line 25  Line 24 
24  #include <gtkmm/stock.h>  #include <gtkmm/stock.h>
25  #include <gtkmm/targetentry.h>  #include <gtkmm/targetentry.h>
26  #include <gtkmm/main.h>  #include <gtkmm/main.h>
27    #include <gtkmm/toggleaction.h>
28    
29    #include "global.h"
30    
31  #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 6) || GTKMM_MAJOR_VERSION > 2  #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 6) || GTKMM_MAJOR_VERSION > 2
32  #define ABOUT_DIALOG  #define ABOUT_DIALOG
# Line 48  Glib::ustring filename_display_basename( Line 50  Glib::ustring filename_display_basename(
50    
51  #include "mainwindow.h"  #include "mainwindow.h"
52    
53  #define _(String) gettext(String)  #include "../../gfx/status_attached.xpm"
54    #include "../../gfx/status_detached.xpm"
55    
56  template<class T> inline std::string ToString(T o) {  template<class T> inline std::string ToString(T o) {
57      std::stringstream ss;      std::stringstream ss;
# Line 56  template<class T> inline std::string ToS Line 59  template<class T> inline std::string ToS
59      return ss.str();      return ss.str();
60  }  }
61    
62  MainWindow::MainWindow()  Table::Table(int x, int y) : Gtk::Table(x, y), rowno(0) {  }
63    
64    void Table::add(BoolEntry& boolentry)
65    {
66        attach(boolentry.widget, 0, 2, rowno, rowno + 1,
67               Gtk::FILL, Gtk::SHRINK);
68        rowno++;
69    }
70    
71    void Table::add(BoolEntryPlus6& boolentry)
72    {
73        attach(boolentry.widget, 0, 2, rowno, rowno + 1,
74               Gtk::FILL, Gtk::SHRINK);
75        rowno++;
76    }
77    
78    void Table::add(LabelWidget& prop)
79    {
80        attach(prop.label, 1, 2, rowno, rowno + 1,
81               Gtk::FILL, Gtk::SHRINK);
82        attach(prop.widget, 2, 3, rowno, rowno + 1,
83               Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK);
84        rowno++;
85    }
86    
87    MainWindow::MainWindow() :
88        dimreg_label(_("Changes apply to:")),
89        dimreg_all_regions(_("all regions")),
90        dimreg_all_dimregs(_("all dimension splits")),
91        dimreg_stereo(_("both channels"))
92  {  {
93  //    set_border_width(5);  //    set_border_width(5);
94  //    set_default_size(400, 200);  //    set_default_size(400, 200);
# Line 86  MainWindow::MainWindow() Line 118  MainWindow::MainWindow()
118      m_TreeViewNotebook.set_size_request(300);      m_TreeViewNotebook.set_size_request(300);
119    
120      m_HPaned.add1(m_TreeViewNotebook);      m_HPaned.add1(m_TreeViewNotebook);
121      m_HPaned.add2(dimreg_edit);      dimreg_hbox.add(dimreg_label);
122        dimreg_hbox.add(dimreg_all_regions);
123        dimreg_hbox.add(dimreg_all_dimregs);
124        dimreg_stereo.set_active();
125        dimreg_hbox.add(dimreg_stereo);
126        dimreg_vbox.add(dimreg_edit);
127        dimreg_vbox.pack_start(dimreg_hbox, Gtk::PACK_SHRINK);
128        m_HPaned.add2(dimreg_vbox);
129    
130    
131      m_TreeViewNotebook.append_page(m_ScrolledWindowSamples, "Samples");      m_TreeViewNotebook.append_page(m_ScrolledWindowSamples, "Samples");
# Line 127  MainWindow::MainWindow() Line 166  MainWindow::MainWindow()
166                           *this, &MainWindow::on_action_quit));                           *this, &MainWindow::on_action_quit));
167      actionGroup->add(Gtk::Action::create("MenuInstrument", _("_Instrument")));      actionGroup->add(Gtk::Action::create("MenuInstrument", _("_Instrument")));
168    
169        actionGroup->add(Gtk::Action::create("MenuView", _("_View")));
170        Glib::RefPtr<Gtk::ToggleAction> toggle_action =
171            Gtk::ToggleAction::create("Statusbar", _("_Statusbar"));
172        toggle_action->set_active(true);
173        actionGroup->add(toggle_action,
174                         sigc::mem_fun(
175                             *this, &MainWindow::on_action_view_status_bar));
176    
177      action = Gtk::Action::create("MenuHelp", Gtk::Stock::HELP);      action = Gtk::Action::create("MenuHelp", Gtk::Stock::HELP);
178      actionGroup->add(Gtk::Action::create("MenuHelp",      actionGroup->add(Gtk::Action::create("MenuHelp",
179                                           action->property_label()));                                           action->property_label()));
# Line 182  MainWindow::MainWindow() Line 229  MainWindow::MainWindow()
229          "    </menu>"          "    </menu>"
230          "    <menu action='MenuInstrument'>"          "    <menu action='MenuInstrument'>"
231          "    </menu>"          "    </menu>"
232            "    <menu action='MenuView'>"
233            "      <menuitem action='Statusbar'/>"
234            "    </menu>"
235  #ifdef ABOUT_DIALOG  #ifdef ABOUT_DIALOG
236          "    <menu action='MenuHelp'>"          "    <menu action='MenuHelp'>"
237          "      <menuitem action='About'/>"          "      <menuitem action='About'/>"
# Line 210  MainWindow::MainWindow() Line 260  MainWindow::MainWindow()
260      m_VBox.pack_start(*menuBar, Gtk::PACK_SHRINK);      m_VBox.pack_start(*menuBar, Gtk::PACK_SHRINK);
261      m_VBox.pack_start(m_HPaned);      m_VBox.pack_start(m_HPaned);
262      m_VBox.pack_start(m_RegionChooser, Gtk::PACK_SHRINK);      m_VBox.pack_start(m_RegionChooser, Gtk::PACK_SHRINK);
263        m_VBox.pack_start(m_RegionChooser.m_VirtKeybPropsBox, Gtk::PACK_SHRINK);
264      m_VBox.pack_start(m_DimRegionChooser, Gtk::PACK_SHRINK);      m_VBox.pack_start(m_DimRegionChooser, Gtk::PACK_SHRINK);
265        m_VBox.pack_start(m_StatusBar, Gtk::PACK_SHRINK);
266    
267        // Status Bar:
268        m_StatusBar.pack_start(m_AttachedStateLabel, Gtk::PACK_SHRINK);
269        m_StatusBar.pack_start(m_AttachedStateImage, Gtk::PACK_SHRINK);
270        m_StatusBar.show();
271    
272      m_RegionChooser.signal_region_selected().connect(      m_RegionChooser.signal_region_selected().connect(
273          sigc::mem_fun(*this, &MainWindow::region_changed) );          sigc::mem_fun(*this, &MainWindow::region_changed) );
# Line 257  MainWindow::MainWindow() Line 314  MainWindow::MainWindow()
314          sigc::mem_fun(*this, &MainWindow::on_sample_label_drop_drag_data_received)          sigc::mem_fun(*this, &MainWindow::on_sample_label_drop_drag_data_received)
315      );      );
316      dimreg_edit.signal_dimreg_changed().connect(      dimreg_edit.signal_dimreg_changed().connect(
317          sigc::mem_fun(*this, &MainWindow::file_changed));          sigc::hide(sigc::mem_fun(*this, &MainWindow::file_changed)));
318      m_RegionChooser.signal_instrument_changed().connect(      m_RegionChooser.signal_instrument_changed().connect(
319          sigc::mem_fun(*this, &MainWindow::file_changed));          sigc::mem_fun(*this, &MainWindow::file_changed));
320      m_DimRegionChooser.signal_region_changed().connect(      m_DimRegionChooser.signal_region_changed().connect(
321          sigc::mem_fun(*this, &MainWindow::file_changed));          sigc::mem_fun(*this, &MainWindow::file_changed));
322      instrumentProps.signal_instrument_changed().connect(      instrumentProps.signal_instrument_changed().connect(
323          sigc::mem_fun(*this, &MainWindow::file_changed));          sigc::mem_fun(*this, &MainWindow::file_changed));
324        propDialog.signal_info_changed().connect(
325            sigc::mem_fun(*this, &MainWindow::file_changed));
326    
327        dimreg_edit.signal_dimreg_to_be_changed().connect(
328            dimreg_to_be_changed_signal.make_slot());
329        dimreg_edit.signal_dimreg_changed().connect(
330            dimreg_changed_signal.make_slot());
331        dimreg_edit.signal_sample_ref_changed().connect(
332            sample_ref_changed_signal.make_slot());
333    
334        m_RegionChooser.signal_instrument_struct_to_be_changed().connect(
335            sigc::hide(
336                sigc::bind(
337                    file_structure_to_be_changed_signal.make_slot(),
338                    sigc::ref(this->file)
339                )
340            )
341        );
342        m_RegionChooser.signal_instrument_struct_changed().connect(
343            sigc::hide(
344                sigc::bind(
345                    file_structure_changed_signal.make_slot(),
346                    sigc::ref(this->file)
347                )
348            )
349        );
350        m_RegionChooser.signal_region_to_be_changed().connect(
351            region_to_be_changed_signal.make_slot());
352        m_RegionChooser.signal_region_changed_signal().connect(
353            region_changed_signal.make_slot());
354    
355        note_on_signal.connect(
356            sigc::mem_fun(m_RegionChooser, &RegionChooser::on_note_on_event));
357        note_off_signal.connect(
358            sigc::mem_fun(m_RegionChooser, &RegionChooser::on_note_off_event));
359    
360        dimreg_all_regions.signal_toggled().connect(
361            sigc::mem_fun(*this, &MainWindow::update_dimregs));
362        dimreg_all_dimregs.signal_toggled().connect(
363            sigc::mem_fun(*this, &MainWindow::dimreg_all_dimregs_toggled));
364        dimreg_stereo.signal_toggled().connect(
365            sigc::mem_fun(*this, &MainWindow::update_dimregs));
366    
367      file = 0;      file = 0;
368      file_is_changed = false;      file_is_changed = false;
369        set_file_is_shared(false);
370    
371      show_all_children();      show_all_children();
372    
# Line 279  MainWindow::~MainWindow() Line 380  MainWindow::~MainWindow()
380    
381  bool MainWindow::on_delete_event(GdkEventAny* event)  bool MainWindow::on_delete_event(GdkEventAny* event)
382  {  {
383      return file_is_changed && !close_confirmation_dialog();      return !file_is_shared && file_is_changed && !close_confirmation_dialog();
384  }  }
385    
386  void MainWindow::on_action_quit()  void MainWindow::on_action_quit()
387  {  {
388      if (file_is_changed && !close_confirmation_dialog()) return;      if (!file_is_shared && file_is_changed && !close_confirmation_dialog()) return;
389      hide();      hide();
390  }  }
391    
# Line 293  void MainWindow::region_changed() Line 394  void MainWindow::region_changed()
394      m_DimRegionChooser.set_region(m_RegionChooser.get_region());      m_DimRegionChooser.set_region(m_RegionChooser.get_region());
395  }  }
396    
397  void MainWindow::dimreg_changed()  gig::Instrument* MainWindow::get_instrument()
 {  
     dimreg_edit.set_dim_region(m_DimRegionChooser.get_dimregion());  
 }  
   
 void MainWindow::on_sel_change()  
398  {  {
399        gig::Instrument* instrument = 0;
400      Glib::RefPtr<Gtk::TreeSelection> tree_sel_ref = m_TreeView.get_selection();      Glib::RefPtr<Gtk::TreeSelection> tree_sel_ref = m_TreeView.get_selection();
401    
402      Gtk::TreeModel::iterator it = tree_sel_ref->get_selected();      Gtk::TreeModel::iterator it = tree_sel_ref->get_selected();
403      if (it) {      if (it) {
404          Gtk::TreeModel::Row row = *it;          Gtk::TreeModel::Row row = *it;
405          std::cout << row[m_Columns.m_col_name] << std::endl;          instrument = row[m_Columns.m_col_instr];
406        }
407        return instrument;
408    }
409    
410    void MainWindow::add_region_to_dimregs(gig::Region* region, bool stereo, bool all_dimregs)
411    {
412        if (all_dimregs) {
413            for (int i = 0 ; i < region->DimensionRegions ; i++) {
414                if (region->pDimensionRegions[i]) {
415                    dimreg_edit.dimregs.insert(region->pDimensionRegions[i]);
416                }
417            }
418        } else {
419            m_DimRegionChooser.get_dimregions(region, stereo, dimreg_edit.dimregs);
420        }
421    }
422    
423    void MainWindow::update_dimregs()
424    {
425        dimreg_edit.dimregs.clear();
426        bool all_regions = dimreg_all_regions.get_active();
427        bool stereo = dimreg_stereo.get_active();
428        bool all_dimregs = dimreg_all_dimregs.get_active();
429    
430          m_RegionChooser.set_instrument(row[m_Columns.m_col_instr]);      if (all_regions) {
431            gig::Instrument* instrument = get_instrument();
432            if (instrument) {
433                for (gig::Region* region = instrument->GetFirstRegion() ;
434                     region ;
435                     region = instrument->GetNextRegion()) {
436                    add_region_to_dimregs(region, stereo, all_dimregs);
437                }
438            }
439      } else {      } else {
440          m_RegionChooser.set_instrument(0);          gig::Region* region = m_RegionChooser.get_region();
441            if (region) {
442                add_region_to_dimregs(region, stereo, all_dimregs);
443            }
444      }      }
445  }  }
446    
447    void MainWindow::dimreg_all_dimregs_toggled()
448    {
449        dimreg_stereo.set_sensitive(!dimreg_all_dimregs.get_active());
450        update_dimregs();
451    }
452    
453    void MainWindow::dimreg_changed()
454    {
455        update_dimregs();
456        dimreg_edit.set_dim_region(m_DimRegionChooser.get_dimregion());
457    }
458    
459    void MainWindow::on_sel_change()
460    {
461        m_RegionChooser.set_instrument(get_instrument());
462    }
463    
464  void loader_progress_callback(gig::progress_t* progress)  void loader_progress_callback(gig::progress_t* progress)
465  {  {
466      Loader* loader = static_cast<Loader*>(progress->custom);      Loader* loader = static_cast<Loader*>(progress->custom);
# Line 398  void MainWindow::__clear() { Line 546  void MainWindow::__clear() {
546      m_refTreeModel->clear();      m_refTreeModel->clear();
547      m_refSamplesTreeModel->clear();      m_refSamplesTreeModel->clear();
548      // free libgig's gig::File instance      // free libgig's gig::File instance
549      if (file) {      if (file && !file_is_shared) delete file;
550          delete file;      file = NULL;
551          file = NULL;      set_file_is_shared(false);
     }  
552  }  }
553    
554  void MainWindow::on_action_file_new()  void MainWindow::on_action_file_new()
555  {  {
556      if (file_is_changed && !close_confirmation_dialog()) return;      if (!file_is_shared && file_is_changed && !close_confirmation_dialog()) return;
557    
558        if (file_is_shared && !leaving_shared_mode_dialog()) return;
559    
560      // clear all GUI elements      // clear all GUI elements
561      __clear();      __clear();
# Line 438  bool MainWindow::close_confirmation_dial Line 587  bool MainWindow::close_confirmation_dial
587      return response != Gtk::RESPONSE_CANCEL;      return response != Gtk::RESPONSE_CANCEL;
588  }  }
589    
590    bool MainWindow::leaving_shared_mode_dialog() {
591        Glib::ustring msg = _("Detach from sampler and proceed working stand-alone?");
592        Gtk::MessageDialog dialog(*this, msg, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_NONE);
593    #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 6) || GTKMM_MAJOR_VERSION > 2
594        dialog.set_secondary_text(
595            _("If you proceed to work on another instrument file, it won't be "
596              "used by the sampler until you tell the sampler explicitly to "
597              "load it.")
598       );
599    #endif
600        dialog.add_button(_("_Yes, Detach"), Gtk::RESPONSE_YES);
601        dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
602        dialog.set_default_response(Gtk::RESPONSE_CANCEL);
603        int response = dialog.run();
604        dialog.hide();
605        return response == Gtk::RESPONSE_YES;
606    }
607    
608  void MainWindow::on_action_file_open()  void MainWindow::on_action_file_open()
609  {  {
610      if (file_is_changed && !close_confirmation_dialog()) return;      if (!file_is_shared && file_is_changed && !close_confirmation_dialog()) return;
611    
612        if (file_is_shared && !leaving_shared_mode_dialog()) return;
613    
614      Gtk::FileChooserDialog dialog(*this, _("Open file"));      Gtk::FileChooserDialog dialog(*this, _("Open file"));
615      dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);      dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
# Line 481  void MainWindow::load_instrument(gig::In Line 650  void MainWindow::load_instrument(gig::In
650          msg.run();          msg.run();
651          Gtk::Main::quit();          Gtk::Main::quit();
652      }      }
653        // clear all GUI elements
654        __clear();
655        // load the instrument
656      gig::File* pFile = (gig::File*) instr->GetParent();      gig::File* pFile = (gig::File*) instr->GetParent();
657      load_gig(pFile, 0 /*file name*/);      load_gig(pFile, 0 /*file name*/, true /*shared instrument*/);
658      //TODO: automatically select the given instrument      //TODO: automatically select the given instrument
659  }  }
660    
# Line 531  bool MainWindow::check_if_savable() Line 703  bool MainWindow::check_if_savable()
703  bool MainWindow::file_save()  bool MainWindow::file_save()
704  {  {
705      if (!check_if_savable()) return false;      if (!check_if_savable()) return false;
706      if (!file_has_name) return file_save_as();      if (!file_is_shared && !file_has_name) return file_save_as();
707    
708      std::cout << "Saving file\n" << std::flush;      std::cout << "Saving file\n" << std::flush;
709        file_structure_to_be_changed_signal.emit(this->file);
710      try {      try {
711          file->Save();          file->Save();
712          if (file_is_changed) {          if (file_is_changed) {
# Line 541  bool MainWindow::file_save() Line 714  bool MainWindow::file_save()
714              file_is_changed = false;              file_is_changed = false;
715          }          }
716      } catch (RIFF::Exception e) {      } catch (RIFF::Exception e) {
717          Glib::ustring txt = "Could not save file: " + e.Message;          file_structure_changed_signal.emit(this->file);
718            Glib::ustring txt = _("Could not save file: ") + e.Message;
719          Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);          Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
720          msg.run();          msg.run();
721          return false;          return false;
722      }      }
723      std::cout << "Saving file done\n" << std::flush;      std::cout << "Saving file done\n" << std::flush;
724      __import_queued_samples();      __import_queued_samples();
725        file_structure_changed_signal.emit(this->file);
726      return true;      return true;
727  }  }
728    
# Line 580  bool MainWindow::file_save_as() Line 755  bool MainWindow::file_save_as()
755      dialog.set_current_name(Glib::filename_display_basename(filename));      dialog.set_current_name(Glib::filename_display_basename(filename));
756    
757      if (dialog.run() == Gtk::RESPONSE_OK) {      if (dialog.run() == Gtk::RESPONSE_OK) {
758            file_structure_to_be_changed_signal.emit(this->file);
759          try {          try {
760              std::string filename = dialog.get_filename();              std::string filename = dialog.get_filename();
761              if (!Glib::str_has_suffix(filename, ".gig")) {              if (!Glib::str_has_suffix(filename, ".gig")) {
# Line 593  bool MainWindow::file_save_as() Line 769  bool MainWindow::file_save_as()
769              file_has_name = true;              file_has_name = true;
770              file_is_changed = false;              file_is_changed = false;
771          } catch (RIFF::Exception e) {          } catch (RIFF::Exception e) {
772              Glib::ustring txt = "Could not save file: " + e.Message;              file_structure_changed_signal.emit(this->file);
773                Glib::ustring txt = _("Could not save file: ") + e.Message;
774              Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);              Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
775              msg.run();              msg.run();
776              return false;              return false;
777          }          }
778          __import_queued_samples();          __import_queued_samples();
779            file_structure_changed_signal.emit(this->file);
780          return true;          return true;
781      }      }
782      return false;      return false;
# Line 689  void MainWindow::__import_queued_samples Line 867  void MainWindow::__import_queued_samples
867      }      }
868      // show error message box when some sample(s) could not be imported      // show error message box when some sample(s) could not be imported
869      if (error_files.size()) {      if (error_files.size()) {
870          Glib::ustring txt = "Could not import the following sample(s):\n" + error_files;          Glib::ustring txt = _("Could not import the following sample(s):\n") + error_files;
871          Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);          Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
872          msg.run();          msg.run();
873      }      }
# Line 706  void MainWindow::on_action_help_about() Line 884  void MainWindow::on_action_help_about()
884  #ifdef ABOUT_DIALOG  #ifdef ABOUT_DIALOG
885      Gtk::AboutDialog dialog;      Gtk::AboutDialog dialog;
886      dialog.set_version(VERSION);      dialog.set_version(VERSION);
887        dialog.set_copyright("Copyright (C) 2006,2007 Andreas Persson");
888        dialog.set_comments(
889            "Released under the GNU General Public License.\n"
890            "\n"
891            "Please notice that this is still a very young instrument editor. "
892            "So better backup your Gigasampler files before editing them with "
893            "this application.\n"
894            "\n"
895            "Please report bugs to: http://bugs.linuxsampler.org"
896        );
897        dialog.set_website("http://www.linuxsampler.org");
898        dialog.set_website_label("http://www.linuxsampler.org");
899      dialog.run();      dialog.run();
900  #endif  #endif
901  }  }
902    
903  PropDialog::PropDialog()  PropDialog::PropDialog()
904      : table(2,1)      : table(2,1),
905          eName("Name"),
906          eCreationDate("Creation date"),
907          eComments("Comments"),
908          eProduct("Product"),
909          eCopyright("Copyright"),
910          eArtists("Artists"),
911          eGenre("Genre"),
912          eKeywords("Keywords"),
913          eEngineer("Engineer"),
914          eTechnician("Technician"),
915          eSoftware("Software"),
916          eMedium("Medium"),
917          eSource("Source"),
918          eSourceForm("Source form"),
919          eCommissioned("Commissioned"),
920          eSubject("Subject"),
921          quitButton(Gtk::Stock::CLOSE),
922          update_model(0)
923  {  {
924        set_title("File Properties");
925        eName.set_width_chars(50);
926    
927        connect(eName, &DLS::Info::Name);
928        connect(eCreationDate, &DLS::Info::CreationDate);
929        connect(eComments, &DLS::Info::Comments);
930        connect(eProduct, &DLS::Info::Product);
931        connect(eCopyright, &DLS::Info::Copyright);
932        connect(eArtists, &DLS::Info::Artists);
933        connect(eGenre, &DLS::Info::Genre);
934        connect(eKeywords, &DLS::Info::Keywords);
935        connect(eEngineer, &DLS::Info::Engineer);
936        connect(eTechnician, &DLS::Info::Technician);
937        connect(eSoftware, &DLS::Info::Software);
938        connect(eMedium, &DLS::Info::Medium);
939        connect(eSource, &DLS::Info::Source);
940        connect(eSourceForm, &DLS::Info::SourceForm);
941        connect(eCommissioned, &DLS::Info::Commissioned);
942        connect(eSubject, &DLS::Info::Subject);
943    
944        table.add(eName);
945        table.add(eCreationDate);
946        table.add(eComments);
947        table.add(eProduct);
948        table.add(eCopyright);
949        table.add(eArtists);
950        table.add(eGenre);
951        table.add(eKeywords);
952        table.add(eEngineer);
953        table.add(eTechnician);
954        table.add(eSoftware);
955        table.add(eMedium);
956        table.add(eSource);
957        table.add(eSourceForm);
958        table.add(eCommissioned);
959        table.add(eSubject);
960    
961      table.set_col_spacings(5);      table.set_col_spacings(5);
962      const char* propLabels[] = {      add(vbox);
963          "Name:",      table.set_border_width(5);
964          "CreationDate:",      vbox.add(table);
965          "Comments:", // TODO: multiline      vbox.pack_start(buttonBox, Gtk::PACK_SHRINK);
966          "Product:",      buttonBox.set_layout(Gtk::BUTTONBOX_END);
967          "Copyright:",      buttonBox.set_border_width(5);
968          "Artists:",      buttonBox.show();
969          "Genre:",      buttonBox.pack_start(quitButton);
970          "Keywords:",      quitButton.set_flags(Gtk::CAN_DEFAULT);
971          "Engineer:",      quitButton.grab_focus();
972          "Technician:",      quitButton.signal_clicked().connect(
973          "Software:", // TODO: readonly          sigc::mem_fun(*this, &PropDialog::hide));
         "Medium:",  
         "Source:",  
         "SourceForm:",  
         "Commissioned:",  
         "Subject:"  
     };  
     for (int i = 0 ; i < sizeof(propLabels) / sizeof(char*) ; i++) {  
         label[i].set_text(propLabels[i]);  
         label[i].set_alignment(Gtk::ALIGN_LEFT);  
         table.attach(label[i], 0, 1, i, i + 1, Gtk::FILL, Gtk::SHRINK);  
         table.attach(entry[i], 1, 2, i, i + 1, Gtk::FILL | Gtk::EXPAND,  
                      Gtk::SHRINK);  
     }  
974    
975      add(table);      quitButton.show();
976      // add_button(Gtk::Stock::CANCEL, 0);      vbox.show();
     // add_button(Gtk::Stock::OK, 1);  
977      show_all_children();      show_all_children();
978  }  }
979    
980  void PropDialog::set_info(DLS::Info* info)  void PropDialog::set_info(DLS::Info* info)
981  {  {
982      entry[0].set_text(info->Name);      this->info = info;
983      entry[1].set_text(info->CreationDate);      update_model++;
984      entry[2].set_text(Glib::convert(info->Comments, "UTF-8", "ISO-8859-1"));      eName.set_value(info->Name);
985      entry[3].set_text(info->Product);      eCreationDate.set_value(info->CreationDate);
986      entry[4].set_text(info->Copyright);      eComments.set_value(info->Comments);
987      entry[5].set_text(info->Artists);      eProduct.set_value(info->Product);
988      entry[6].set_text(info->Genre);      eCopyright.set_value(info->Copyright);
989      entry[7].set_text(info->Keywords);      eArtists.set_value(info->Artists);
990      entry[8].set_text(info->Engineer);      eGenre.set_value(info->Genre);
991      entry[9].set_text(info->Technician);      eKeywords.set_value(info->Keywords);
992      entry[10].set_text(info->Software);      eEngineer.set_value(info->Engineer);
993      entry[11].set_text(info->Medium);      eTechnician.set_value(info->Technician);
994      entry[12].set_text(info->Source);      eSoftware.set_value(info->Software);
995      entry[13].set_text(info->SourceForm);      eMedium.set_value(info->Medium);
996      entry[14].set_text(info->Commissioned);      eSource.set_value(info->Source);
997      entry[15].set_text(info->Subject);      eSourceForm.set_value(info->SourceForm);
998        eCommissioned.set_value(info->Commissioned);
999        eSubject.set_value(info->Subject);
1000        update_model--;
1001  }  }
1002    
1003  void InstrumentProps::add_prop(BoolEntry& boolentry)  sigc::signal<void>& PropDialog::signal_info_changed()
1004  {  {
1005      table.attach(boolentry.widget, 0, 2, rowno, rowno + 1,      return info_changed;
                  Gtk::FILL, Gtk::SHRINK);  
     rowno++;  
     boolentry.signal_changed_by_user().connect(instrument_changed.make_slot());  
1006  }  }
1007    
1008  void InstrumentProps::add_prop(BoolEntryPlus6& boolentry)  void InstrumentProps::set_IsDrum(bool value)
1009  {  {
1010      table.attach(boolentry.widget, 0, 2, rowno, rowno + 1,      instrument->IsDrum = value;
                  Gtk::FILL, Gtk::SHRINK);  
     rowno++;  
     boolentry.signal_changed_by_user().connect(instrument_changed.make_slot());  
1011  }  }
1012    
1013  void InstrumentProps::add_prop(LabelWidget& prop)  void InstrumentProps::set_MIDIBank(uint16_t value)
1014  {  {
1015      table.attach(prop.label, 0, 1, rowno, rowno + 1,      instrument->MIDIBank = value;
1016                   Gtk::FILL, Gtk::SHRINK);  }
1017      table.attach(prop.widget, 1, 2, rowno, rowno + 1,  
1018                   Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);  void InstrumentProps::set_MIDIProgram(uint32_t value)
1019      rowno++;  {
1020      prop.signal_changed_by_user().connect(instrument_changed.make_slot());      instrument->MIDIProgram = value;
1021    }
1022    
1023    void InstrumentProps::set_DimensionKeyRange_low(uint8_t value)
1024    {
1025        instrument->DimensionKeyRange.low = value;
1026        if (value > instrument->DimensionKeyRange.high) {
1027            eDimensionKeyRangeHigh.set_value(value);
1028        }
1029    }
1030    
1031    void InstrumentProps::set_DimensionKeyRange_high(uint8_t value)
1032    {
1033        instrument->DimensionKeyRange.high = value;
1034        if (value < instrument->DimensionKeyRange.low) {
1035            eDimensionKeyRangeLow.set_value(value);
1036        }
1037  }  }
1038    
1039  InstrumentProps::InstrumentProps()  InstrumentProps::InstrumentProps()
# Line 805  InstrumentProps::InstrumentProps() Line 1049  InstrumentProps::InstrumentProps()
1049        eFineTune("Fine tune", -8400, 8400),        eFineTune("Fine tune", -8400, 8400),
1050        ePitchbendRange("Pitchbend range", 0, 12),        ePitchbendRange("Pitchbend range", 0, 12),
1051        ePianoReleaseMode("Piano release mode"),        ePianoReleaseMode("Piano release mode"),
1052        eDimensionKeyRangeLow("Dimension key range low"),        eDimensionKeyRangeLow("Keyswitching range low"),
1053        eDimensionKeyRangeHigh("Dimension key range high")        eDimensionKeyRangeHigh("Keyswitching range high"),
1054  {        update_model(0)
1055      set_title("Instrument properties");  {
1056        set_title("Instrument Properties");
1057    
1058        eDimensionKeyRangeLow.set_tip(
1059            _("start of the keyboard area which should switch the "
1060              "\"keyswitching\" dimension")
1061        );
1062        eDimensionKeyRangeHigh.set_tip(
1063            _("end of the keyboard area which should switch the "
1064              "\"keyswitching\" dimension")
1065        );
1066    
1067        connect(eIsDrum, &InstrumentProps::set_IsDrum);
1068        connect(eMIDIBank, &InstrumentProps::set_MIDIBank);
1069        connect(eMIDIProgram, &InstrumentProps::set_MIDIProgram);
1070        connect(eAttenuation, &gig::Instrument::Attenuation);
1071        connect(eGainPlus6, &gig::Instrument::Attenuation);
1072        connect(eEffectSend, &gig::Instrument::EffectSend);
1073        connect(eFineTune, &gig::Instrument::FineTune);
1074        connect(ePitchbendRange, &gig::Instrument::PitchbendRange);
1075        connect(ePianoReleaseMode, &gig::Instrument::PianoReleaseMode);
1076        connect(eDimensionKeyRangeLow,
1077                &InstrumentProps::set_DimensionKeyRange_low);
1078        connect(eDimensionKeyRangeHigh,
1079                &InstrumentProps::set_DimensionKeyRange_high);
1080    
     rowno = 0;  
1081      table.set_col_spacings(5);      table.set_col_spacings(5);
1082    
1083      add_prop(eName);      table.add(eName);
1084      add_prop(eIsDrum);      table.add(eIsDrum);
1085      add_prop(eMIDIBank);      table.add(eMIDIBank);
1086      add_prop(eMIDIProgram);      table.add(eMIDIProgram);
1087      add_prop(eAttenuation);      table.add(eAttenuation);
1088      add_prop(eGainPlus6);      table.add(eGainPlus6);
1089      add_prop(eEffectSend);      table.add(eEffectSend);
1090      add_prop(eFineTune);      table.add(eFineTune);
1091      add_prop(ePitchbendRange);      table.add(ePitchbendRange);
1092      add_prop(ePianoReleaseMode);      table.add(ePianoReleaseMode);
1093      add_prop(eDimensionKeyRangeLow);      table.add(eDimensionKeyRangeLow);
1094      add_prop(eDimensionKeyRangeHigh);      table.add(eDimensionKeyRangeHigh);
   
     eDimensionKeyRangeLow.signal_changed_by_user().connect(  
         sigc::mem_fun(*this, &InstrumentProps::key_range_low_changed));  
     eDimensionKeyRangeHigh.signal_changed_by_user().connect(  
         sigc::mem_fun(*this, &InstrumentProps::key_range_high_changed));  
1095    
1096      add(vbox);      add(vbox);
1097      table.set_border_width(5);      table.set_border_width(5);
# Line 853  InstrumentProps::InstrumentProps() Line 1115  InstrumentProps::InstrumentProps()
1115    
1116  void InstrumentProps::set_instrument(gig::Instrument* instrument)  void InstrumentProps::set_instrument(gig::Instrument* instrument)
1117  {  {
1118      eName.set_ptr(&instrument->pInfo->Name);      this->instrument = instrument;
     eIsDrum.set_ptr(&instrument->IsDrum);  
     eMIDIBank.set_ptr(&instrument->MIDIBank);  
     eMIDIProgram.set_ptr(&instrument->MIDIProgram);  
     eAttenuation.set_ptr(&instrument->Attenuation);  
     eGainPlus6.set_ptr(&instrument->Attenuation);  
     eEffectSend.set_ptr(&instrument->EffectSend);  
     eFineTune.set_ptr(&instrument->FineTune);  
     ePitchbendRange.set_ptr(&instrument->PitchbendRange);  
     ePianoReleaseMode.set_ptr(&instrument->PianoReleaseMode);  
     eDimensionKeyRangeLow.set_ptr(0);  
     eDimensionKeyRangeHigh.set_ptr(0);  
     eDimensionKeyRangeLow.set_ptr(&instrument->DimensionKeyRange.low);  
     eDimensionKeyRangeHigh.set_ptr(&instrument->DimensionKeyRange.high);  
 }  
1119    
1120  void InstrumentProps::key_range_low_changed()      update_model++;
1121  {      eName.set_value(instrument->pInfo->Name);
1122      double l = eDimensionKeyRangeLow.get_value();      eIsDrum.set_value(instrument->IsDrum);
1123      double h = eDimensionKeyRangeHigh.get_value();      eMIDIBank.set_value(instrument->MIDIBank);
1124      if (h < l) eDimensionKeyRangeHigh.set_value(l);      eMIDIProgram.set_value(instrument->MIDIProgram);
1125  }      eAttenuation.set_value(instrument->Attenuation);
1126        eGainPlus6.set_value(instrument->Attenuation);
1127  void InstrumentProps::key_range_high_changed()      eEffectSend.set_value(instrument->EffectSend);
1128  {      eFineTune.set_value(instrument->FineTune);
1129      double l = eDimensionKeyRangeLow.get_value();      ePitchbendRange.set_value(instrument->PitchbendRange);
1130      double h = eDimensionKeyRangeHigh.get_value();      ePianoReleaseMode.set_value(instrument->PianoReleaseMode);
1131      if (h < l) eDimensionKeyRangeLow.set_value(h);      eDimensionKeyRangeLow.set_value(instrument->DimensionKeyRange.low);
1132        eDimensionKeyRangeHigh.set_value(instrument->DimensionKeyRange.high);
1133        update_model--;
1134  }  }
1135    
1136  sigc::signal<void> InstrumentProps::signal_instrument_changed()  sigc::signal<void>& InstrumentProps::signal_instrument_changed()
1137  {  {
1138      return instrument_changed;      return instrument_changed;
1139  }  }
# Line 896  void MainWindow::file_changed() Line 1146  void MainWindow::file_changed()
1146      }      }
1147  }  }
1148    
1149  void MainWindow::load_gig(gig::File* gig, const char* filename)  void MainWindow::load_gig(gig::File* gig, const char* filename, bool isSharedInstrument)
1150  {  {
1151      file = 0;      file = 0;
1152        set_file_is_shared(isSharedInstrument);
1153    
1154      this->filename = filename ? filename : _("Unsaved Gig File");      this->filename = filename ? filename : _("Unsaved Gig File");
1155      set_title(Glib::filename_display_basename(this->filename));      set_title(Glib::filename_display_basename(this->filename));
# Line 961  void MainWindow::load_gig(gig::File* gig Line 1212  void MainWindow::load_gig(gig::File* gig
1212    
1213  void MainWindow::show_instr_props()  void MainWindow::show_instr_props()
1214  {  {
1215      Glib::RefPtr<Gtk::TreeSelection> tree_sel_ref = m_TreeView.get_selection();      gig::Instrument* instrument = get_instrument();
1216      Gtk::TreeModel::iterator it = tree_sel_ref->get_selected();      if (instrument)
     if (it)  
1217      {      {
1218          Gtk::TreeModel::Row row = *it;          instrumentProps.set_instrument(instrument);
1219          if (row[m_Columns.m_col_instr])          instrumentProps.show();
1220          {          instrumentProps.deiconify();
1221              instrumentProps.set_instrument(row[m_Columns.m_col_instr]);      }
1222              instrumentProps.show();  }
1223              instrumentProps.deiconify();  
1224          }  void MainWindow::on_action_view_status_bar() {
1225        Gtk::CheckMenuItem* item =
1226            dynamic_cast<Gtk::CheckMenuItem*>(uiManager->get_widget("/MenuBar/MenuView/Statusbar"));
1227        if (!item) {
1228            std::cerr << "/MenuBar/MenuView/Statusbar == NULL\n";
1229            return;
1230      }      }
1231        if (item->get_active()) m_StatusBar.show();
1232        else                    m_StatusBar.hide();
1233  }  }
1234    
1235  void MainWindow::on_button_release(GdkEventButton* button)  void MainWindow::on_button_release(GdkEventButton* button)
# Line 1032  void MainWindow::on_action_add_instrumen Line 1289  void MainWindow::on_action_add_instrumen
1289    
1290  void MainWindow::on_action_remove_instrument() {  void MainWindow::on_action_remove_instrument() {
1291      if (!file) return;      if (!file) return;
1292        if (file_is_shared) {
1293            Gtk::MessageDialog msg(
1294                *this,
1295                 _("You cannot delete an instrument from this file, since it's "
1296                   "currently used by the sampler."),
1297                 false, Gtk::MESSAGE_INFO
1298            );
1299            msg.run();
1300            return;
1301        }
1302    
1303      Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeView.get_selection();      Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeView.get_selection();
1304      Gtk::TreeModel::iterator it = sel->get_selected();      Gtk::TreeModel::iterator it = sel->get_selected();
1305      if (it) {      if (it) {
# Line 1218  void MainWindow::on_action_add_sample() Line 1486  void MainWindow::on_action_add_sample()
1486          }          }
1487          // show error message box when some file(s) could not be opened / added          // show error message box when some file(s) could not be opened / added
1488          if (error_files.size()) {          if (error_files.size()) {
1489              Glib::ustring txt = "Could not add the following sample(s):\n" + error_files;              Glib::ustring txt = _("Could not add the following sample(s):\n") + error_files;
1490              Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);              Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
1491              msg.run();              msg.run();
1492          }          }
# Line 1244  void MainWindow::on_action_remove_sample Line 1512  void MainWindow::on_action_remove_sample
1512                       pSample; pSample = group->GetNextSample()) {                       pSample; pSample = group->GetNextSample()) {
1513                      members.push_back(pSample);                      members.push_back(pSample);
1514                  }                  }
1515                    // notify everybody that we're going to remove these samples
1516                    samples_to_be_removed_signal.emit(members);
1517                  // delete the group in the .gig file including the                  // delete the group in the .gig file including the
1518                  // samples that belong to the group                  // samples that belong to the group
1519                  file->DeleteGroup(group);                  file->DeleteGroup(group);
1520                    // notify that we're done with removal
1521                    samples_removed_signal.emit();
1522                  // if sample(s) were just previously added, remove                  // if sample(s) were just previously added, remove
1523                  // them from the import queue                  // them from the import queue
1524                  for (std::list<gig::Sample*>::iterator member = members.begin();                  for (std::list<gig::Sample*>::iterator member = members.begin();
# Line 1263  void MainWindow::on_action_remove_sample Line 1535  void MainWindow::on_action_remove_sample
1535                  }                  }
1536                  file_changed();                  file_changed();
1537              } else if (sample) {              } else if (sample) {
1538                    // notify everybody that we're going to remove this sample
1539                    std::list<gig::Sample*> lsamples;
1540                    lsamples.push_back(sample);
1541                    samples_to_be_removed_signal.emit(lsamples);
1542                  // remove sample from the .gig file                  // remove sample from the .gig file
1543                  file->DeleteSample(sample);                  file->DeleteSample(sample);
1544                    // notify that we're done with removal
1545                    samples_removed_signal.emit();
1546                  // if sample was just previously added, remove it from                  // if sample was just previously added, remove it from
1547                  // the import queue                  // the import queue
1548                  for (std::list<SampleImportItem>::iterator iter = m_SampleImportQueue.begin();                  for (std::list<SampleImportItem>::iterator iter = m_SampleImportQueue.begin();
# Line 1282  void MainWindow::on_action_remove_sample Line 1560  void MainWindow::on_action_remove_sample
1560              // remove respective row(s) from samples tree view              // remove respective row(s) from samples tree view
1561              m_refSamplesTreeModel->erase(it);              m_refSamplesTreeModel->erase(it);
1562          } catch (RIFF::Exception e) {          } catch (RIFF::Exception e) {
1563                // pretend we're done with removal (i.e. to avoid dead locks)
1564                samples_removed_signal.emit();
1565                // show error message
1566              Gtk::MessageDialog msg(*this, e.Message.c_str(), false, Gtk::MESSAGE_ERROR);              Gtk::MessageDialog msg(*this, e.Message.c_str(), false, Gtk::MESSAGE_ERROR);
1567              msg.run();              msg.run();
1568          }          }
# Line 1328  void MainWindow::on_sample_label_drop_dr Line 1609  void MainWindow::on_sample_label_drop_dr
1609          // drop success          // drop success
1610          context->drop_reply(true, time);          context->drop_reply(true, time);
1611    
1612          // find the samplechannel dimension          //TODO: we should better move most of the following code to DimRegionEdit::set_sample()
1613    
1614            // notify everybody that we're going to alter the region
1615          gig::Region* region = m_RegionChooser.get_region();          gig::Region* region = m_RegionChooser.get_region();
1616            region_to_be_changed_signal.emit(region);
1617    
1618            // find the samplechannel dimension
1619          gig::dimension_def_t* stereo_dimension = 0;          gig::dimension_def_t* stereo_dimension = 0;
1620          for (int i = 0 ; i < region->Dimensions ; i++) {          for (int i = 0 ; i < region->Dimensions ; i++) {
1621              if (region->pDimensionDefinitions[i].dimension ==              if (region->pDimensionDefinitions[i].dimension ==
# Line 1363  void MainWindow::on_sample_label_drop_dr Line 1649  void MainWindow::on_sample_label_drop_dr
1649              for (int i = 0 ; i < region->DimensionRegions ; i++) {              for (int i = 0 ; i < region->DimensionRegions ; i++) {
1650                  gig::DimensionRegion* d = region->pDimensionRegions[i];                  gig::DimensionRegion* d = region->pDimensionRegions[i];
1651                  if (d->pSample && d->pSample->Channels != sample->Channels) {                  if (d->pSample && d->pSample->Channels != sample->Channels) {
1652                      d->pSample = 0;                      gig::Sample* oldref = d->pSample;
1653                        d->pSample = NULL;
1654                        sample_ref_changed_signal.emit(oldref, NULL);
1655                  }                  }
1656              }              }
1657          }          }
1658    
1659            // notify we're done with altering
1660            region_changed_signal.emit(region);
1661    
1662            file_changed();
1663    
1664          return;          return;
1665      }      }
1666      // drop failed      // drop failed
# Line 1407  void MainWindow::instrument_name_changed Line 1700  void MainWindow::instrument_name_changed
1700          file_changed();          file_changed();
1701      }      }
1702  }  }
1703    
1704    void MainWindow::set_file_is_shared(bool b) {
1705        this->file_is_shared = b;
1706    
1707        if (file_is_shared) {
1708            m_AttachedStateLabel.set_label(_("live-mode"));
1709            m_AttachedStateImage.set(
1710                Gdk::Pixbuf::create_from_xpm_data(status_attached_xpm)
1711            );
1712        } else {
1713            m_AttachedStateLabel.set_label(_("stand-alone"));
1714            m_AttachedStateImage.set(
1715                Gdk::Pixbuf::create_from_xpm_data(status_detached_xpm)
1716            );
1717        }
1718    }
1719    
1720    sigc::signal<void, gig::File*>& MainWindow::signal_file_structure_to_be_changed() {
1721        return file_structure_to_be_changed_signal;
1722    }
1723    
1724    sigc::signal<void, gig::File*>& MainWindow::signal_file_structure_changed() {
1725        return file_structure_changed_signal;
1726    }
1727    
1728    sigc::signal<void, std::list<gig::Sample*> >& MainWindow::signal_samples_to_be_removed() {
1729        return samples_to_be_removed_signal;
1730    }
1731    
1732    sigc::signal<void>& MainWindow::signal_samples_removed() {
1733        return samples_removed_signal;
1734    }
1735    
1736    sigc::signal<void, gig::Region*>& MainWindow::signal_region_to_be_changed() {
1737        return region_to_be_changed_signal;
1738    }
1739    
1740    sigc::signal<void, gig::Region*>& MainWindow::signal_region_changed() {
1741        return region_changed_signal;
1742    }
1743    
1744    sigc::signal<void, gig::Sample*/*old*/, gig::Sample*/*new*/>& MainWindow::signal_sample_ref_changed() {
1745        return sample_ref_changed_signal;
1746    }
1747    
1748    sigc::signal<void, gig::DimensionRegion*>& MainWindow::signal_dimreg_to_be_changed() {
1749        return dimreg_to_be_changed_signal;
1750    }
1751    
1752    sigc::signal<void, gig::DimensionRegion*>& MainWindow::signal_dimreg_changed() {
1753        return dimreg_changed_signal;
1754    }
1755    
1756    sigc::signal<void, int/*key*/, int/*velocity*/>& MainWindow::signal_note_on() {
1757        return note_on_signal;
1758    }
1759    
1760    sigc::signal<void, int/*key*/, int/*velocity*/>& MainWindow::signal_note_off() {
1761        return note_off_signal;
1762    }
1763    
1764    sigc::signal<void, int/*key*/, int/*velocity*/>& MainWindow::signal_keyboard_key_hit() {
1765        return m_RegionChooser.signal_keyboard_key_hit();
1766    }
1767    
1768    sigc::signal<void, int/*key*/, int/*velocity*/>& MainWindow::signal_keyboard_key_released() {
1769        return m_RegionChooser.signal_keyboard_key_released();
1770    }

Legend:
Removed from v.1303  
changed lines
  Added in v.1661

  ViewVC Help
Powered by ViewVC