/* randysnd.cpp */

// RandySnd is (C) 1992 Anthony McCarthy
//
// Feel free to use and abuse this program to your own ends.
// However, it is provided FREE, AS-IS and with absolutely NO
// WARRANTY whatsoever. Use it at your own risk.
//
// The above statement is especially appropriate as this is
// my very first program built using MSC++ 7 and Microsoft's
// Foundation Classes. As yet I haven't really got my head
// around how to play with precompiled headers. Also, I ain't
// yet played with the diagnostic services provided by MFC
// and so there may well be memory leaks that I haven't seen
// yet.
//
// Please forgive the proliferation of literal integers and
// strings in the code - this code was a quick hack - they
// should all really be symbolic at least.  Also, I haven't yet
// developed/decided on a personal coding style for C++ so 
// things might feel inconsistent.
//
// If you really like this program, a donation of a few pounds,
// dollars, roubles, francs or whatever would be appreciated, to:
// Anthony McCarthy, 14 Beryl Road, Bedminster, Bristol, BS3 3DH, UK
//
// Having said there's no warranty, please report any bugs, enhancement
// requests or source code criticisms to:
//   100012.3712@compuserve.com   
//   amccarthy@cix.compulink.co.uk
//   amc@beryl.demon.co.uk


// CAVEAT: if you're trying to recompile this source with the same 
// release of MSC 7.0 as I'm using, the compile will fail. Microsoft 
// failed to include the AfxSig_vh enum in afxmsg.h although it is 
// referenced in the ON_WM_DROPFILES() macro in the same file!  To work 
// around this, I simply changed 'AfxSig_vh' to 'AfxSig_vw' in a copy 
// of afxmsg.h.


// Change History
// ~~~~~~~~~~~~~~
//
// v1.00 : AMcC : 10June92 : initial creation



# include <afxwin.h>
# include <afxcoll.h>
# include <mmsystem.h>
# include <commdlg.h>
# include <shellapi.h>
# include <stdlib.h>
# include "randysnd.h"



static char scratchBuffer[512];



/*--------------------------------------------------------------------------*/
/*                                                                          */
/*                          C M a i n W i n d o w                           */
/*                                                                          */
/*--------------------------------------------------------------------------*/

class CMainDialog : public CModalDialog
{
 private:


   CPtrList soundsList;

   BOOL bFilesChanged,
        bAutoRandomiseChanged;

   CString curSysSound;

   WORD templateId;

 public:
   static const char * iniFileName;

   CMainDialog(WORD dlgId) : CModalDialog(dlgId) { bFilesChanged = FALSE; 
                                                   bAutoRandomiseChanged = FALSE;
                                                   templateId = dlgId;
                                                 }
   ~CMainDialog();

   BOOL OnInitDialog();

   BOOL GetIniString(CString section, CString key, CString& value);
   BOOL LoadWavFilesList(const CString & sysSound);
   BOOL SaveWavFilesList(const CString & sysSound);
   BOOL FlushWavFilesList();
   BOOL GetSysSound(CString& sysSound);
   BOOL TryToRandomizeSysSound(CString sysSound);

   afx_msg void OnAbout();
   afx_msg void OnDestroy();
   afx_msg void OnSysSndSelChange();

   afx_msg void OnFileSelChange();
   afx_msg void OnPlaySound();
   afx_msg void OnAddSoundFile();
   afx_msg void OnRemoveSoundFile();
   afx_msg void OnAutoRandomise();
   afx_msg void OnRandomiseNow();
   afx_msg void OnDropFiles(HDROP hDrop);

   DECLARE_MESSAGE_MAP()

};


/*--------------------------------------------------------------------------*/
/*                                                                          */
/*                               C M y A p p                                */
/*                                                                          */
/*--------------------------------------------------------------------------*/

class CMyApp : public CWinApp
{
 public:
    int  Run();
    BOOL WantedMinimize();
};


BOOL CMyApp::WantedMinimize()
{
 return (    m_nCmdShow == SW_SHOWMINIMIZED
          || m_nCmdShow == SW_MINIMIZE
          || m_nCmdShow == SW_SHOWMINNOACTIVE );
}


int CMyApp::Run()
{
 WORD dlgId;

 if (WantedMinimize())
    dlgId = IDD_WORKINPROGRESS;
 else
    dlgId = IDD_MAIN;

 CMainDialog mainDialog(dlgId);

 mainDialog.DoModal();

 return 0;
}



