//---------------------------------------------------------------------------
//
//      File: PICKICON.CPP
//
//      Support code for the Change Icon dialog.
//
//---------------------------------------------------------------------------

#include <windows.h>
#include <windowsx.h>
#include "rc.h"
#include "pickicon.h"

#define MAX_ICONS   500             // that is a lot 'o icons

#define ARRAYSIZE(s)                (sizeof(s) / sizeof((s)[0]))

typedef struct
{
        LPSTR pszIconPath;              // input/output
        int cbIconPath;                 // input
        int iIconIndex;             // input/output
        // private state variables
        HWND hDlg;
        BOOL fFirstPass;
        char szPathField[MAX_PATH];
        char szBuffer[MAX_PATH];
} PICKICON_DATA, FAR *LPPICKICON_DATA;

extern HINSTANCE g_hInst;

// Checks if the file exists, if it doesn't it tries tagging on .exe and if that
// fails it reports an error. The given path is environment expanded.  If it needs
// to put up an error box, it changes the cursor back.  Path's assumed to be
// MAXITEMPATHLEN long.  The main reason for moving this out of the DlgProc was
// because we're running out of stack space on the call to the comm dlg.
BOOL NEAR PASCAL IconFileExists( LPPICKICON_DATA lppid )
{
TCHAR szTitle[128];
TCHAR szInvPath[ 64 ];
TCHAR szText[MAX_PATH+40];
TCHAR szPath[MAX_PATH];
LPTSTR psz;
DWORD dwRet;

        if( lppid->szBuffer[0] == 0 )
                return FALSE;

        // Use the Win32 version instead of the shell version.  The shell version
        // is/was really only there for 16-bit apps.  (RickTu)
        //
        // DoEnvironmentSubst( lppid->szBuffer, sizeof(lppid->szBuffer) );
        //

        dwRet = ExpandEnvironmentStrings( lppid->szBuffer, szPath, MAX_PATH );
        if (dwRet > 0 && dwRet <= MAX_PATH)
        {
            lstrcpy( lppid->szBuffer, szPath );
        }

//      PathUnquoteSpaces( lppid->szBuffer );   // JER

//      if( PathResolve( lppid->szBuffer, NULL, PRF_VERIFYEXISTS | PRF_TRYPROGRAMEXTENSIONS ) ) // JER
        if (SearchPath(NULL, lppid->szBuffer, NULL, ARRAYSIZE(szPath), szPath, &psz) != 0)
                return TRUE;

        LoadString( g_hInst, IDS_BADPATHMSG, szTitle, ARRAYSIZE(szTitle) );
        LoadString( g_hInst, IDS_INVALIDPATH, szInvPath, ARRAYSIZE(szInvPath) );
        wsprintf( szText, szTitle, lppid->szBuffer );
        GetWindowText( lppid->hDlg, szTitle, sizeof(szTitle) );
        lstrcat( szTitle, szInvPath );
        MessageBox( GetDesktopWindow(), szText, szTitle , MB_OK | MB_ICONEXCLAMATION );
        return FALSE;
}

