/[svn]/qsampler/trunk/src/qsamplerMainForm.ui.h
ViewVC logotype

Diff of /qsampler/trunk/src/qsamplerMainForm.ui.h

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

revision 122 by capela, Sat Jun 12 13:21:35 2004 UTC revision 391 by capela, Fri Feb 18 10:28:45 2005 UTC
# Line 2  Line 2 
2  //  //
3  // ui.h extension file, included from the uic-generated form implementation.  // ui.h extension file, included from the uic-generated form implementation.
4  /****************************************************************************  /****************************************************************************
5     Copyright (C) 2004, rncbc aka Rui Nuno Capela. All rights reserved.     Copyright (C) 2004-2005, rncbc aka Rui Nuno Capela. All rights reserved.
6    
7     This program is free software; you can redistribute it and/or     This program is free software; you can redistribute it and/or
8     modify it under the terms of the GNU General Public License     modify it under the terms of the GNU General Public License
# Line 26  Line 26 
26  #include <qprocess.h>  #include <qprocess.h>
27  #include <qmessagebox.h>  #include <qmessagebox.h>
28  #include <qdragobject.h>  #include <qdragobject.h>
29    #include <qregexp.h>
30  #include <qfiledialog.h>  #include <qfiledialog.h>
31  #include <qfileinfo.h>  #include <qfileinfo.h>
32  #include <qfile.h>  #include <qfile.h>
# Line 36  Line 37 
37    
38  #include "qsamplerAbout.h"  #include "qsamplerAbout.h"
39  #include "qsamplerOptions.h"  #include "qsamplerOptions.h"
40    #include "qsamplerChannel.h"
41  #include "qsamplerMessages.h"  #include "qsamplerMessages.h"
42    
43  #include "qsamplerChannelStrip.h"  #include "qsamplerChannelStrip.h"
# Line 43  Line 45 
45    
46  #include "config.h"  #include "config.h"
47    
48    #ifdef HAVE_SIGNAL_H
49    #include <signal.h>
50    #endif
51    
52  // Timer constant stuff.  // Timer constant stuff.
53  #define QSAMPLER_TIMER_MSECS    200  #define QSAMPLER_TIMER_MSECS    200
# Line 54  Line 59 
59  #define QSAMPLER_STATUS_SESSION 3       // Current session modification state.  #define QSAMPLER_STATUS_SESSION 3       // Current session modification state.
60    
61    
62    // All winsock apps needs this.
63  #if defined(WIN32)  #if defined(WIN32)
64  static WSADATA _wsaData;  static WSADATA _wsaData;
65  #endif  #endif
66    
67    
68    //-------------------------------------------------------------------------
69    // qsamplerCustomEvent -- specialty for callback comunication.
70    
71    #define QSAMPLER_CUSTOM_EVENT   1000
72    
73    class qsamplerCustomEvent : public QCustomEvent
74    {
75    public:
76    
77        // Constructor.
78        qsamplerCustomEvent(lscp_event_t event, const char *pchData, int cchData)
79            : QCustomEvent(QSAMPLER_CUSTOM_EVENT)
80        {
81            m_event = event;
82            m_data.setLatin1(pchData, cchData);
83        }
84    
85        // Accessors.
86        lscp_event_t event() { return m_event; }
87        QString&     data()  { return m_data;  }
88    
89    private:
90    
91        // The proper event type.
92        lscp_event_t m_event;
93        // The event data as a string.
94        QString      m_data;
95    };
96    
97    
98  //-------------------------------------------------------------------------  //-------------------------------------------------------------------------
99  // qsamplerMainForm -- Main window form implementation.  // qsamplerMainForm -- Main window form implementation.
100    
# Line 71  void qsamplerMainForm::init (void) Line 108  void qsamplerMainForm::init (void)
108      m_pMessages = NULL;      m_pMessages = NULL;
109    
110      // We'll start clean.      // We'll start clean.
111      m_iUntitled = 0;      m_iUntitled    = 0;
112      m_iDirtyCount = 0;      m_iDirtyCount  = 0;
113        m_iChangeCount = 0;
114    
115      m_pServer = NULL;      m_pServer = NULL;
116      m_pClient = NULL;      m_pClient = NULL;
# Line 82  void qsamplerMainForm::init (void) Line 120  void qsamplerMainForm::init (void)
120    
121      m_iTimerSlot = 0;      m_iTimerSlot = 0;
122    
123    #ifdef HAVE_SIGNAL_H
124            // Set to ignore any fatal "Broken pipe" signals.
125            ::signal(SIGPIPE, SIG_IGN);
126    #endif
127    
128      // Make it an MDI workspace.      // Make it an MDI workspace.
129      m_pWorkspace = new QWorkspace(this);      m_pWorkspace = new QWorkspace(this);
130      m_pWorkspace->setScrollBarsEnabled(true);      m_pWorkspace->setScrollBarsEnabled(true);
# Line 252  void qsamplerMainForm::closeEvent ( QClo Line 295  void qsamplerMainForm::closeEvent ( QClo
295  }  }
296    
297    
298  void qsamplerMainForm::dragEnterEvent ( QDragEnterEvent* pDragEnterEvent )  // Drag'n'drop file handler.
299    bool qsamplerMainForm::decodeDragFiles ( const QMimeSource *pEvent, QStringList& files )
300  {  {
301      bool bAccept = false;      bool bDecode = false;
302    
303      if (QTextDrag::canDecode(pDragEnterEvent)) {      if (QTextDrag::canDecode(pEvent)) {
304          QString sUrl;          QString sText;
305          if (QTextDrag::decode(pDragEnterEvent, sUrl) && m_pClient)          bDecode = QTextDrag::decode(pEvent, sText);
306              bAccept = QFileInfo(QUrl(sUrl).path()).exists();          if (bDecode) {
307                files = QStringList::split('\n', sText);
308                for (QStringList::Iterator iter = files.begin(); iter != files.end(); iter++)
309                    *iter = (*iter).stripWhiteSpace().replace(QRegExp("^file:"), QString::null);
310            }
311      }      }
312    
313      pDragEnterEvent->accept(bAccept);      return bDecode;
314    }
315    
316    
317    // Window drag-n-drop event handlers.
318    void qsamplerMainForm::dragEnterEvent ( QDragEnterEvent* pDragEnterEvent )
319    {
320        QStringList files;
321    
322        pDragEnterEvent->accept(decodeDragFiles(pDragEnterEvent, files));
323  }  }
324    
325    
326  void qsamplerMainForm::dropEvent ( QDropEvent* pDropEvent )  void qsamplerMainForm::dropEvent ( QDropEvent* pDropEvent )
327  {  {
328      if (QTextDrag::canDecode(pDropEvent)) {      QStringList files;
329          QString sUrl;  
330          if (QTextDrag::decode(pDropEvent, sUrl) && closeSession(true))      if (!decodeDragFiles(pDropEvent, files))
331              loadSessionFile(QUrl(sUrl).path());          return;
332    
333        for (QStringList::Iterator iter = files.begin(); iter != files.end(); iter++) {
334                    const QString& sPath = QUrl(*iter).path();
335                    if (qsamplerChannel::isInstrumentFile(sPath)) {
336                            // Try to create a new channel from instrument file...
337                        qsamplerChannel *pChannel = new qsamplerChannel(this);
338                        if (pChannel == NULL)
339                            return;
340                            // Start setting the instrument filename...
341                            pChannel->setInstrument(sPath, 0);
342                        // Before we show it up, may be we'll
343                        // better ask for some initial values?
344                        if (!pChannel->channelSetup(this)) {
345                            delete pChannel;
346                            return;
347                        }
348                        // Finally, give it to a new channel strip...
349                            if (!createChannelStrip(pChannel)) {
350                            delete pChannel;
351                            return;
352                            }
353                        // Make that an overall update.
354                        m_iDirtyCount++;
355                        m_iChangeCount++;
356                        stabilizeForm();
357                    }   // Otherwise, load an usual session file (LSCP script)...
358                    else if (closeSession(true))
359                            loadSessionFile(sPath);
360                    // Make it look responsive...:)
361                    QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
362            }
363    }
364    
365    
366    // Custome event handler.
367    void qsamplerMainForm::customEvent ( QCustomEvent *pCustomEvent )
368    {
369        // For the time being, just pump it to messages.
370        if (pCustomEvent->type() == QSAMPLER_CUSTOM_EVENT) {
371            qsamplerCustomEvent *pEvent = (qsamplerCustomEvent *) pCustomEvent;
372            appendMessagesColor(tr("Notify event: %1 data: %2")
373                .arg(::lscp_event_to_text(pEvent->event()))
374                .arg(pEvent->data()), "#996699");
375      }      }
376  }  }
377    
378    
379    // Context menu event handler.
380    void qsamplerMainForm::contextMenuEvent( QContextMenuEvent *pEvent )
381    {
382        stabilizeForm();
383        
384        editMenu->exec(pEvent->globalPos());
385    }
386    
387    
388  //-------------------------------------------------------------------------  //-------------------------------------------------------------------------
389  // qsamplerMainForm -- Brainless public property accessors.  // qsamplerMainForm -- Brainless public property accessors.
390    
# Line 428  bool qsamplerMainForm::closeSession ( bo Line 537  bool qsamplerMainForm::closeSession ( bo
537          m_pWorkspace->setUpdatesEnabled(false);          m_pWorkspace->setUpdatesEnabled(false);
538          QWidgetList wlist = m_pWorkspace->windowList();          QWidgetList wlist = m_pWorkspace->windowList();
539          for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {          for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
540              qsamplerChannelStrip *pChannel = (qsamplerChannelStrip *) wlist.at(iChannel);              qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
541              if (bForce && ::lscp_remove_channel(m_pClient, pChannel->channelID()) != LSCP_OK)              if (pChannelStrip) {
542                  appendMessagesClient("lscp_remove_channel");                  qsamplerChannel *pChannel = pChannelStrip->channel();
543              delete pChannel;                  if (bForce && pChannel)
544                        pChannel->removeChannel();
545                    delete pChannelStrip;
546                }
547          }          }
548          m_pWorkspace->setUpdatesEnabled(true);          m_pWorkspace->setUpdatesEnabled(true);
549          // We're now clean, for sure.          // We're now clean, for sure.
# Line 470  bool qsamplerMainForm::loadSessionFile ( Line 582  bool qsamplerMainForm::loadSessionFile (
582              if (::lscp_client_query(m_pClient, sCommand.latin1()) != LSCP_OK) {              if (::lscp_client_query(m_pClient, sCommand.latin1()) != LSCP_OK) {
583                  appendMessagesClient("lscp_client_query");                  appendMessagesClient("lscp_client_query");
584                  iErrors++;                  iErrors++;
585                    break;
586              }              }
587          }          }
588          // Try to make it snappy :)          // Try to make it snappy :)
# Line 481  bool qsamplerMainForm::loadSessionFile ( Line 594  bool qsamplerMainForm::loadSessionFile (
594    
595      // Have we any errors?      // Have we any errors?
596      if (iErrors > 0)      if (iErrors > 0)
597          appendMessagesError(tr("Some setttings could not be loaded\nfrom \"%1\" session file.\n\nSorry.").arg(sFilename));          appendMessagesError(tr("Session could not be loaded\nfrom \"%1\".\n\nSorry.").arg(sFilename));
598    
599      // Now we'll try to create the whole GUI session.      // Now we'll try to create the whole GUI session.
600      int iChannels = ::lscp_get_channels(m_pClient);      int *piChannelIDs = ::lscp_list_channels(m_pClient);
601      if (iChannels < 0) {      if (piChannelIDs == NULL) {
602          appendMessagesClient("lscp_get_channels");          appendMessagesClient("lscp_list_channels");
603          appendMessagesError(tr("Could not get current number of channels.\n\nSorry."));          appendMessagesError(tr("Could not get current list of channels.\n\nSorry."));
604      }      } else {
605                        // Try to (re)create each channel.
606      // Try to (re)create each channel.                  m_pWorkspace->setUpdatesEnabled(false);
607      m_pWorkspace->setUpdatesEnabled(false);                  for (int iChannel = 0; piChannelIDs[iChannel] >= 0; iChannel++) {
608      for (int iChannelID = 0; iChannelID < iChannels; iChannelID++) {                          createChannelStrip(new qsamplerChannel(this, piChannelIDs[iChannel]));
609          createChannel(iChannelID, false);                          QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
610          QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);                  }
611      }                  m_pWorkspace->setUpdatesEnabled(true);
612      m_pWorkspace->setUpdatesEnabled(true);          }
613    
614      // Save as default session directory.      // Save as default session directory.
615      if (m_pOptions)      if (m_pOptions)
# Line 507  bool qsamplerMainForm::loadSessionFile ( Line 620  bool qsamplerMainForm::loadSessionFile (
620      m_sFilename = sFilename;      m_sFilename = sFilename;
621      updateRecentFiles(sFilename);      updateRecentFiles(sFilename);
622      appendMessages(tr("Open session: \"%1\".").arg(sessionName(m_sFilename)));      appendMessages(tr("Open session: \"%1\".").arg(sessionName(m_sFilename)));
623        
624        // Make that an overall update.
625        m_iChangeCount++;
626      stabilizeForm();      stabilizeForm();
627      return true;      return true;
628  }  }
# Line 540  bool qsamplerMainForm::saveSessionFile ( Line 656  bool qsamplerMainForm::saveSessionFile (
656      ts << endl;      ts << endl;
657      QWidgetList wlist = m_pWorkspace->windowList();      QWidgetList wlist = m_pWorkspace->windowList();
658      for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {      for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
659          qsamplerChannelStrip *pChannel = (qsamplerChannelStrip *) wlist.at(iChannel);          qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
660          int iChannelID = pChannel->channelID();          if (pChannelStrip) {
661          ts << "# " << pChannel->caption() << endl;              qsamplerChannel *pChannel = pChannelStrip->channel();
662          ts << "ADD CHANNEL" << endl;              if (pChannel) {
663          ts << "SET CHANNEL AUDIO_OUTPUT_TYPE " << iChannelID << " " << pChannel->audioDriver() << endl;                  int iChannelID = pChannel->channelID();
664          ts << "SET CHANNEL MIDI_INPUT_TYPE " << iChannelID << " " << pChannel->midiDriver() << endl;                  ts << "# " << pChannelStrip->caption() << endl;
665      //  ts << "SET CHANNEL MIDI_INPUT_PORT " << iChannelID << " " << pChannel->midiPort() << endl;                  ts << "ADD CHANNEL" << endl;
666          ts << "SET CHANNEL MIDI_INPUT_CHANNEL " << iChannelID << " " << pChannel->midiChannel() << endl;                  ts << "SET CHANNEL AUDIO_OUTPUT_TYPE " << iChannelID << " " << pChannel->audioDriver() << endl;
667          ts << "LOAD ENGINE " << pChannel->engineName() << " " << iChannelID << endl;                  ts << "SET CHANNEL MIDI_INPUT_TYPE " << iChannelID << " " << pChannel->midiDriver() << endl;
668          ts << "LOAD INSTRUMENT " << pChannel->instrumentFile() << " " << pChannel->instrumentNr() << " " << iChannelID << endl;                  ts << "SET CHANNEL MIDI_INPUT_PORT " << iChannelID << " " << pChannel->midiPort() << endl;
669          ts << "SET CHANNEL VOLUME " << iChannelID << " " << pChannel->volume() << endl;                  ts << "SET CHANNEL MIDI_INPUT_CHANNEL " << iChannelID << " ";
670          ts << endl;                  if (pChannel->midiChannel() == LSCP_MIDI_CHANNEL_ALL)
671                        ts << "ALL";
672                    else
673                        ts << pChannel->midiChannel();
674                    ts << endl;
675                    ts << "LOAD ENGINE " << pChannel->engineName() << " " << iChannelID << endl;
676                    ts << "LOAD INSTRUMENT NON_MODAL '" << pChannel->instrumentFile() << "' " << pChannel->instrumentNr() << " " << iChannelID << endl;
677                    ts << "SET CHANNEL VOLUME " << iChannelID << " " << pChannel->volume() << endl;
678                    ts << endl;
679                }
680            }
681          // Try to keep it snappy :)          // Try to keep it snappy :)
682          QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);          QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
683      }      }
# Line 623  void qsamplerMainForm::fileSaveAs (void) Line 749  void qsamplerMainForm::fileSaveAs (void)
749  }  }
750    
751    
752    // Reset the sampler instance.
753    void qsamplerMainForm::fileReset (void)
754    {
755        if (m_pClient == NULL)
756            return;
757    
758        // Ask user whether he/she want's an internal sampler reset...
759        if (QMessageBox::warning(this, tr("Warning"),
760            tr("Resetting the sampler instance will close\n"
761               "all device and channel configurations.\n\n"
762               "Please note that this operation may cause\n"
763               "temporary MIDI and Audio disruption\n\n"
764               "Do you want to reset the sampler engine now?"),
765            tr("Reset"), tr("Cancel")) > 0)
766            return;
767    
768        // Just do the reset, after closing down current session...
769        if (closeSession(true) && ::lscp_reset_sampler(m_pClient) != LSCP_OK) {
770            appendMessagesClient("lscp_reset_sampler");
771            appendMessagesError(tr("Could not reset sampler instance.\n\nSorry."));
772            return;
773        }
774    
775        // Log this.
776        appendMessages(tr("Sampler reset."));
777    }
778    
779    
780  // Restart the client/server instance.  // Restart the client/server instance.
781  void qsamplerMainForm::fileRestart (void)  void qsamplerMainForm::fileRestart (void)
782  {  {
# Line 670  void qsamplerMainForm::editAddChannel (v Line 824  void qsamplerMainForm::editAddChannel (v
824      if (m_pClient == NULL)      if (m_pClient == NULL)
825          return;          return;
826    
827      // Create the new sampler channel.      // Just create the channel instance...
828      int iChannelID = ::lscp_add_channel(m_pClient);      qsamplerChannel *pChannel = new qsamplerChannel(this);
829      if (iChannelID < 0) {      if (pChannel == NULL)
         appendMessagesClient("lscp_add_channel");  
         appendMessagesError(tr("Could not create the new channel.\n\nSorry."));  
830          return;          return;
     }  
831    
832      // Log this happening.      // Before we show it up, may be we'll
833      appendMessages(tr("Channel %1 created.").arg(iChannelID));      // better ask for some initial values?
834        if (!pChannel->channelSetup(this)) {
835      // Just create the channel strip with given id.          delete pChannel;
836      createChannel(iChannelID, true);          return;
837        }
838        
839        // And give it to the strip (will own the channel instance, if successful).
840        if (!createChannelStrip(pChannel)) {
841            delete pChannel;
842            return;
843        }
844    
845      // We'll be dirty, for sure...      // Make that an overall update.
846      m_iDirtyCount++;      m_iDirtyCount++;
847      // Stabilize form anyway.      m_iChangeCount++;
848      stabilizeForm();      stabilizeForm();
849  }  }
850    
# Line 697  void qsamplerMainForm::editRemoveChannel Line 855  void qsamplerMainForm::editRemoveChannel
855      if (m_pClient == NULL)      if (m_pClient == NULL)
856          return;          return;
857    
858      qsamplerChannelStrip *pChannel = activeChannel();      qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
859        if (pChannelStrip == NULL)
860            return;
861            
862        qsamplerChannel *pChannel = pChannelStrip->channel();
863      if (pChannel == NULL)      if (pChannel == NULL)
864          return;          return;
865    
# Line 707  void qsamplerMainForm::editRemoveChannel Line 869  void qsamplerMainForm::editRemoveChannel
869              tr("About to remove channel:\n\n"              tr("About to remove channel:\n\n"
870                 "%1\n\n"                 "%1\n\n"
871                 "Are you sure?")                 "Are you sure?")
872                 .arg(pChannel->caption()),                 .arg(pChannelStrip->caption()),
873              tr("OK"), tr("Cancel")) > 0)              tr("OK"), tr("Cancel")) > 0)
874              return;              return;
875      }      }
876    
877      // Remove the existing sampler channel.      // Remove the existing sampler channel.
878      if (::lscp_remove_channel(m_pClient, pChannel->channelID()) != LSCP_OK) {      if (!pChannel->removeChannel())
         appendMessagesClient("lscp_remove_channel");  
         appendMessagesError(tr("Could not remove channel.\n\nSorry."));  
879          return;          return;
     }  
880    
     // Log this happening.  
     appendMessages(tr("Channel %1 removed.").arg(pChannel->channelID()));  
       
881      // Just delete the channel strip.      // Just delete the channel strip.
882      delete pChannel;      delete pChannelStrip;
883        
884      // Do we auto-arrange?      // Do we auto-arrange?
885      if (m_pOptions && m_pOptions->bAutoArrange)      if (m_pOptions && m_pOptions->bAutoArrange)
886          channelsArrange();          channelsArrange();
# Line 740  void qsamplerMainForm::editSetupChannel Line 897  void qsamplerMainForm::editSetupChannel
897      if (m_pClient == NULL)      if (m_pClient == NULL)
898          return;          return;
899    
900      qsamplerChannelStrip *pChannel = activeChannel();      qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
901      if (pChannel == NULL)      if (pChannelStrip == NULL)
902          return;          return;
903    
904      // Just invoque the channel strip procedure.      // Just invoque the channel strip procedure.
905      pChannel->channelSetup();      pChannelStrip->channelSetup();
906  }  }
907    
908    
# Line 755  void qsamplerMainForm::editResetChannel Line 912  void qsamplerMainForm::editResetChannel
912      if (m_pClient == NULL)      if (m_pClient == NULL)
913          return;          return;
914    
915      qsamplerChannelStrip *pChannel = activeChannel();      qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
916      if (pChannel == NULL)      if (pChannelStrip == NULL)
917          return;          return;
918    
919      // Remove the existing sampler channel.      qsamplerChannel *pChannel = pChannelStrip->channel();
920      if (::lscp_reset_channel(m_pClient, pChannel->channelID()) != LSCP_OK) {      if (pChannel == NULL)
         appendMessagesClient("lscp_reset_channel");  
         appendMessagesError(tr("Could not reset channel.\n\nSorry."));  
921          return;          return;
     }  
922    
923      // Log this.      // Reset the existing sampler channel.
924      appendMessages(tr("Channel %1 reset.").arg(pChannel->channelID()));      pChannel->resetChannel();
925    
926      // Refresh channel strip info.      // And force a deferred update.
927      pChannel->updateChannelInfo();      m_iChangeCount++;
928  }  }
929    
930    
# Line 831  void qsamplerMainForm::viewOptions (void Line 985  void qsamplerMainForm::viewOptions (void
985      qsamplerOptionsForm *pOptionsForm = new qsamplerOptionsForm(this);      qsamplerOptionsForm *pOptionsForm = new qsamplerOptionsForm(this);
986      if (pOptionsForm) {      if (pOptionsForm) {
987          // Check out some initial nullities(tm)...          // Check out some initial nullities(tm)...
988          qsamplerChannelStrip *pChannel = activeChannel();          qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
989          if (m_pOptions->sDisplayFont.isEmpty() && pChannel)          if (m_pOptions->sDisplayFont.isEmpty() && pChannelStrip)
990              m_pOptions->sDisplayFont = pChannel->displayFont().toString();              m_pOptions->sDisplayFont = pChannelStrip->displayFont().toString();
991          if (m_pOptions->sMessagesFont.isEmpty() && m_pMessages)          if (m_pOptions->sMessagesFont.isEmpty() && m_pMessages)
992              m_pOptions->sMessagesFont = m_pMessages->messagesFont().toString();              m_pOptions->sMessagesFont = m_pMessages->messagesFont().toString();
993          // To track down deferred or immediate changes.          // To track down deferred or immediate changes.
# Line 843  void qsamplerMainForm::viewOptions (void Line 997  void qsamplerMainForm::viewOptions (void
997          bool    bOldServerStart     = m_pOptions->bServerStart;          bool    bOldServerStart     = m_pOptions->bServerStart;
998          QString sOldServerCmdLine   = m_pOptions->sServerCmdLine;          QString sOldServerCmdLine   = m_pOptions->sServerCmdLine;
999          QString sOldDisplayFont     = m_pOptions->sDisplayFont;          QString sOldDisplayFont     = m_pOptions->sDisplayFont;
1000            bool    bOldDisplayEffect   = m_pOptions->bDisplayEffect;
1001          int     iOldMaxVolume       = m_pOptions->iMaxVolume;          int     iOldMaxVolume       = m_pOptions->iMaxVolume;
1002          QString sOldMessagesFont    = m_pOptions->sMessagesFont;          QString sOldMessagesFont    = m_pOptions->sMessagesFont;
1003          bool    bOldStdoutCapture   = m_pOptions->bStdoutCapture;          bool    bOldStdoutCapture   = m_pOptions->bStdoutCapture;
1004          int     bOldMessagesLimit   = m_pOptions->bMessagesLimit;          int     bOldMessagesLimit   = m_pOptions->bMessagesLimit;
1005          int     iOldMessagesLimitLines = m_pOptions->iMessagesLimitLines;          int     iOldMessagesLimitLines = m_pOptions->iMessagesLimitLines;
1006          bool    bOldCompletePath    = m_pOptions->bCompletePath;          bool    bOldCompletePath    = m_pOptions->bCompletePath;
1007            bool    bOldInstrumentNames = m_pOptions->bInstrumentNames;
1008          int     iOldMaxRecentFiles  = m_pOptions->iMaxRecentFiles;          int     iOldMaxRecentFiles  = m_pOptions->iMaxRecentFiles;
1009          // Load the current setup settings.          // Load the current setup settings.
1010          pOptionsForm->setup(m_pOptions);          pOptionsForm->setup(m_pOptions);
# Line 867  void qsamplerMainForm::viewOptions (void Line 1023  void qsamplerMainForm::viewOptions (void
1023                  (!bOldCompletePath &&  m_pOptions->bCompletePath) ||                  (!bOldCompletePath &&  m_pOptions->bCompletePath) ||
1024                  (iOldMaxRecentFiles != m_pOptions->iMaxRecentFiles))                  (iOldMaxRecentFiles != m_pOptions->iMaxRecentFiles))
1025                  updateRecentFilesMenu();                  updateRecentFilesMenu();
1026                if (( bOldInstrumentNames && !m_pOptions->bInstrumentNames) ||
1027                    (!bOldInstrumentNames &&  m_pOptions->bInstrumentNames))
1028                    updateInstrumentNames();
1029                if (( bOldDisplayEffect && !m_pOptions->bDisplayEffect) ||
1030                    (!bOldDisplayEffect &&  m_pOptions->bDisplayEffect))
1031                    updateDisplayEffect();
1032              if (sOldDisplayFont != m_pOptions->sDisplayFont)              if (sOldDisplayFont != m_pOptions->sDisplayFont)
1033                  updateDisplayFont();                  updateDisplayFont();
1034              if (iOldMaxVolume != m_pOptions->iMaxVolume)              if (iOldMaxVolume != m_pOptions->iMaxVolume)
# Line 909  void qsamplerMainForm::channelsArrange ( Line 1071  void qsamplerMainForm::channelsArrange (
1071      m_pWorkspace->setUpdatesEnabled(false);      m_pWorkspace->setUpdatesEnabled(false);
1072      int y = 0;      int y = 0;
1073      for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {      for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1074          qsamplerChannelStrip *pChannel = (qsamplerChannelStrip *) wlist.at(iChannel);          qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1075      /*  if (pChannel->testWState(WState_Maximized | WState_Minimized)) {      /*  if (pChannelStrip->testWState(WState_Maximized | WState_Minimized)) {
1076              // Prevent flicker...              // Prevent flicker...
1077              pChannel->hide();              pChannelStrip->hide();
1078              pChannel->showNormal();              pChannelStrip->showNormal();
1079          }   */          }   */
1080          pChannel->adjustSize();          pChannelStrip->adjustSize();
1081          int iWidth  = m_pWorkspace->width();          int iWidth  = m_pWorkspace->width();
1082          if (iWidth < pChannel->width())          if (iWidth < pChannelStrip->width())
1083              iWidth = pChannel->width();              iWidth = pChannelStrip->width();
1084      //  int iHeight = pChannel->height() + pChannel->parentWidget()->baseSize().height();      //  int iHeight = pChannelStrip->height() + pChannelStrip->parentWidget()->baseSize().height();
1085          int iHeight = pChannel->parentWidget()->frameGeometry().height();          int iHeight = pChannelStrip->parentWidget()->frameGeometry().height();
1086          pChannel->parentWidget()->setGeometry(0, y, iWidth, iHeight);          pChannelStrip->parentWidget()->setGeometry(0, y, iWidth, iHeight);
1087          y += iHeight;          y += iHeight;
1088      }      }
1089      m_pWorkspace->setUpdatesEnabled(true);      m_pWorkspace->setUpdatesEnabled(true);
# Line 969  void qsamplerMainForm::helpAbout (void) Line 1131  void qsamplerMainForm::helpAbout (void)
1131      sText += tr("Debugging option enabled.");      sText += tr("Debugging option enabled.");
1132      sText += "</font></small><br />";      sText += "</font></small><br />";
1133  #endif  #endif
1134    #ifndef CONFIG_LIBGIG
1135        sText += "<small><font color=\"red\">";
1136        sText += tr("GIG (libgig) file support disabled.");
1137        sText += "</font></small><br />";
1138    #endif
1139    #ifndef CONFIG_INSTRUMENT_NAME
1140        sText += "<small><font color=\"red\">";
1141        sText += tr("LSCP (liblscp) instrument_name support disabled.");
1142        sText += "</font></small><br />";
1143    #endif
1144      sText += "<br />\n";      sText += "<br />\n";
1145      sText += tr("Using") + ": ";      sText += tr("Using") + ": ";
1146      sText += ::lscp_client_package();      sText += ::lscp_client_package();
# Line 1002  void qsamplerMainForm::stabilizeForm (vo Line 1174  void qsamplerMainForm::stabilizeForm (vo
1174      setCaption(tr(QSAMPLER_TITLE " - [%1]").arg(sSessioName));      setCaption(tr(QSAMPLER_TITLE " - [%1]").arg(sSessioName));
1175    
1176      // Update the main menu state...      // Update the main menu state...
1177      qsamplerChannelStrip *pChannel = activeChannel();      qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
1178      bool bHasClient  = (m_pOptions != NULL && m_pClient != NULL);      bool bHasClient  = (m_pOptions != NULL && m_pClient != NULL);
1179      bool bHasChannel = (bHasClient && pChannel != NULL);      bool bHasChannel = (bHasClient && pChannelStrip != NULL);
1180      fileNewAction->setEnabled(bHasClient);      fileNewAction->setEnabled(bHasClient);
1181      fileOpenAction->setEnabled(bHasClient);      fileOpenAction->setEnabled(bHasClient);
1182      fileSaveAction->setEnabled(bHasClient && m_iDirtyCount > 0);      fileSaveAction->setEnabled(bHasClient && m_iDirtyCount > 0);
1183      fileSaveAsAction->setEnabled(bHasClient);      fileSaveAsAction->setEnabled(bHasClient);
1184        fileResetAction->setEnabled(bHasClient);
1185      fileRestartAction->setEnabled(bHasClient || m_pServer == NULL);      fileRestartAction->setEnabled(bHasClient || m_pServer == NULL);
1186      editAddChannelAction->setEnabled(bHasClient);      editAddChannelAction->setEnabled(bHasClient);
1187      editRemoveChannelAction->setEnabled(bHasChannel);      editRemoveChannelAction->setEnabled(bHasChannel);
# Line 1027  void qsamplerMainForm::stabilizeForm (vo Line 1200  void qsamplerMainForm::stabilizeForm (vo
1200      }      }
1201      // Channel status...      // Channel status...
1202      if (bHasChannel)      if (bHasChannel)
1203          m_status[QSAMPLER_STATUS_CHANNEL]->setText(pChannel->caption());          m_status[QSAMPLER_STATUS_CHANNEL]->setText(pChannelStrip->caption());
1204      else      else
1205          m_status[QSAMPLER_STATUS_CHANNEL]->clear();          m_status[QSAMPLER_STATUS_CHANNEL]->clear();
1206      // Session status...      // Session status...
# Line 1046  void qsamplerMainForm::stabilizeForm (vo Line 1219  void qsamplerMainForm::stabilizeForm (vo
1219    
1220    
1221  // Channel change receiver slot.  // Channel change receiver slot.
1222  void qsamplerMainForm::channelChanged( qsamplerChannelStrip * )  void qsamplerMainForm::channelStripChanged( qsamplerChannelStrip * )
1223  {  {
1224        // Flag that we're update those channel strips.
1225        m_iChangeCount++;
1226      // Just mark the dirty form.      // Just mark the dirty form.
1227      m_iDirtyCount++;      m_iDirtyCount++;
1228      // and update the form status...      // and update the form status...
# Line 1099  void qsamplerMainForm::updateRecentFiles Line 1274  void qsamplerMainForm::updateRecentFiles
1274  }  }
1275    
1276    
1277    // Force update of the channels instrument names mode.
1278    void qsamplerMainForm::updateInstrumentNames (void)
1279    {
1280        // Full channel list update...
1281        QWidgetList wlist = m_pWorkspace->windowList();
1282        if (wlist.isEmpty())
1283            return;
1284    
1285        m_pWorkspace->setUpdatesEnabled(false);
1286        for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1287            qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1288            if (pChannelStrip)
1289                pChannelStrip->updateInstrumentName(true);
1290        }
1291        m_pWorkspace->setUpdatesEnabled(true);
1292    }
1293    
1294    
1295  // Force update of the channels display font.  // Force update of the channels display font.
1296  void qsamplerMainForm::updateDisplayFont (void)  void qsamplerMainForm::updateDisplayFont (void)
1297  {  {
# Line 1120  void qsamplerMainForm::updateDisplayFont Line 1313  void qsamplerMainForm::updateDisplayFont
1313    
1314      m_pWorkspace->setUpdatesEnabled(false);      m_pWorkspace->setUpdatesEnabled(false);
1315      for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {      for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1316          qsamplerChannelStrip *pChannel = (qsamplerChannelStrip *) wlist.at(iChannel);          qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1317          pChannel->setDisplayFont(font);          if (pChannelStrip)
1318                pChannelStrip->setDisplayFont(font);
1319        }
1320        m_pWorkspace->setUpdatesEnabled(true);
1321    }
1322    
1323    
1324    // Update channel strips background effect.
1325    void qsamplerMainForm::updateDisplayEffect (void)
1326    {
1327       QPixmap pm;
1328        if (m_pOptions->bDisplayEffect)
1329            pm = QPixmap::fromMimeSource("displaybg1.png");
1330    
1331        // Full channel list update...
1332        QWidgetList wlist = m_pWorkspace->windowList();
1333        if (wlist.isEmpty())
1334            return;
1335    
1336        m_pWorkspace->setUpdatesEnabled(false);
1337        for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1338            qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1339            if (pChannelStrip)
1340                pChannelStrip->setDisplayBackground(pm);
1341      }      }
1342      m_pWorkspace->setUpdatesEnabled(true);      m_pWorkspace->setUpdatesEnabled(true);
1343  }  }
# Line 1140  void qsamplerMainForm::updateMaxVolume ( Line 1356  void qsamplerMainForm::updateMaxVolume (
1356    
1357      m_pWorkspace->setUpdatesEnabled(false);      m_pWorkspace->setUpdatesEnabled(false);
1358      for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {      for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1359          qsamplerChannelStrip *pChannel = (qsamplerChannelStrip *) wlist.at(iChannel);          qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1360          pChannel->setMaxVolume(m_pOptions->iMaxVolume);          if (pChannelStrip)
1361                pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume);
1362      }      }
1363      m_pWorkspace->setUpdatesEnabled(true);      m_pWorkspace->setUpdatesEnabled(true);
1364  }  }
# Line 1240  void qsamplerMainForm::updateMessagesCap Line 1457  void qsamplerMainForm::updateMessagesCap
1457  // qsamplerMainForm -- MDI channel strip management.  // qsamplerMainForm -- MDI channel strip management.
1458    
1459  // The channel strip creation executive.  // The channel strip creation executive.
1460  void qsamplerMainForm::createChannel ( int iChannelID, bool bPrompt )  qsamplerChannelStrip *qsamplerMainForm::createChannelStrip ( qsamplerChannel *pChannel )
1461  {  {
1462      if (m_pClient == NULL)      if (m_pClient == NULL || pChannel == NULL)
1463          return;          return NULL;
1464    
1465      // Prepare for auto-arrange?      // Prepare for auto-arrange?
1466      qsamplerChannelStrip *pChannel = NULL;      qsamplerChannelStrip *pChannelStrip = NULL;
1467      int y = 0;      int y = 0;
1468      if (m_pOptions && m_pOptions->bAutoArrange) {      if (m_pOptions && m_pOptions->bAutoArrange) {
1469          QWidgetList wlist = m_pWorkspace->windowList();          QWidgetList wlist = m_pWorkspace->windowList();
1470          for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {          for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1471              pChannel = (qsamplerChannelStrip *) wlist.at(iChannel);              pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1472          //  y += pChannel->height() + pChannel->parentWidget()->baseSize().height();          //  y += pChannelStrip->height() + pChannelStrip->parentWidget()->baseSize().height();
1473              y += pChannel->parentWidget()->frameGeometry().height();              y += pChannelStrip->parentWidget()->frameGeometry().height();
1474          }          }
1475      }      }
1476    
1477      // Add a new channel itema...      // Add a new channel itema...
1478      WFlags wflags = Qt::WStyle_Customize | Qt::WStyle_Tool | Qt::WStyle_Title | Qt::WStyle_NoBorder;      WFlags wflags = Qt::WStyle_Customize | Qt::WStyle_Tool | Qt::WStyle_Title | Qt::WStyle_NoBorder;
1479      pChannel = new qsamplerChannelStrip(m_pWorkspace, 0, wflags);      pChannelStrip = new qsamplerChannelStrip(m_pWorkspace, 0, wflags);
1480      pChannel->setMaxVolume(m_pOptions->iMaxVolume);      if (pChannelStrip == NULL)
1481      pChannel->setup(this, iChannelID);          return NULL;
1482      // We'll need a display font.          
1483      QFont font;      // Actual channel strip setup...
1484      if (m_pOptions && font.fromString(m_pOptions->sDisplayFont))      pChannelStrip->setup(pChannel);
1485          pChannel->setDisplayFont(font);      QObject::connect(pChannelStrip, SIGNAL(channelChanged(qsamplerChannelStrip *)), this, SLOT(channelStripChanged(qsamplerChannelStrip *)));
1486      // Track channel setup changes.      // Set some initial aesthetic options...
1487      QObject::connect(pChannel, SIGNAL(channelChanged(qsamplerChannelStrip *)), this, SLOT(channelChanged(qsamplerChannelStrip *)));      if (m_pOptions) {
1488      // Before we show it up, may be we'll          // Background display effect...
1489      // better ask for some initial values?          pChannelStrip->setDisplayEffect(m_pOptions->bDisplayEffect);
1490      if (bPrompt)          // We'll need a display font.
1491          pChannel->channelSetup();                QFont font;
1492            if (font.fromString(m_pOptions->sDisplayFont))
1493                pChannelStrip->setDisplayFont(font);
1494            // Maximum allowed volume setting.
1495            pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume);
1496        }
1497    
1498      // Now we show up us to the world.      // Now we show up us to the world.
1499      pChannel->show();      pChannelStrip->show();
1500      // Only then, we'll auto-arrange...      // Only then, we'll auto-arrange...
1501      if (m_pOptions && m_pOptions->bAutoArrange) {      if (m_pOptions && m_pOptions->bAutoArrange) {
1502          int iWidth  = m_pWorkspace->width();          int iWidth  = m_pWorkspace->width();
1503      //  int iHeight = pChannel->height() + pChannel->parentWidget()->baseSize().height();      //  int iHeight = pChannel->height() + pChannel->parentWidget()->baseSize().height();
1504          int iHeight = pChannel->parentWidget()->frameGeometry().height();          int iHeight = pChannelStrip->parentWidget()->frameGeometry().height();
1505          pChannel->parentWidget()->setGeometry(0, y, iWidth, iHeight);          pChannelStrip->parentWidget()->setGeometry(0, y, iWidth, iHeight);
1506      }      }
1507        
1508        // Return our successful reference...
1509        return pChannelStrip;
1510  }  }
1511    
1512    
1513  // Retrieve the active channel strip.  // Retrieve the active channel strip.
1514  qsamplerChannelStrip *qsamplerMainForm::activeChannel (void)  qsamplerChannelStrip *qsamplerMainForm::activeChannelStrip (void)
1515  {  {
1516      return (qsamplerChannelStrip *) m_pWorkspace->activeWindow();      return (qsamplerChannelStrip *) m_pWorkspace->activeWindow();
1517  }  }
1518    
1519    
1520  // Retrieve a channel strip by index.  // Retrieve a channel strip by index.
1521  qsamplerChannelStrip *qsamplerMainForm::channelAt ( int iChannel )  qsamplerChannelStrip *qsamplerMainForm::channelStripAt ( int iChannel )
1522  {  {
1523      QWidgetList wlist = m_pWorkspace->windowList();      QWidgetList wlist = m_pWorkspace->windowList();
1524      if (wlist.isEmpty())      if (wlist.isEmpty())
# Line 1313  void qsamplerMainForm::channelsMenuAbout Line 1539  void qsamplerMainForm::channelsMenuAbout
1539      if (!wlist.isEmpty()) {      if (!wlist.isEmpty()) {
1540          channelsMenu->insertSeparator();          channelsMenu->insertSeparator();
1541          for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {          for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1542              qsamplerChannelStrip *pChannel = (qsamplerChannelStrip *) wlist.at(iChannel);              qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1543              int iItemID = channelsMenu->insertItem(pChannel->caption(), this, SLOT(channelsMenuActivated(int)));              if (pChannelStrip) {
1544              channelsMenu->setItemParameter(iItemID, iChannel);                  int iItemID = channelsMenu->insertItem(pChannelStrip->caption(), this, SLOT(channelsMenuActivated(int)));
1545              channelsMenu->setItemChecked(iItemID, activeChannel() == pChannel);                  channelsMenu->setItemParameter(iItemID, iChannel);
1546                    channelsMenu->setItemChecked(iItemID, activeChannelStrip() == pChannelStrip);
1547                }
1548          }          }
1549      }      }
1550  }  }
# Line 1325  void qsamplerMainForm::channelsMenuAbout Line 1553  void qsamplerMainForm::channelsMenuAbout
1553  // Windows menu activation slot  // Windows menu activation slot
1554  void qsamplerMainForm::channelsMenuActivated ( int iChannel )  void qsamplerMainForm::channelsMenuActivated ( int iChannel )
1555  {  {
1556      qsamplerChannelStrip *pChannel = channelAt(iChannel);      qsamplerChannelStrip *pChannelStrip = channelStripAt(iChannel);
1557      if (pChannel)      if (pChannelStrip)
1558          pChannel->showNormal();          pChannelStrip->showNormal();
1559      pChannel->setFocus();      pChannelStrip->setFocus();
1560  }  }
1561    
1562    
# Line 1368  void qsamplerMainForm::timerSlot (void) Line 1596  void qsamplerMainForm::timerSlot (void)
1596      }      }
1597            
1598          // Refresh each channel usage, on each period...          // Refresh each channel usage, on each period...
1599      if (m_pClient && m_pOptions->bAutoRefresh && m_pWorkspace->isUpdatesEnabled()) {      if (m_pClient && (m_iChangeCount > 0 || m_pOptions->bAutoRefresh)) {
1600          m_iTimerSlot += QSAMPLER_TIMER_MSECS;          m_iTimerSlot += QSAMPLER_TIMER_MSECS;
1601          if (m_iTimerSlot >= m_pOptions->iAutoRefreshTime)  {          if (m_iTimerSlot >= m_pOptions->iAutoRefreshTime && m_pWorkspace->isUpdatesEnabled())  {
1602              m_iTimerSlot = 0;              m_iTimerSlot = 0;
1603                m_iChangeCount = 0;
1604              QWidgetList wlist = m_pWorkspace->windowList();              QWidgetList wlist = m_pWorkspace->windowList();
1605              for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {              for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1606                  qsamplerChannelStrip *pChannel = (qsamplerChannelStrip *) wlist.at(iChannel);                  qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1607                  if (pChannel->isVisible())                  if (pChannelStrip && pChannelStrip->isVisible()) {
1608                      pChannel->updateChannelUsage();                      // If we can't make it clean, try next time.
1609                        if (!pChannelStrip->updateChannelUsage())
1610                            m_iChangeCount++;
1611                    }
1612              }              }
1613          }          }
1614      }      }
# Line 1513  void qsamplerMainForm::processServerExit Line 1745  void qsamplerMainForm::processServerExit
1745  //-------------------------------------------------------------------------  //-------------------------------------------------------------------------
1746  // qsamplerMainForm -- Client stuff.  // qsamplerMainForm -- Client stuff.
1747    
   