// Static application object... the constructor for
// this kicks everything off. 
//
// This comment is here because even though I find the 
// concept of static intializers simple enough to comprehend,
// its still damned difficult to see where the app 'gets going'.

CMyApp myApp;




const char * CMainDialog::iniFileName = "randysnd.ini";

BEGIN_MESSAGE_MAP( CMainDialog, CModalDialog )
	ON_COMMAND( ID_ABOUT          , OnAbout           )
    ON_COMMAND( ID_PLAYSOUNDFILE  , OnPlaySound       )
    ON_COMMAND( ID_ADDSOUNDFILE   , OnAddSoundFile    )
    ON_COMMAND( ID_REMOVESOUNDFILE, OnRemoveSoundFile )
    ON_COMMAND( ID_AUTORANDOMISE  , OnAutoRandomise   )
    ON_COMMAND( ID_RANDOMIZENOW   , OnRandomiseNow    )

    ON_WM_DESTROY( )
    ON_WM_DROPFILES( ) // *SIGH* MS omitted the AfxSig_vh enum from afxmsg.h!
                       // To work round this, I modified the ON_WM_DROPFILES()
                       // macro (in afxmsg.h) to use the AfxSig_vw signature.

    ON_CBN_SELCHANGE( ID_SYSTEMSOUNDS, OnSysSndSelChange )
    ON_LBN_SELCHANGE( ID_FILESLIST   , OnFileSelChange )
END_MESSAGE_MAP()


CMainDialog::~CMainDialog()
{
 CString * pCstr;

 while (     ! soundsList.IsEmpty() 
         &&  (pCstr = (CString *)soundsList.RemoveHead()) != NULL)
       delete pCstr;
}


BOOL CMainDialog::GetSysSound(CString& sysSound)
{
 BOOL ok = FALSE;
 CComboBox * cb = (CComboBox *)GetDlgItem(ID_SYSTEMSOUNDS);
 CString * s = (CString *)(soundsList.GetAt(soundsList.FindIndex(cb->GetCurSel())));


 if (! soundsList.IsEmpty())
    {
     sysSound = *s;
     ok = TRUE;
    }

 return ok;
}


BOOL CMainDialog::GetIniString(CString section, CString key, CString& value)
{
 BOOL ok;

 ok = GetPrivateProfileString( section, 
                               key, 
                               "", 
                               value.GetBuffer(256), 
                               256, 
                               iniFileName ) > 0;

 value.ReleaseBuffer();

 return ok;
}


BOOL CMainDialog::FlushWavFilesList()
{
 BOOL ok = TRUE;

 if (bFilesChanged  ||  bAutoRandomiseChanged)
    if (! curSysSound.IsEmpty())
       SaveWavFilesList(curSysSound);

 return ok;
}


BOOL CMainDialog::LoadWavFilesList(const CString& sysSound)
{
 BOOL ok = TRUE;
 char str[20];
 int ctr;
 CListBox * lb = (CListBox *)GetDlgItem(ID_FILESLIST);
 CButton * but = (CButton *)GetDlgItem(ID_AUTORANDOMISE);
 CString wavName;


 FlushWavFilesList();


 lb->ResetContent();

 int qty = GetPrivateProfileInt(sysSound, "FileCount", 0, iniFileName);

 for ( ctr  = 1;
       ctr <= qty;
       ctr++ )
     {
      wsprintf(str, "SoundFile%d", ctr);

      if (GetIniString(sysSound, str, wavName))
         lb->AddString(wavName);
     }


 but->SetCheck(GetPrivateProfileInt(sysSound, "Randomize", 0, iniFileName));


 curSysSound = sysSound;

 bFilesChanged = FALSE;
 bAutoRandomiseChanged = FALSE;

 return ok;
}