void NEAR PASCAL PutIconsInList( LPPICKICON_DATA lppid )
{
HICON  *rgIcons;
int      iTempIcon;
int  cIcons;
HWND hDlg = lppid->hDlg;
//HCURSOR hOldCursor;

        SendDlgItemMessage( hDlg, IDD_ICON, LB_RESETCONTENT, 0, 0L );

        GetDlgItemText( hDlg, IDD_PATH, lppid->szPathField, sizeof(lppid->szPathField) );

        lstrcpy( lppid->szBuffer, lppid->szPathField );

        if( !IconFileExists(lppid) )
        {
                if( lppid->fFirstPass )
                {
                        // Icon File doesn't exist, use progman
                        lppid->fFirstPass = FALSE;  // Only do this bit once.
                        GetModuleFileName( g_hInst, lppid->szBuffer, ARRAYSIZE(lppid->szBuffer) );
                }
                else
                {
                        return;
                }
        }
        lstrcpy( lppid->szPathField, lppid->szBuffer );
        SetDlgItemText( hDlg, IDD_PATH, lppid->szPathField );

//      hOldCursor = SetCursor( LoadCursor( NULL, IDC_WAIT ) );

        rgIcons = (HICON *)LocalAlloc(LPTR, MAX_ICONS*sizeof(HICON));

        if( rgIcons != NULL )
                cIcons = (int)ExtractIconEx( lppid->szBuffer, 0, rgIcons, NULL, MAX_ICONS );
        else
                cIcons = 0;

//      SetCursor( hOldCursor );
    if( !cIcons )
    {
        char szText[256];
        char szTitle[40];

                GetWindowText( lppid->hDlg, szTitle, sizeof(szTitle) );
                if( lppid->fFirstPass )
                {
                        lppid->fFirstPass = FALSE;  // Only do this bit once.
                        LoadString( g_hInst, IDS_NOICONSMSG1, szText, 256 );
                        MessageBox( GetDesktopWindow(), szText, szTitle, MB_OK | MB_ICONEXCLAMATION );

                        // No icons here - change the path do somewhere where we
                        // know there are icons. Get the path to progman.
//                      GetModuleFileName( g_hInst, lppid->szPathField, sizeof(lppid->szPathField) );
                        GetSystemDirectory( lppid->szPathField, sizeof(lppid->szPathField) );
                        lstrcat( lppid->szPathField, "\\shell32.dll" );
                        SetDlgItemText( hDlg, IDD_PATH, lppid->szPathField );
                        PutIconsInList( lppid );
                }
                else
                {
                        LoadString( g_hInst, IDS_NOICONSMSG, szText, 256 );
                        MessageBox( GetDesktopWindow(), szText, szTitle, MB_OK | MB_ICONEXCLAMATION );
                        return;
                }
        }
//      hOldCursor = SetCursor( LoadCursor( NULL, IDC_WAIT ) );

        SendDlgItemMessage( hDlg, IDD_ICON, WM_SETREDRAW, FALSE, 0L );

    if( rgIcons )
    {
                for( iTempIcon = 0; iTempIcon < cIcons; iTempIcon++ )
                {
                        SendDlgItemMessage( hDlg, IDD_ICON, LB_ADDSTRING, 0, (LPARAM)(UINT)rgIcons[iTempIcon] );
                }
                LocalFree((HLOCAL)rgIcons);
        }

        if( SendDlgItemMessage( hDlg, IDD_ICON, LB_SETCURSEL, lppid->iIconIndex, 0L ) == LB_ERR )
        {
                // select the first.
                SendDlgItemMessage( hDlg, IDD_ICON, LB_SETCURSEL, 0, 0L );
        }

        SendDlgItemMessage( hDlg, IDD_ICON, WM_SETREDRAW, TRUE, 0L );
        InvalidateRect( GetDlgItem(hDlg, IDD_ICON), NULL, TRUE );

//      SetCursor( hOldCursor );
}

void NEAR PASCAL InitPickIconDlg( HWND hDlg, LPPICKICON_DATA lppid )
{
RECT rc;
UINT cy;
HWND hwndIcons;

        // init state variables
        lppid->hDlg = hDlg;
        lstrcpyn( lppid->szPathField, lppid->pszIconPath, sizeof(lppid->szPathField) );

        // this first pass stuff is so that the first time something bogus happens
        // (file not found, no icons) we give the user a list of icons from progman.
        lppid->fFirstPass = TRUE;

        // init the dialog controls
        SetDlgItemText( hDlg, IDD_PATH, lppid->pszIconPath );
        SendDlgItemMessage( hDlg, IDD_PATH, EM_LIMITTEXT, lppid->cbIconPath, 0L );

        SendDlgItemMessage( hDlg, IDD_ICON, LB_SETCOLUMNWIDTH, GetSystemMetrics(SM_CXICON) + 12, 0L );

        hwndIcons = GetDlgItem( hDlg, IDD_ICON );

        // compute the height of the listbox based on icon dimensions
        GetClientRect( hwndIcons, &rc );

        cy = GetSystemMetrics( SM_CYICON ) + GetSystemMetrics( SM_CYHSCROLL ) + GetSystemMetrics( SM_CYEDGE ) * 3;

        SetWindowPos( hwndIcons, NULL, 0, 0, rc.right, cy, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );

        // REVIEW, explicitly position this dialog?

        cy = rc.bottom - cy;

        GetWindowRect( hDlg, &rc );
        rc.bottom -= rc.top;
        rc.right -= rc.left;
        rc.bottom = rc.bottom - cy;

        SetWindowPos( hDlg, NULL, 0, 0, rc.right, rc.bottom, SWP_NOMOVE | SWP_NOACTIVATE );

        PutIconsInList( lppid );
}

