/[svn]/qsampler/trunk/src/qsamplerInstrumentList.cpp
ViewVC logotype

Diff of /qsampler/trunk/src/qsamplerInstrumentList.cpp

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

revision 962 by capela, Sun Dec 3 18:27:23 2006 UTC revision 1463 by capela, Thu Nov 1 13:01:27 2007 UTC
# Line 1  Line 1 
1  // qsamplerInstrumentList.cpp  // qsamplerInstrumentList.cpp
2  //  //
3  /****************************************************************************  /****************************************************************************
4     Copyright (C) 2003-2005, rncbc aka Rui Nuno Capela. All rights reserved.     Copyright (C) 2003-2007, rncbc aka Rui Nuno Capela. All rights reserved.
5    
6     This program is free software; you can redistribute it and/or     This program is free software; you can redistribute it and/or
7     modify it under the terms of the GNU General Public License     modify it under the terms of the GNU General Public License
8     as published by the Free Software Foundation; either version 2     as published by the Free Software Foundation; either version 2
9     of the License, or (at your option) any later version.     of the License, or (at your option) any later version.
10    
11     This program is distributed in the hope that it will be useful,     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.     GNU General Public License for more details.
15    
16     You should have received a copy of the GNU General Public License     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software     along with this program; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19    
20  *****************************************************************************/  *****************************************************************************/
21    
22  #include "qsamplerAbout.h"  #include "qsamplerAbout.h"
23  #include "qsamplerInstrumentList.h"  #include "qsamplerInstrumentList.h"
24    
25  #include "qsamplerInstrument.h"  #include "qsamplerInstrument.h"
26  #include "qsamplerInstrumentForm.h"  #include "qsamplerInstrumentForm.h"
27    
28  #include "qsamplerMainForm.h"  #include "qsamplerOptions.h"
29    #include "qsamplerMainForm.h"
30  #include <qaction.h>  
31  #include <qfileinfo.h>  #include <qapplication.h>
32  #include <qpopupmenu.h>  #include <qmessagebox.h>
33    #include <qeventloop.h>
34    #include <qaction.h>
35  //----------------------------------------------------------------------  #include <qcursor.h>
36  // class qsamplerInstrumentGroup -- custom group list view item.  #include <qfileinfo.h>
37  //  
38    #include <QMenu>
39  // Constructors.  
40  qsamplerInstrumentGroup::qsamplerInstrumentGroup (  // Needed for lroundf()
41          qsamplerInstrumentList *pListView, const QString& sName,  #include <math.h>
42          QListViewItem *pItemAfter )  
43          : QListViewItem(pListView, pItemAfter ? pItemAfter : pListView->lastItem())  #ifndef CONFIG_ROUND
44  {  static inline long lroundf ( float x )
45          QListViewItem::setRenameEnabled(0, true);  {
46            if (x >= 0.0f)
47          QListViewItem::setPixmap(0, QPixmap::fromMimeSource("itemGroup.png"));                  return long(x + 0.5f);
48          QListViewItem::setText(0, sName);          else
49  }                  return long(x - 0.5f);
50    }
51    #endif
52  qsamplerInstrumentGroup::qsamplerInstrumentGroup (  
53          qsamplerInstrumentGroup *pGroupItem, const QString& sName )  using namespace QSampler;
54          : QListViewItem(pGroupItem, sName)  
55  {  //----------------------------------------------------------------------
56          QListViewItem::setRenameEnabled(0, true);  // class qsamplerInstrumentGroup -- custom group list view item.
57    //
58          QListViewItem::setPixmap(0, QPixmap::fromMimeSource("itemGroup.png"));  
59  }  #if 0
60    // Constructors.
61    qsamplerInstrumentGroup::qsamplerInstrumentGroup (
62  // Default destructor.          qsamplerInstrumentList *pListView, const QString& sName,
63  qsamplerInstrumentGroup::~qsamplerInstrumentGroup (void)          QListViewItem *pItemAfter )
64  {          : QListViewItem(pListView, pItemAfter ? pItemAfter : pListView->lastItem())
65  }  {
66            QListViewItem::setRenameEnabled(0, true);
67    
68  // Instance accessors.          QListViewItem::setPixmap(0, QPixmap(":/icons/itemGroup.png"));
69  void qsamplerInstrumentGroup::setName ( const QString& sName )          QListViewItem::setText(0, sName);
70  {  }
71          QListViewItem::setText(0, sName);  
72  }  
73    qsamplerInstrumentGroup::qsamplerInstrumentGroup (
74            qsamplerInstrumentGroup *pGroupItem, const QString& sName )
75  QString qsamplerInstrumentGroup::name (void) const          : QListViewItem(pGroupItem, sName)
76  {  {
77          return QListViewItem::text(0);          QListViewItem::setRenameEnabled(0, true);
78  }  
79            QListViewItem::setPixmap(0, QPixmap(":/icons/itemGroup.png"));
80    }
81  qsamplerInstrumentGroup *qsamplerInstrumentGroup::groupItem (void) const  
82  {  
83          QListViewItem *pParent = QListViewItem::parent();  // Default destructor.
84          while (pParent && pParent->rtti() != qsamplerInstrumentList::Group)  qsamplerInstrumentGroup::~qsamplerInstrumentGroup (void)
85                  pParent = pParent->parent();  {
86          return static_cast<qsamplerInstrumentGroup *> (pParent);  }
87  }  
88    
89    // Instance accessors.
90  qsamplerInstrumentList *qsamplerInstrumentGroup::listView (void) const  void qsamplerInstrumentGroup::setName ( const QString& sName )
91  {  {
92          return static_cast<qsamplerInstrumentList *> (QListViewItem::listView());          QListViewItem::setText(0, sName);
93  }  }
94    
95    
96  // To show up whether its open or not.  QString qsamplerInstrumentGroup::name (void) const
97  void qsamplerInstrumentGroup::setOpen ( bool bOpen )  {
98  {          return QListViewItem::text(0);
99          // Set the proper pixmap of this...  }
100          if (rtti() == qsamplerInstrumentList::Group) {  
101                  QListViewItem::setPixmap(0, QPixmap::fromMimeSource(  
102                          bOpen ? "itemGroupOpen.png" : "itemGroup.png"));  qsamplerInstrumentGroup *qsamplerInstrumentGroup::groupItem (void) const
103          }  {
104          // Open it up...          QListViewItem *pParent = QListViewItem::parent();
105          QListViewItem::setOpen(bOpen);          while (pParent && pParent->rtti() != qsamplerInstrumentList::Group)
106                    pParent = pParent->parent();
107          // All ancestors should be also visible.          return static_cast<qsamplerInstrumentGroup *> (pParent);
108          if (bOpen && QListViewItem::parent())  }
109                  QListViewItem::parent()->setOpen(true);  
110  }  
111    qsamplerInstrumentList *qsamplerInstrumentGroup::listView (void) const
112    {
113  // To virtually distinguish between list view items.          return static_cast<qsamplerInstrumentList *> (QListViewItem::listView());
114  int qsamplerInstrumentGroup::rtti (void) const  }
115  {  
116          return qsamplerInstrumentList::Group;  
117  }  // To show up whether its open or not.
118    void qsamplerInstrumentGroup::setOpen ( bool bOpen )
119    {
120  //----------------------------------------------------------------------          // Set the proper pixmap of this...
121  // class qsamplerInstrumentItem -- custom file list view item.          if (rtti() == qsamplerInstrumentList::Group) {
122  //                  QListViewItem::setPixmap(0, QPixmap(bOpen ?
123                            ":/icons/itemGroupOpen.png" : ":/icons/itemGroup.png"));
124  // Constructors.          }
125  qsamplerInstrumentItem::qsamplerInstrumentItem (          // Open it up...
126          qsamplerInstrumentList *pListView,          QListViewItem::setOpen(bOpen);
127          qsamplerInstrument *pInstrument,  
128          QListViewItem *pItemAfter )          // All ancestors should be also visible.
129          : qsamplerInstrumentGroup(pListView, pInstrument->name(), pItemAfter)          if (bOpen && QListViewItem::parent())
130  {                  QListViewItem::parent()->setOpen(true);
131          m_pInstrument = pInstrument;  }
132    
133          update();  
134  }  // To virtually distinguish between list view items.
135    int qsamplerInstrumentGroup::rtti (void) const
136  qsamplerInstrumentItem::qsamplerInstrumentItem (  {
137          qsamplerInstrumentGroup *pGroupItem,          return qsamplerInstrumentList::Group;
138          qsamplerInstrument *pInstrument )  }
139          : qsamplerInstrumentGroup(pGroupItem, pInstrument->name())  
140  {  
141          m_pInstrument = pInstrument;  //----------------------------------------------------------------------
142    // class qsamplerInstrumentItem -- custom file list view item.
143          update();  //
144  }  
145    // Constructors.
146    qsamplerInstrumentItem::qsamplerInstrumentItem (
147  // Default destructor.          qsamplerInstrumentList *pListView,
148  qsamplerInstrumentItem::~qsamplerInstrumentItem (void)          qsamplerInstrument *pInstrument,
149  {          QListViewItem *pItemAfter )
150          if (m_pInstrument)          : qsamplerInstrumentGroup(pListView, pInstrument->name(), pItemAfter)
151                  delete m_pInstrument;  {
152  }          m_pInstrument = pInstrument;
153    
154            update();
155  // To virtually distinguish between list view items.  }
156  int qsamplerInstrumentItem::rtti (void) const  
157  {  qsamplerInstrumentItem::qsamplerInstrumentItem (
158          return qsamplerInstrumentList::Item;          qsamplerInstrumentGroup *pGroupItem,
159  }          qsamplerInstrument *pInstrument )
160            : qsamplerInstrumentGroup(pGroupItem, pInstrument->name())
161    {
162  // Payload accessor.          m_pInstrument = pInstrument;
163  qsamplerInstrument *qsamplerInstrumentItem::Instrument (void) const  
164  {          update();
165          return m_pInstrument;  }
166  }  
167    
168    // Default destructor.
169  // Item refreshment.  qsamplerInstrumentItem::~qsamplerInstrumentItem (void)
170  void qsamplerInstrumentItem::update (void)  {
171  {          if (m_pInstrument)
172          QListViewItem::setPixmap(0, QPixmap::fromMimeSource("itemFile.png"));                  delete m_pInstrument;
173    }
174          const QString s = "-";  
175          if (m_pInstrument) {  
176                  setText(0, m_pInstrument->name());  // To virtually distinguish between list view items.
177                  setText(1, QString::number(m_pInstrument->bank()));  int qsamplerInstrumentItem::rtti (void) const
178                  setText(2, QString::number(m_pInstrument->program()));  {
179                  setText(3, m_pInstrument->engineName());          return qsamplerInstrumentList::Item;
180                  setText(4, QFileInfo(m_pInstrument->instrumentFile()).fileName());  }
181                  setText(5, QString::number(m_pInstrument->instrumentNr()));  
182                  setText(6, QString::number(int(m_pInstrument->volume() * 100.0f)));  
183                  QString sLoadMode = s;  // Payload accessor.
184                  switch (m_pInstrument->loadMode()) {  qsamplerInstrument *qsamplerInstrumentItem::instrument (void) const
185                  case 3:  {
186                          sLoadMode = QObject::tr("Persistent");          return m_pInstrument;
187                          break;  }
188                  case 2:  
189                          sLoadMode = QObject::tr("On Demand Hold");  
190                          break;  // Item refreshment.
191                  case 1:  void qsamplerInstrumentItem::update (void)
192                          sLoadMode = QObject::tr("On Demand");  {
193                          break;          QListViewItem::setPixmap(0, QPixmap(":/icons/itemFile.png"));
194                  }  
195                  setText(7, sLoadMode);          const QString s = "-";
196          } else {          if (m_pInstrument) {
197                  for (int i = 0; i < listView()->columns(); i++)                  setText(0, m_pInstrument->name());
198                          setText(i, s);                  setText(1, QString::number(m_pInstrument->map()));
199          }                  setText(2, QString::number(m_pInstrument->bank()));
200  }                  setText(3, QString::number(m_pInstrument->prog() + 1));
201                    setText(4, m_pInstrument->engineName());
202                    setText(5, QFileInfo(m_pInstrument->instrumentFile()).fileName());
203  //----------------------------------------------------------------------------                  setText(6, QString::number(m_pInstrument->instrumentNr()));
204  // qsamplerInstrumentList -- MIDI instrument list view.                  setText(7, QString::number(::lroundf(100.0f * m_pInstrument->volume())));
205  //                  QString sLoadMode = s;
206                    switch (m_pInstrument->loadMode()) {
207  // Constructor.                  case 3:
208  qsamplerInstrumentList::qsamplerInstrumentList (                          sLoadMode = QObject::tr("Persistent");
209          QWidget *pParent, const char *pszName )                          break;
210          : QListView(pParent, pszName)                  case 2:
211  {                          sLoadMode = QObject::tr("On Demand Hold");
212  //  QListView::setRootIsDecorated(true);                          break;
213          QListView::setResizeMode(QListView::NoColumn);                  case 1:
214  //      QListView::setAcceptDrops(true);                          sLoadMode = QObject::tr("On Demand");
215          QListView::setDragAutoScroll(true);                          break;
216          QListView::setSizePolicy(                  }
217                  QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));                  setText(8, sLoadMode);
218  //      QListView::setShowToolTips(false);          } else {
219          QListView::setSortColumn(-1);                  for (int i = 0; i < listView()->columns(); i++)
220                            setText(i, s);
221          QListView::addColumn(tr("Name"));          }
222          QListView::addColumn(tr("Bank"));  }
223          QListView::addColumn(tr("Prog"));  
224          QListView::addColumn(tr("Engine"));  
225          QListView::addColumn(tr("File"));  //----------------------------------------------------------------------------
226          QListView::addColumn(tr("Nr"));  // qsamplerInstrumentList -- MIDI instrument list view.
227          QListView::addColumn(tr("Vol"));  //
228          QListView::addColumn(tr("Mode"));  
229    // Constructor.
230          QListView::setColumnAlignment(1, Qt::AlignHCenter);     // Bank  qsamplerInstrumentList::qsamplerInstrumentList (
231          QListView::setColumnAlignment(2, Qt::AlignHCenter);     // Prog          QWidget *pParent, const char *pszName )
232          QListView::setColumnAlignment(5, Qt::AlignHCenter);     // Nr          : QListView(pParent, pszName)
233          QListView::setColumnAlignment(6, Qt::AlignHCenter);     // Vol  {
234            m_iMidiMap = LSCP_MIDI_MAP_ALL;
235          QListView::setColumnWidth(0, 60);       // Name  
236          QListView::setColumnWidth(0, 120);      // File  //  QListView::setRootIsDecorated(true);
237            QListView::setAllColumnsShowFocus(true);
238          m_pNewGroupAction = new QAction(tr("New &Group"), tr("Ctrl+G"), this);          QListView::setResizeMode(QListView::NoColumn);
239          m_pNewItemAction  = new QAction(tr("New &Instrument..."), tr("Ctrl+I"), this);  //      QListView::setAcceptDrops(true);
240          m_pEditItemAction = new QAction(tr("&Edit..."), tr("Ctrl+E"), this);          QListView::setDragAutoScroll(true);
241          m_pRenameAction   = new QAction(tr("&Rename"), tr("Ctrl+R"), this);          QListView::setSizePolicy(
242          m_pDeleteAction   = new QAction(tr("&Delete"), tr("Ctrl+D"), this);                  QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
243          m_pRefreshAction  = new QAction(tr("Re&fresh"), tr("Ctrl+F"), this);  //      QListView::setShowToolTips(false);
244            QListView::setSortColumn(-1);
245          QObject::connect(m_pNewGroupAction,  
246                  SIGNAL(activated()),          QListView::addColumn(tr("Name"));
247                  SLOT(newGroupSlot()));          QListView::addColumn(tr("Map"));
248          QObject::connect(m_pNewItemAction,          QListView::addColumn(tr("Bank"));
249                  SIGNAL(activated()),          QListView::addColumn(tr("Prog"));
250                  SLOT(newItemSlot()));          QListView::addColumn(tr("Engine"));
251          QObject::connect(m_pEditItemAction,          QListView::addColumn(tr("File"));
252                  SIGNAL(activated()),          QListView::addColumn(tr("Nr"));
253                  SLOT(editItemSlot()));          QListView::addColumn(tr("Vol"));
254          QObject::connect(m_pRenameAction,          QListView::addColumn(tr("Mode"));
255                  SIGNAL(activated()),  
256                  SLOT(renameSlot()));          QListView::setColumnAlignment(1, Qt::AlignHCenter);     // Map
257          QObject::connect(m_pDeleteAction,          QListView::setColumnAlignment(2, Qt::AlignHCenter);     // Bank
258                  SIGNAL(activated()),          QListView::setColumnAlignment(3, Qt::AlignHCenter);     // Prog
259                  SLOT(deleteSlot()));          QListView::setColumnAlignment(6, Qt::AlignHCenter);     // Nr
260          QObject::connect(m_pRefreshAction,          QListView::setColumnAlignment(7, Qt::AlignHCenter);     // Vol
261                  SIGNAL(activated()),  
262                  SLOT(refresh()));          QListView::setColumnWidth(0, 120);      // Name
263            QListView::setColumnWidth(5, 240);      // File
264          QObject::connect(this,  
265                  SIGNAL(selectionChanged()),          m_pNewGroupAction = new QAction(
266                  SLOT(selectionChangedSlot()));                  QIcon(":/icons/itemGroupNew.png"),
267          QObject::connect(this,                  tr("New &Group"), tr("Ctrl+G"), this);
268                  SIGNAL(doubleClicked(QListViewItem*, const QPoint&, int)),          m_pNewItemAction  = new QAction(
269                  SLOT(activatedSlot(QListViewItem*)));                  QIcon(":/icons/itemNew.png"),
270          QObject::connect(this,                  tr("New &Instrument..."), tr("Ins"), this);
271                  SIGNAL(returnPressed(QListViewItem*)),          m_pEditItemAction = new QAction(
272                  SLOT(activatedSlot(QListViewItem*)));                  QIcon(":/icons/formEdit.png"),
273          QObject::connect(this,                  tr("&Edit..."), tr("Enter"), this);
274                  SIGNAL(itemRenamed(QListViewItem*,int)),          m_pRenameAction   = new QAction(tr("&Rename"), tr("F2"), this);
275                  SLOT(renamedSlot(QListViewItem*)));          m_pDeleteAction   = new QAction(
276                    QIcon(":/icons/formRemove.png"),
277          selectionChangedSlot();                  tr("&Delete"), tr("Del"), this);
278  }          m_pRefreshAction  = new QAction(
279                    QIcon(":/icons/formRefresh.png"),
280                    tr("Re&fresh"), tr("F5"), this);
281  // Default destructor.  
282  qsamplerInstrumentList::~qsamplerInstrumentList (void)          m_pNewGroupAction->setToolTip(tr("New Group"));
283  {          m_pNewItemAction->setToolTip(tr("New Instrument"));
284          delete m_pNewGroupAction;          m_pEditItemAction->setToolTip(tr("Edit"));
285          delete m_pNewItemAction;          m_pRenameAction->setToolTip(tr("Rename"));
286          delete m_pEditItemAction;          m_pDeleteAction->setToolTip(tr("Delete"));
287          delete m_pRenameAction;          m_pRefreshAction->setToolTip(tr("Refresh"));
288          delete m_pDeleteAction;  
289  }          QObject::connect(m_pNewGroupAction,
290                    SIGNAL(activated()),
291                    SLOT(newGroupSlot()));
292  // Add a new instrument item, optionally under a given group.          QObject::connect(m_pNewItemAction,
293  qsamplerInstrumentItem *qsamplerInstrumentList::addItem (                  SIGNAL(activated()),
294          qsamplerInstrument *pInstrument,                  SLOT(newItemSlot()));
295          qsamplerInstrumentGroup *pParentGroup )          QObject::connect(m_pEditItemAction,
296  {                  SIGNAL(activated()),
297          qsamplerInstrumentItem *pItem = findItem(pInstrument);                  SLOT(editItemSlot()));
298          if (pItem == NULL) {          QObject::connect(m_pRenameAction,
299                  if (pParentGroup)                  SIGNAL(activated()),
300                          pItem = new qsamplerInstrumentItem(pParentGroup, pInstrument);                  SLOT(renameSlot()));
301                  else          QObject::connect(m_pDeleteAction,
302                          pItem = new qsamplerInstrumentItem(this, pInstrument);                  SIGNAL(activated()),
303          }                  SLOT(deleteSlot()));
304          QListView::setSelected(pItem, true);          QObject::connect(m_pRefreshAction,
305          return pItem;                  SIGNAL(activated()),
306  }                  SLOT(refresh()));
307    
308            QObject::connect(this,
309  // Add a new instrument group, optionally under another group.                  SIGNAL(selectionChanged()),
310  qsamplerInstrumentGroup *qsamplerInstrumentList::addGroup (                  SLOT(selectionChangedSlot()));
311          const QString& sName, qsamplerInstrumentGroup *pParentGroup )          QObject::connect(this,
312  {                  SIGNAL(doubleClicked(QListViewItem*, const QPoint&, int)),
313          qsamplerInstrumentGroup *pGroup = findGroup(sName);                  SLOT(activatedSlot(QListViewItem*)));
314          if (pGroup == NULL) {          QObject::connect(this,
315                  if (pParentGroup)                  SIGNAL(returnPressed(QListViewItem*)),
316                          pGroup = new qsamplerInstrumentGroup(pParentGroup, sName);                  SLOT(activatedSlot(QListViewItem*)));
317                  else          QObject::connect(this,
318                          pGroup = new qsamplerInstrumentGroup(this, sName);                  SIGNAL(itemRenamed(QListViewItem*,int)),
319          }                  SLOT(renamedSlot(QListViewItem*)));
320          QListView::setSelected(pGroup, true);  
321          return pGroup;          selectionChangedSlot();
322  }  }
323    
324    
325  // Find a group item, given its name.  // Default destructor.
326  qsamplerInstrumentGroup *qsamplerInstrumentList::findGroup (  qsamplerInstrumentList::~qsamplerInstrumentList (void)
327          const QString& sName ) const  {
328  {          delete m_pNewGroupAction;
329          // Iterate all over the place to search for the group.          delete m_pNewItemAction;
330          QListViewItemIterator iter((QListView *) this);          delete m_pEditItemAction;
331          while (iter.current()) {          delete m_pRenameAction;
332                  QListViewItem *pItem = iter.current();          delete m_pDeleteAction;
333                  if (pItem->rtti() == Group && pItem->text(0) == sName)  }
334                          return static_cast<qsamplerInstrumentGroup *> (pItem);  
335                  ++iter;  
336          }  // Add a new instrument item, optionally under a given group.
337          // Not found.  qsamplerInstrumentItem *qsamplerInstrumentList::addItem (
338          return NULL;          qsamplerInstrument *pInstrument,
339  }          qsamplerInstrumentGroup *pParentGroup )
340    {
341            // Check it there's already one instrument item
342  // Find a file item, given its name.          // with the very same key (bank, program);
343  qsamplerInstrumentItem *qsamplerInstrumentList::findItem (          // if yes, just remove it without prejudice...
344          qsamplerInstrument *pInstrument ) const          qsamplerInstrumentItem *pItem = findItem(pInstrument);
345  {          if (pItem) {
346          // Iterate all over the place to search for the group.                  // If exactly the same, just update view and bail out...
347          QListViewItemIterator iter((QListView *) this);                  if (pItem->instrument() == pInstrument) {
348          while (iter.current()) {                          pItem->update();
349                  QListViewItem *pListItem = iter.current();                          return pItem;
350                  if (pListItem->rtti() == Item) {                  }
351                          qsamplerInstrumentItem *pItem                  // Remove it, as instrument keys must be unique.
352                                  = static_cast<qsamplerInstrumentItem *> (pListItem);                  delete pItem;
353                          if (pItem && pItem->Instrument() == pInstrument)          }
354                                  return pItem;  
355                  }          // Add the new item under proper group one, if any...
356                  ++iter;          if (pParentGroup) {
357          }                  pParentGroup->setOpen(true);
358          // Not found.                  pItem = new qsamplerInstrumentItem(pParentGroup, pInstrument);
359          return NULL;          } else {
360  }                  pItem = new qsamplerInstrumentItem(this, pInstrument);
361            }
362    
363  // Find and return the nearest group item...          // Set it as current selection...
364  qsamplerInstrumentGroup *qsamplerInstrumentList::groupItem (          QListView::setSelected(pItem, true);
365          QListViewItem *pItem ) const  
366  {          return pItem;
367          while (pItem && pItem->rtti() != Group)  }
368                  pItem = pItem->parent();  
369          return static_cast<qsamplerInstrumentGroup *> (pItem);  
370  }  // Add a new instrument group, optionally under another group.
371    qsamplerInstrumentGroup *qsamplerInstrumentList::addGroup (
372            const QString& sName, qsamplerInstrumentGroup *pParentGroup )
373  // Add a new group item below the current one.  {
374  void qsamplerInstrumentList::newGroupSlot (void)          qsamplerInstrumentGroup *pGroup = findGroup(sName);
375  {          if (pGroup == NULL) {
376          qsamplerInstrumentGroup *pParentGroup                  if (pParentGroup) {
377                  = groupItem(QListView::selectedItem());                          pParentGroup->setOpen(true);
378          qsamplerInstrumentGroup *pNewGroup                          pGroup = new qsamplerInstrumentGroup(pParentGroup, sName);
379                  = addGroup(tr("New Group"), pParentGroup);                  } else {
380          if (pParentGroup)                          pGroup = new qsamplerInstrumentGroup(this, sName);
381                  pParentGroup->setOpen(true);                  }
382          if (pNewGroup)          }
383                  pNewGroup->startRename(0);          QListView::setSelected(pGroup, true);
384            return pGroup;
385          selectionChangedSlot();  }
386  }  
387    
388    // Find a group item, given its name.
389  // Add a new instrument item below the current one.  qsamplerInstrumentGroup *qsamplerInstrumentList::findGroup (
390  void qsamplerInstrumentList::newItemSlot (void)          const QString& sName ) const
391  {  {
392          qsamplerInstrument *pInstrument = new qsamplerInstrument();          // Iterate all over the place to search for the group.
393            QListViewItemIterator iter((QListView *) this);
394          qsamplerInstrumentForm form(this);          while (iter.current()) {
395          form.setup(pInstrument);                  QListViewItem *pItem = iter.current();
396          if (!form.exec()) {                  if (pItem->rtti() == Group && pItem->text(0) == sName)
397                  delete pInstrument;                          return static_cast<qsamplerInstrumentGroup *> (pItem);
398                  return;                  ++iter;
399          }          }
400            // Not found.
401          pInstrument->map();          return NULL;
402          emit instrumentsChanged();  }
403    
404          qsamplerInstrumentGroup *pParentGroup  
405                  = groupItem(QListView::selectedItem());  // Find a file item, given its name.
406          addItem(pInstrument, pParentGroup);  qsamplerInstrumentItem *qsamplerInstrumentList::findItem (
407          if (pParentGroup)          qsamplerInstrument *pInstrument ) const
408                  pParentGroup->setOpen(true);  {
409            if (pInstrument == NULL)
410          selectionChangedSlot();                  return NULL;
411  }  
412            // Iterate all over the place to search for the group.
413            QListViewItemIterator iter((QListView *) this);
414  // Edit current item below the current one.          while (iter.current()) {
415  void qsamplerInstrumentList::editItemSlot (void)                  QListViewItem *pListItem = iter.current();
416  {                  if (pListItem->rtti() == Item) {
417          QListViewItem *pListItem = QListView::selectedItem();                          qsamplerInstrumentItem *pItem
418          if (pListItem == NULL)                                  = static_cast<qsamplerInstrumentItem *> (pListItem);
419                  return;                          if (pItem && pItem->instrument()
420          if (pListItem->rtti() == Item) {                                  && pItem->instrument()->map()  == pInstrument->map()
421                  qsamplerInstrumentItem *pItem                                  && pItem->instrument()->bank() == pInstrument->bank()
422                          = static_cast<qsamplerInstrumentItem *> (pListItem);                                  && pItem->instrument()->prog() == pInstrument->prog())
423                  if (pItem && pItem->Instrument()) {                                  return pItem;
424                          qsamplerInstrumentForm form(this);                  }
425                          form.setup(pItem->Instrument());                  ++iter;
426                          if (form.exec()) {          }
427                                  pItem->Instrument()->map();          // Not found.
428                                  emit instrumentsChanged();          return NULL;
429                                  pItem->update();  }
430                          }  
431                  }  
432          }  // Find and return the nearest group item...
433    qsamplerInstrumentGroup *qsamplerInstrumentList::groupItem (
434          selectionChangedSlot();          QListViewItem *pItem ) const
435  }  {
436            while (pItem && pItem->rtti() != Group)
437                    pItem = pItem->parent();
438  // Rename current group/item.          return static_cast<qsamplerInstrumentGroup *> (pItem);
439  void qsamplerInstrumentList::renameSlot (void)  }
440  {  
441          QListViewItem *pListItem = QListView::selectedItem();  
442          if (pListItem)  // Add a new group item below the current one.
443                  pListItem->startRename(0);  void qsamplerInstrumentList::newGroupSlot (void)
444    {
445          selectionChangedSlot();          qsamplerInstrumentGroup *pNewGroup
446  }                  = addGroup(tr("New Group"), groupItem(QListView::selectedItem()));
447            if (pNewGroup)
448                    pNewGroup->startRename(0);
449  // Remove current group/item.  
450  void qsamplerInstrumentList::deleteSlot (void)          selectionChangedSlot();
451  {  }
452          QListViewItem *pListItem = QListView::selectedItem();  
453          if (pListItem) {  
454                  if (pListItem->rtti() == Item) {  // Map selector.
455                          qsamplerInstrumentItem *pItem  void qsamplerInstrumentList::setMidiMap ( int iMidiMap )
456                                  = static_cast<qsamplerInstrumentItem *> (pListItem);  {
457                          if (pItem && pItem->Instrument()) {          if (iMidiMap < 0)
458                                  pItem->Instrument()->unmap();                  iMidiMap = LSCP_MIDI_MAP_ALL;
459                                  emit instrumentsChanged();  
460                          }          m_iMidiMap = iMidiMap;
461                  }  }
462                  delete pListItem;  
463          }  int qsamplerInstrumentList::midiMap (void) const
464    {
465          selectionChangedSlot();          return m_iMidiMap;
466  }  }
467    
468    
469  // In-place selection slot.  // List actions accessors.
470  void qsamplerInstrumentList::selectionChangedSlot (void)  QAction *qsamplerInstrumentList::newGroupAction (void) const
471  {  {
472          qsamplerMainForm *pMainForm = qsamplerMainForm::getInstance();          return m_pNewGroupAction;
473          QListViewItem *pListItem = QListView::selectedItem();  }
474          bool bEnabled = (pMainForm && pMainForm->client());  
475          m_pNewItemAction->setEnabled(bEnabled);  QAction *qsamplerInstrumentList::newItemAction (void) const
476          bEnabled = (bEnabled && pListItem != NULL);  {
477          m_pEditItemAction->setEnabled(bEnabled && pListItem->rtti() == Item);          return m_pNewItemAction;
478          m_pRenameAction->setEnabled(bEnabled);  }
479          m_pDeleteAction->setEnabled(bEnabled);  
480  }  QAction *qsamplerInstrumentList::editItemAction (void) const
481    {
482            return m_pEditItemAction;
483  // In-place activation slot.  }
484  void qsamplerInstrumentList::activatedSlot ( QListViewItem *pListItem )  
485  {  QAction *qsamplerInstrumentList::renameAction (void) const
486          // FIXME: Hope the list view item is the one selected.  {
487          if (pListItem->rtti() == Item)          return m_pRenameAction;
488                  editItemSlot();  }
489  }  
490    QAction *qsamplerInstrumentList::deleteAction (void) const
491    {
492  // In-place aliasing slot.          return m_pDeleteAction;
493  void qsamplerInstrumentList::renamedSlot ( QListViewItem *pListItem )  }
494  {  
495          if (pListItem->rtti() == Item) {  QAction *qsamplerInstrumentList::refreshAction (void) const
496                  qsamplerInstrumentItem *pItem  {
497                          = static_cast<qsamplerInstrumentItem *> (pListItem);          return m_pRefreshAction;
498                  if (pItem && pItem->Instrument()) {  }
499                          pItem->Instrument()->setName(pListItem->text(0));  
500                          pItem->Instrument()->map();  
501                          emit instrumentsChanged();  // Add a new instrument item below the current one.
502                          pItem->update();  void qsamplerInstrumentList::newItemSlot (void)
503                  }  {
504          }          qsamplerInstrument *pInstrument = new qsamplerInstrument();
505  }  
506            qsamplerInstrumentForm form(this);
507            form.setup(pInstrument);
508  // Context menu request event handler.          if (!form.exec()) {
509  void qsamplerInstrumentList::contextMenuEvent (                  delete pInstrument;
510          QContextMenuEvent *pContextMenuEvent )                  return;
511  {          }
512          QPopupMenu menu(this);  
513            // Commit...
514          // Construct context menu.          pInstrument->mapInstrument();
515          m_pNewItemAction->addTo(&menu);          // add new item to the tree...
516  //      m_pNewGroupAction->addTo(&menu);          addItem(pInstrument, groupItem(QListView::selectedItem()));
517          menu.insertSeparator();          // Notify we've changes...
518          m_pEditItemAction->addTo(&menu);          emit instrumentsChanged();
519          m_pRenameAction->addTo(&menu);  
520          m_pDeleteAction->addTo(&menu);          selectionChangedSlot();
521          menu.insertSeparator();  }
522          m_pRefreshAction->addTo(&menu);  
523    
524          menu.exec(pContextMenuEvent->globalPos());  // Edit current item below the current one.
525  }  void qsamplerInstrumentList::editItemSlot (void)
526    {
527            QListViewItem *pListItem = QListView::selectedItem();
528  // General reloader.          if (pListItem == NULL)
529  void qsamplerInstrumentList::refresh (void)                  return;
530  {          if (pListItem->rtti() == Item) {
531          clear();                  qsamplerInstrument *pInstrument = NULL;
532                    qsamplerInstrumentItem *pItem
533          qsamplerMainForm *pMainForm = qsamplerMainForm::getInstance();                          = static_cast<qsamplerInstrumentItem *> (pListItem);
534          if (pMainForm == NULL)                  if (pItem)
535                  return;                          pInstrument = pItem->instrument();
536          if (pMainForm->client() == NULL)                  if (pInstrument) {
537                  return;                          // Save current key values...
538                            qsamplerInstrument oldInstrument(*pInstrument);
539          qsamplerInstrumentItem *pItem = NULL;                          // Do the edit dance...
540          lscp_midi_instrument_t *pInstrs                          qsamplerInstrumentForm form(this);
541                  = ::lscp_list_midi_instruments(pMainForm->client());                          form.setup(pInstrument);
542          for (int iInstr = 0; pInstrs && pInstrs[iInstr].program >= 0; ++iInstr) {                          if (form.exec()) {
543                  int iBank = (pInstrs[iInstr].bank_msb << 7) | pInstrs[iInstr].bank_lsb;                                  // Commit...
544                  int iProgram = pInstrs[iInstr].program;                                  pInstrument->mapInstrument();
545                  qsamplerInstrument *pInstrument                                  // Check whether we changed instrument key...
546                          = new qsamplerInstrument(iBank, iProgram);                                  if (oldInstrument.map()  == pInstrument->map()  &&
547                  if (pInstrument->get())                                          oldInstrument.bank() == pInstrument->bank() &&
548                          pItem = new qsamplerInstrumentItem(this, pInstrument, pItem);                                          oldInstrument.prog() == pInstrument->prog()) {
549          }                                          // just update tree item...
550                                            pItem->update();
551          if (pInstrs == NULL && ::lscp_client_get_errno(pMainForm->client())) {                                  } else {
552                  pMainForm->appendMessagesClient("lscp_list_midi_instruments");                                          // Unmap old instance...
553                  pMainForm->appendMessagesError(tr("Could not get current list of MIDI instrument mappings.\n\nSorry."));                                          oldInstrument.unmapInstrument();
554          }                                          // Change item tree, whether applicable...
555                                            if (m_iMidiMap < 0 || m_iMidiMap == pInstrument->map()) {
556          selectionChangedSlot();                                                  // Add new brand item into view...
557  }                                                  addItem(pInstrument, groupItem(pListItem));
558                                            } else {
559                                                    // Just remove/hide old one.
560  // end of qsamplerInstrumentList.cpp                                                  delete pItem;
561                                            }
562                                    }
563                                    // Notify we've changes...
564                                    emit instrumentsChanged();
565                            }
566                    }
567            }
568    
569            selectionChangedSlot();
570    }
571    
572    
573    // Rename current group/item.
574    void qsamplerInstrumentList::renameSlot (void)
575    {
576            QListViewItem *pListItem = QListView::selectedItem();
577            if (pListItem)
578                    pListItem->startRename(0);
579    
580            selectionChangedSlot();
581    }
582    
583    
584    // Remove current group/item.
585    void qsamplerInstrumentList::deleteSlot (void)
586    {
587            QListViewItem *pListItem = QListView::selectedItem();
588            if (pListItem == NULL)
589                    return;
590    
591            qsamplerMainForm *pMainForm = qsamplerMainForm::getInstance();
592            if (pMainForm == NULL)
593                    return;
594    
595            // Prompt user if this is for real...
596            qsamplerOptions *pOptions = pMainForm->options();
597            if (pOptions && pOptions->bConfirmRemove) {
598                    if (QMessageBox::warning(this,
599                            QSAMPLER_TITLE ": " + tr("Warning"),
600                            tr("Delete %1:\n\n"
601                            "%2\n\n"
602                            "Are you sure?")
603                            .arg(pListItem->rtti() == Item ? tr("instrument") : tr("group"))
604                            .arg(pListItem->text(0)),
605                            tr("OK"), tr("Cancel")) > 0)
606                            return;
607            }
608    
609            // Unmap instrument entry...
610            if (pListItem->rtti() == Item) {
611                    qsamplerInstrumentItem *pItem
612                            = static_cast<qsamplerInstrumentItem *> (pListItem);
613                    if (pItem && pItem->instrument()) {
614                            pItem->instrument()->unmapInstrument();
615                            emit instrumentsChanged();
616                    }
617            }
618    
619            // Do it for real...
620            delete pListItem;
621    
622            selectionChangedSlot();
623    }
624    
625    
626    // In-place selection slot.
627    void qsamplerInstrumentList::selectionChangedSlot (void)
628    {
629            qsamplerMainForm *pMainForm = qsamplerMainForm::getInstance();
630            QListViewItem *pListItem = QListView::selectedItem();
631            bool bEnabled = (pMainForm && pMainForm->client());
632            m_pNewItemAction->setEnabled(bEnabled);
633            bEnabled = (bEnabled && pListItem != NULL);
634            m_pEditItemAction->setEnabled(bEnabled && pListItem->rtti() == Item);
635            m_pRenameAction->setEnabled(bEnabled);
636            m_pDeleteAction->setEnabled(bEnabled);
637    }
638    
639    
640    // In-place activation slot.
641    void qsamplerInstrumentList::activatedSlot ( QListViewItem *pListItem )
642    {
643            // FIXME: Hope the list view item is the one selected.
644            if (pListItem && pListItem->rtti() == Item)
645                    editItemSlot();
646    }
647    
648    
649    // In-place aliasing slot.
650    void qsamplerInstrumentList::renamedSlot ( QListViewItem *pListItem )
651    {
652            if (pListItem->rtti() == Item) {
653                    qsamplerInstrumentItem *pItem
654                            = static_cast<qsamplerInstrumentItem *> (pListItem);
655                    if (pItem && pItem->instrument()) {
656                            pItem->instrument()->setName(pListItem->text(0));
657                            pItem->instrument()->mapInstrument();
658                            emit instrumentsChanged();
659                            pItem->update();
660                    }
661            }
662    }
663    
664    
665    // Context menu request event handler.
666    void qsamplerInstrumentList::contextMenuEvent (
667            QContextMenuEvent *pContextMenuEvent )
668    {
669            if (!m_pNewItemAction->isEnabled())
670                    return;
671    
672            QPopupMenu menu(this);
673    
674            // Construct context menu.
675            m_pNewItemAction->addTo(&menu);
676    //      m_pNewGroupAction->addTo(&menu);
677            menu.insertSeparator();
678            m_pEditItemAction->addTo(&menu);
679            m_pRenameAction->addTo(&menu);
680            m_pDeleteAction->addTo(&menu);
681            menu.insertSeparator();
682            m_pRefreshAction->addTo(&menu);
683    
684            menu.exec(pContextMenuEvent->globalPos());
685    }
686    
687    
688    // General reloader.
689    void qsamplerInstrumentList::refresh (void)
690    {
691            clear();
692    
693            qsamplerMainForm *pMainForm = qsamplerMainForm::getInstance();
694            if (pMainForm == NULL)
695                    return;
696            if (pMainForm->client() == NULL)
697                    return;
698    
699            QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
700    
701            // Load the whole bunch of instrument items...
702            qsamplerInstrumentItem *pItem = NULL;
703            lscp_midi_instrument_t *pInstrs
704                    = ::lscp_list_midi_instruments(pMainForm->client(), m_iMidiMap);
705            for (int iInstr = 0; pInstrs && pInstrs[iInstr].map >= 0; ++iInstr) {
706                    int iMap  = pInstrs[iInstr].map;
707                    int iBank = pInstrs[iInstr].bank;
708                    int iProg = pInstrs[iInstr].prog;
709                    qsamplerInstrument *pInstrument
710                            = new qsamplerInstrument(iMap, iBank, iProg);
711                    if (pInstrument->getInstrument())
712                            pItem = new qsamplerInstrumentItem(this, pInstrument, pItem);
713                    // Try to keep it snappy :)
714                    QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
715            }
716    
717            QApplication::restoreOverrideCursor();
718    
719            if (pInstrs == NULL && ::lscp_client_get_errno(pMainForm->client())) {
720                    pMainForm->appendMessagesClient("lscp_list_midi_instruments");
721                    pMainForm->appendMessagesError(tr("Could not get current list of MIDI instrument mappings.\n\nSorry."));
722            }
723    
724            selectionChangedSlot();
725    }
726    #endif
727    
728    MidiInstrumentsModel::MidiInstrumentsModel(QObject* parent) : QAbstractTableModel(parent) {
729        m_iMidiMap = LSCP_MIDI_MAP_ALL;
730    }
731    
732    int MidiInstrumentsModel::rowCount(const QModelIndex& /*parent*/) const {
733        if (m_iMidiMap == LSCP_MIDI_MAP_ALL) {
734            int n = 0;
735            for (InstrumentsMap::const_iterator itMap = instruments.begin(); itMap != instruments.end(); ++itMap)
736                n += (*itMap).size();
737            return n;
738        }
739        InstrumentsMap::const_iterator itMap = instruments.find(m_iMidiMap);
740        if (itMap == instruments.end()) return 0;
741        return (*itMap).size();
742    }
743    
744    int MidiInstrumentsModel::columnCount(const QModelIndex& /*parent*/) const {
745        return 9;
746    }
747    
748    QVariant MidiInstrumentsModel::data(const QModelIndex &index, int role) const {
749        if (!index.isValid() || role != Qt::DisplayRole) return QVariant();
750    
751        if (m_iMidiMap == LSCP_MIDI_MAP_ALL) {
752            int n = 0;
753            for (InstrumentsMap::const_iterator itMap = instruments.begin(); itMap != instruments.end(); ++itMap) {
754                n += (*itMap).size();
755                if (index.row() < n)
756                    return QVariant::fromValue(
757                        (*itMap)[index.row() + (*itMap).size() - n]
758                    );
759            }
760        } else {
761            // resolve MIDI instrument map
762            InstrumentsMap::const_iterator itMap = instruments.find(m_iMidiMap);
763            if (itMap == instruments.end()) return QVariant();
764            // resolve instrument in that map
765            if (index.row() >= (*itMap).size()) return QVariant();
766            return QVariant::fromValue(
767                (*itMap)[index.row()]
768            );
769        }
770    
771        return QVariant();
772    }
773    
774    QVariant MidiInstrumentsModel::headerData(int section, Qt::Orientation orientation, int role) const {
775        if (orientation != Qt::Horizontal || role != Qt::DisplayRole)
776            return QVariant();
777    
778        switch (section) {
779            case 0: return tr("Name");
780            case 1: return tr("Map");
781            case 2: return tr("Bank");
782            case 3: return tr("Prog");
783            case 4: return tr("Engine");
784            case 5: return tr("File");
785            case 6: return tr("Nr");
786            case 7: return tr("Vol");
787            case 8: return tr("Mode");
788            default: return QVariant();
789        }
790    }
791    
792    qsamplerInstrument* MidiInstrumentsModel::addInstrument(int iMap, int iBank, int iProg) {
793        // Check it there's already one instrument item
794        // with the very same key (bank, program);
795        // if yes, just remove it without prejudice...
796        for (int i = 0; i < instruments[iMap].size(); i++) {
797            if (
798                instruments[iMap][i].bank() == iBank &&
799                instruments[iMap][i].prog() == iProg
800            ) {
801                instruments[iMap].removeAt(i);
802                break;
803            }
804        }
805    
806        // resolve the appropriate place, we keep the list sorted that way ...
807        int i = 0;
808        for (; i < instruments[iMap].size(); i++)
809            if (
810                iBank > instruments[iMap][i].bank() ||
811                ( iBank == instruments[iMap][i].bank() &&
812                  iProg > instruments[iMap][i].prog() )
813            ) break;
814    
815        qsamplerInstrument& instr = instruments[iMap][i] = qsamplerInstrument(iMap, iBank, iProg);
816    
817        return &instr;
818    }
819    
820    void MidiInstrumentsModel::setMidiMap(int iMidiMap) {
821        if (iMidiMap < 0)
822            iMidiMap = LSCP_MIDI_MAP_ALL;
823    
824        m_iMidiMap = iMidiMap;
825    }
826    
827    int MidiInstrumentsModel::midiMap() const {
828        return m_iMidiMap;
829    }
830    
831    void MidiInstrumentsModel::refresh() {
832            instruments.clear();
833    
834            MainForm* pMainForm = MainForm::getInstance();
835            if (pMainForm == NULL)
836                    return;
837            if (pMainForm->client() == NULL)
838                    return;
839    
840            QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
841    
842            // Load the whole bunch of instrument items...
843            lscp_midi_instrument_t* pInstrs
844                    = ::lscp_list_midi_instruments(pMainForm->client(), m_iMidiMap);
845            for (int iInstr = 0; pInstrs && pInstrs[iInstr].map >= 0; ++iInstr) {
846                    const int iMap  = pInstrs[iInstr].map;
847                    const int iBank = pInstrs[iInstr].bank;
848                    const int iProg = pInstrs[iInstr].prog;
849                    addInstrument(iMap, iBank, iProg);
850                    // Try to keep it snappy :)
851                    QApplication::processEvents(QEventLoop::ExcludeUserInput);
852            }
853    
854            QApplication::restoreOverrideCursor();
855    
856            if (pInstrs == NULL && ::lscp_client_get_errno(pMainForm->client())) {
857                    pMainForm->appendMessagesClient("lscp_list_midi_instruments");
858                    pMainForm->appendMessagesError(tr("Could not get current list of MIDI instrument mappings.\n\nSorry."));
859            }
860    
861            //selectionChangedSlot();
862    }
863    
864    
865    MidiInstrumentsDelegate::MidiInstrumentsDelegate(QObject* parent) : QItemDelegate(parent) {
866    }
867    
868    QWidget* MidiInstrumentsDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const {
869        return new QLabel(index.model()->data(index, Qt::DisplayRole).toString(), parent);
870    }
871    
872    void MidiInstrumentsDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const {
873    }
874    
875    void MidiInstrumentsDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const {
876    }
877    
878    void MidiInstrumentsDelegate::updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const {
879        if (editor) editor->setGeometry(option.rect);
880    }
881    
882    // end of qsamplerInstrumentList.cpp
883    

Legend:
Removed from v.962  
changed lines
  Added in v.1463

  ViewVC Help
Powered by ViewVC