BOOL CMainDialog::SaveWavFilesList(const CString & sysSound)
{
 BOOL ok = TRUE;
 int qty = 0;
 int count = 0;
 CListBox * lb = (CListBox *)GetDlgItem(ID_FILESLIST);
 char key[30];
 char value[10];
 CString filename;
 CButton * but = (CButton *)GetDlgItem(ID_AUTORANDOMISE);

 qty = lb->GetCount();

 if (qty == LB_ERR)
    qty = 0;

 for ( count  = 1;
       count <= qty;
       count++ )
     {
      wsprintf(key, "SoundFile%d", count);

      lb->GetText(count - 1, filename);

      WritePrivateProfileString(sysSound, key, filename, iniFileName);
     }

 wsprintf(value, "%d", qty);
 WritePrivateProfileString(sysSound, "FileCount", value, iniFileName);

 wsprintf(value, "%d", but->GetCheck() ? 1 : 0);
 WritePrivateProfileString(sysSound, "Randomize", value, iniFileName);




 bFilesChanged = FALSE;
 bAutoRandomiseChanged = FALSE;

 return ok;
}


void CMainDialog::OnDestroy()
{
 FlushWavFilesList();
}


void CMainDialog::OnDropFiles(HDROP hDrop)
{
 CString sysSound;
 char buffer[129];
 WORD qty;
 CListBox * lb = (CListBox *)GetDlgItem(ID_FILESLIST);


 if (GetSysSound(sysSound))
    {
     qty = DragQueryFile(hDrop, -1, NULL, 0);

     while (qty-- > 0)
           {
            DragQueryFile(hDrop, qty, buffer, sizeof(buffer));

            if (buffer[0])
               if (lb->AddString(buffer) != LB_ERR)
                  bFilesChanged = TRUE;
           }
    }

 DragFinish(hDrop);
}


void CMainDialog::OnAutoRandomise()
{
 CButton * but = (CButton *)GetDlgItem(ID_AUTORANDOMISE);

 bAutoRandomiseChanged = TRUE;
}


BOOL CMainDialog::TryToRandomizeSysSound(CString sysSound)
{
 BOOL randomized = FALSE;
 int files;
 int randomFile;
 char filename[129];
 char key[10];
 LPSTR strptr;
 char txt[150];

 if (GetPrivateProfileInt(sysSound, "Randomize", 0, iniFileName))
    if (files = GetPrivateProfileInt(sysSound, "FileCount", 0, iniFileName))
       {
        randomFile = ((WORD)rand() % files) + 1;
       
        wsprintf(key, "SoundFile%d", randomFile);
        if (GetPrivateProfileString(sysSound, key, "", filename, sizeof(filename), iniFileName))
           {
            GetProfileString("sounds", sysSound, "", scratchBuffer, sizeof(scratchBuffer));
       
            strptr = scratchBuffer;
            while (*strptr && *strptr != ',')
                  strptr++;
       
            if (*strptr == ',')
               {
                lstrcpy(txt, filename);
                lstrcat(txt, strptr);
       
                WriteProfileString("sounds", sysSound, txt);
       
                randomized = TRUE;
               }
           }
       }

 return randomized;
}


void CMainDialog::OnRandomiseNow()
{
 static char buffer[512];
 LPSTR p;
 BOOL winIniChanged = FALSE;

 FlushWavFilesList();

 if (GetProfileString( "sounds", NULL, "", buffer, sizeof(buffer)))
    {
     p = buffer;

     while (*p)
           {
            if (TryToRandomizeSysSound(p))
               winIniChanged = TRUE;

            p += lstrlen(p) + 1;
           }

     if (winIniChanged)
        ::SendMessage((HWND)0xFFFF, WM_WININICHANGE, 0, (LONG)(LPSTR)"sounds");
    }


 if (myApp.WantedMinimize())
    {
     DWORD oldtime = GetCurrentTime(),
           newtime;
     MSG msg;

     while (oldtime <= (newtime = GetCurrentTime()))
           if (newtime > oldtime + 5000L)
              break;
           else
              if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
                 if (! IsDialogMessage(&msg))
                    CDialog::PreTranslateMessage(&msg);

     EndDialog(0);
    }
}


void CMainDialog::OnFileSelChange()
{
 CListBox * lb = (CListBox *)GetDlgItem(ID_FILESLIST);
 CButton * but = (CButton *)GetDlgItem(ID_PLAYSOUNDFILE);

 but->EnableWindow(lb->GetCurSel() != LB_ERR);
}


void CMainDialog::OnPlaySound()
{
 CListBox * lb = (CListBox *)GetDlgItem(ID_FILESLIST);
 CString txt;
 int curSel = lb->GetCurSel();

 if (curSel != LB_ERR)
    {
     lb->GetText(curSel, txt);
     sndPlaySound(txt, SND_ASYNC | SND_NODEFAULT);
    }
}