// call the common browse code for this
BOOL NEAR PASCAL BrowseForIconFile( LPPICKICON_DATA lppid )
{
OPENFILENAME  ofn;
CHAR szFilter[256] = TEXT("Icon Files\0*.ico;*.exe;*.dll\0Programs (*.exe)\0*.exe\0Libraries (*.dll)\0*.dll\0Icons (*.ico)\0*.ico\0All Files (*.*)\0*.*\0\0");
char szTitle[40];

        ZeroMemory(&ofn, sizeof(ofn));

        if (LoadString( g_hInst, IDS_ICONFILTER, szFilter, ARRAYSIZE(szFilter) ) != 0) {
            LPTSTR psz;

            for( psz = szFilter; *psz != TEXT('\0'); psz++ ) {
#ifdef DBCS
                if ( IsDBCSLeadByte(*psz) ) {
                    psz = CharNext(psz) - 1;
                    continue;
                }
#endif

                if (*psz == TEXT('\1')) {
                    *psz = TEXT('\0');
                }
            }
        }



        GetWindowText( lppid->hDlg, szTitle, sizeof(szTitle) );
        GetDlgItemText( lppid->hDlg, IDD_PATH, lppid->szBuffer, sizeof(lppid->szBuffer) );

        ofn.lStructSize                 = sizeof(OPENFILENAME);
        ofn.hwndOwner                   = lppid->hDlg;
        ofn.lpstrFilter                 = szFilter;
//      ofn.lpstrCustomFilter   = (LPSTR)NULL;
//      ofn.nMaxCustFilter              = 0L;
        ofn.nFilterIndex                = 1L;
        ofn.lpstrFile                   = lppid->szBuffer;
        ofn.nMaxFile                    = sizeof(lppid->szBuffer);
//      ofn.lpstrFileTitle              = (LPSTR)NULL;
//      ofn.lpstrInitialDir             = NULL;
        ofn.lpstrTitle                  = szTitle;
        ofn.Flags                               = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST;
//      ofn.nFileOffset                 = 0;
//      ofn.nFileExtension              = 0;
//      ofn.lCustData                   = 0;

        if( GetOpenFileName( &ofn ) )
        {
//              PathQuoteSpaces( lppid->szBuffer );     // JER
                SetDlgItemText( lppid->hDlg, IDD_PATH, lppid->szBuffer );
                // Set default button to OK.
                SendMessage( lppid->hDlg, DM_SETDEFID, IDOK, 0 );
                return TRUE;
        }
        else
                return FALSE;
}

// test if the name field is different from the last file we looked at
BOOL NEAR PASCAL NameChange( LPPICKICON_DATA lppid )
{
        GetDlgItemText( lppid->hDlg, IDD_PATH, lppid->szBuffer, sizeof(lppid->szBuffer) );
        return lstrcmpi(lppid->szBuffer, lppid->szPathField);
}