1748  // The LSCP client callback procedure.  // The LSCP client callback procedure.
1749  lscp_status_t qsampler_client_callback ( lscp_client_t *pClient, const char *pchBuffer, int cchBuffer, void *pvData )  lscp_status_t qsampler_client_callback ( lscp_client_t */*pClient*/, lscp_event_t event, const char *pchData, int cchData, void *pvData )
1750  {  {
1751      qsamplerMainForm *pMainForm = (qsamplerMainForm *) pvData;      qsamplerMainForm *pMainForm = (qsamplerMainForm *) pvData;
1752      if (pMainForm == NULL)      if (pMainForm == NULL)
1753          return LSCP_FAILED;          return LSCP_FAILED;
1754    
1755      char *pszBuffer = (char *) malloc(cchBuffer + 1);      // ATTN: DO NOT EVER call any GUI code here,
1756      if (pszBuffer == NULL)      // as this is run under some other thread context.
1757          return LSCP_FAILED;      // A custom event must be posted here...
1758        QApplication::postEvent(pMainForm, new qsamplerCustomEvent(event, pchData, cchData));
     memcpy(pszBuffer, pchBuffer, cchBuffer);  
     pszBuffer[cchBuffer] = (char) 0;  
     pMainForm->appendMessagesColor(pszBuffer, "#996699");  
     free(pszBuffer);  
1759    
1760      return LSCP_OK;      return LSCP_OK;
1761  }  }

Legend:
Removed from v.122  
changed lines
  Added in v.391

  ViewVC Help
Powered by ViewVC