void CMainDialog::OnRemoveSoundFile()
{
 CListBox * lb = (CListBox *)GetDlgItem(ID_FILESLIST);
 CString txt;
 int curSel = lb->GetCurSel();
 int count;

 if (curSel != LB_ERR)
    {
     lb->DeleteString(curSel);

     count = lb->GetCount();

     if (count != LB_ERR  &&  count > 0)
        {
         if (curSel >= count)
            curSel = count - 1;

         lb->SetCurSel(curSel);
        }

     bFilesChanged = TRUE;
    }
}


void CMainDialog::OnAddSoundFile()
{
 CListBox * lb = (CListBox *)GetDlgItem(ID_FILESLIST);
 CString txt;
 int curSel = lb->GetCurSel();
 OPENFILENAME o;
 char buffer[129];

 lstrcpy(buffer, "*.wav");

 memset(&o, 0, sizeof(o));

 o.lStructSize  = sizeof(o);
 o.hwndOwner    = GetSafeHwnd();
 o.lpstrFilter  = "Sound Files (*.wav)\0*.wav\0All Files (*.*)\0*.*\0\0";
 o.nFilterIndex = 1;
 o.lpstrFile    = buffer;
 o.nMaxFile     = sizeof(buffer);
 o.lpstrTitle   = "Add Sound File";
 o.Flags        = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
 
 if (GetOpenFileName(&o))
    {
     if (lb->AddString(o.lpstrFile) != LB_ERR)
        bFilesChanged = TRUE;
    }
}


void CMainDialog::OnSysSndSelChange()
{
 CComboBox * cb = (CComboBox *)GetDlgItem(ID_SYSTEMSOUNDS);

 CString * s = (CString *)(soundsList.GetAt(soundsList.FindIndex(cb->GetCurSel())));

 LoadWavFilesList(*s);

 curSysSound = *s;
}

void CMainDialog::OnAbout()
{
 MessageBox( "RandySnd is © Anthony McCarthy, 1992.\n\n"
             "Feel free to use the program and/or play with the source for your "
             "personal enjoyment. However, re-sale of the executable or use of "
             "the source in a commercial "
             "environment (that is, if you're making any money out of my efforts) "
             "is not permitted without my express permission.\n"
             "\n"
             "This program is free and so is provided with NO WARRANTY WHATSOEVER. "
             "If you're worried about it failing in any way, please delete it now.\n\n"
             "However, I'll gladly accept flames regarding bugs, absent features "
             "and my coding style!\n"
             "\n"
             "Enquiries to:\n"
             "\t100012.3712@compuserve.com\n"
             "\tamccarthy@cix.compulink.co.uk\n"
             "\tamc@beryl.demon.co.uk\n"
             "\n"
             "Oh, and if you really, really like the program, feel free "
             "to send a little donation!",
             "About RandySnd",
             MB_OK | MB_ICONASTERISK );
}



BOOL CMainDialog::OnInitDialog()
{
 LPSTR p;
 CComboBox * cb = (CComboBox *)GetDlgItem(ID_SYSTEMSOUNDS);
 char str[150];
 LPSTR strptr;

 srand((WORD)GetCurrentTime());

 rand(); // I've seen reports of MS' rand() routine starting off
 rand(); // rather badly. So I call it a few times to get it going...
 rand(); // (I haven't analysed it myself but this doesn't hurt, so
         // what the heck...)


 if (templateId == IDD_MAIN)
    {
     GetProfileString( "sounds", NULL, "", scratchBuffer, sizeof(scratchBuffer));
     
     p = scratchBuffer;
     
     while (*p)
           {
            GetProfileString("sounds", p, "", str, sizeof(str));
     
            strptr = str;
            while (*strptr && *strptr != ',')
                  strptr++;
     
            if (*strptr)
               {
                cb->InsertString(-1, strptr + 1 /* skip comma */);
                soundsList.AddTail(new CString(p)); 
               }
     
            p += lstrlen(p) + 1;
           }
     
     cb->SetCurSel(0);
     cb->SetFocus();
     
     DragAcceptFiles(GetSafeHwnd(), TRUE);
    }


 // Kick off the randomizing action every time the
 // program runs "minimized" (but not when normal size)

 if (myApp.WantedMinimize())
    PostMessage( WM_COMMAND, 
                 ID_RANDOMIZENOW
               );


 return TRUE;
}




/* eof: randysnd.cpp */