//
// dialog procedure for picking an icon (ala progman change icon)
// uses DLG_PICKICON template
//
// in:
//      pszIconFile
//      cbIconFile
//      iIndex
//
// out:
//      pszIconFile
//      iIndex
//
INT_PTR CALLBACK PickIconDlgProc( HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam )
{
LPPICKICON_DATA lppid = (LPPICKICON_DATA)GetWindowLong(hDlg, DWL_USER);

// Array for context help:
/*static DWORD aPickIconHelpIDs[] = {           // JER
        IDD_PATH,   IDH_FCAB_LINK_ICONNAME,
        IDD_ICON,   IDH_FCAB_LINK_CURRENT_ICON,
        IDD_BROWSE, IDH_BROWSE,
        0, 0
        };
*/
        switch( wMsg )
        {
                case WM_INITDIALOG:
                        SetWindowLong( hDlg, DWL_USER, lParam );
                        InitPickIconDlg( hDlg, (LPPICKICON_DATA)lParam );
                        break;

                case WM_COMMAND:
                        switch( GET_WM_COMMAND_ID(wParam, lParam) )
                        {
                                case IDHELP:        // not wired
                                        break;

                                case IDD_BROWSE:
                                        if( BrowseForIconFile( lppid ) )
                                                PutIconsInList( lppid );
                                        break;

                                case IDD_PATH:
                                        if( NameChange( lppid ) )
                                                SendDlgItemMessage( hDlg, IDD_ICON, LB_SETCURSEL, (WPARAM)-1, 0 );
                                        break;

                                case IDD_ICON:
                                        if( NameChange( lppid ) )
                                        {
                                                PutIconsInList( lppid );
                                                break;
                                        }
                                        if( GET_WM_COMMAND_CMD(wParam, lParam) != LBN_DBLCLK )
                                                break;

                                        /*** FALL THRU on double click ***/

                                case IDOK:
                                        if( NameChange( lppid ) )
                                        {
                                                PutIconsInList( lppid );
                                        }
                                        else
                                        {
                                        int iIconIndex = (int)SendDlgItemMessage( hDlg, IDD_ICON, LB_GETCURSEL, 0, 0L );
                                                if( iIconIndex < 0 )
                                                        iIconIndex = 0;
                                                lppid->iIconIndex = iIconIndex;
                                                lstrcpy( lppid->pszIconPath, lppid->szPathField );

                                                EndDialog( hDlg, TRUE );
                                        }
                                        break;

                                case IDCANCEL:
                                        EndDialog( hDlg, FALSE );
                                        break;

                                default:
                                        return( FALSE );
                        }
                        break;

                // owner draw messages for icon listbox
                case WM_DRAWITEM:
                        #define lpdi ((DRAWITEMSTRUCT FAR *)lParam)

                        if( lpdi->itemState & ODS_SELECTED )
                                SetBkColor( lpdi->hDC, GetSysColor( COLOR_HIGHLIGHT ) );
                        else
                                SetBkColor( lpdi->hDC, GetSysColor( COLOR_WINDOW ) );

                        /* repaint the selection state */
                        ExtTextOut( lpdi->hDC, 0, 0, ETO_OPAQUE, &lpdi->rcItem, NULL, 0, NULL );

                        /* draw the icon */
                        if( (int)lpdi->itemID >= 0 )
                                DrawIcon(lpdi->hDC, (lpdi->rcItem.left + lpdi->rcItem.right - GetSystemMetrics(SM_CXICON)) / 2,
                                        (lpdi->rcItem.bottom + lpdi->rcItem.top - GetSystemMetrics(SM_CYICON)) / 2, (HICON)lpdi->itemData);

                        // InflateRect(&lpdi->rcItem, -1, -1);

                        /* if it has the focus, draw the focus */
                        if( lpdi->itemState & ODS_FOCUS )
                                DrawFocusRect( lpdi->hDC, &lpdi->rcItem );

                        #undef lpdi
                        break;

                case WM_MEASUREITEM:
                        #define lpmi ((MEASUREITEMSTRUCT FAR *)lParam)

                        lpmi->itemWidth = GetSystemMetrics( SM_CXICON ) + 12;
                        lpmi->itemHeight = GetSystemMetrics( SM_CYICON ) + 4;

                        #undef lpmi
                        break;

                case WM_DELETEITEM:
                        #define lpdi ((DELETEITEMSTRUCT FAR *)lParam)

                        DestroyIcon( (HICON)lpdi->itemData );

                        #undef lpdi
                        break;

                case WM_HELP:
//                      WinHelp( ((LPHELPINFO) lParam)->hItemHandle, NULL, HELP_WM_HELP, (DWORD)(LPSTR) aPickIconHelpIDs ); // JER
                        break;

                case WM_CONTEXTMENU:
//                      WinHelp( (HWND) wParam, NULL, HELP_CONTEXTMENU, (DWORD)(LPVOID)aPickIconHelpIDs ); // JER
                        break;

                default:
                        return FALSE;
        }
        return TRUE;
}


// puts up the pick icon dialog
int WINAPI PickIconDlg( HWND hwnd, LPSTR pszIconPath, UINT cbIconPath, int FAR *piIconIndex )
{
PICKICON_DATA *pid;
int iResult;

        // if we are coming up from a 16->32 thunk.  it is possible that SHELL32 will
        // not be loaded in this context, so we will load ourself if we are not loaded.
//      IsDllLoaded( g_hInst, "SHELL32" );      // JER

        pid = (PICKICON_DATA *)LocalAlloc( LPTR, sizeof(PICKICON_DATA) );

        if( pid == NULL )
                return 0;

        pid->pszIconPath = pszIconPath;
        pid->cbIconPath = cbIconPath;
        pid->iIconIndex = *piIconIndex;

        iResult = DialogBoxParam( g_hInst, MAKEINTRESOURCE(DLG_PICKICON), hwnd, PickIconDlgProc, (LPARAM)(LPPICKICON_DATA)pid );

        *piIconIndex = pid->iIconIndex;

        LocalFree( pid );

        return iResult;
}
