/*----------------------------------------------------------------------------*/
/*                                                                            */
/*  Program Name: View Icon                                                   */
/*                                                                            */
/*  Module name : vi.c                                                        */
/*  Date        : 02-23-1989                                                  */
/*  Written by  : Bob Novell                                                  */
/*                                                                            */
/*----------------------------------------------------------------------------*/
#define _WINDOWS
#include "vi.h"
#include <io.h>
#include <stdarg.h>
#include <stdio.h>


FARPROC lpfnAppDlgProc;

HANDLE hFont;
HANDLE hInst;
HWND   ghWnd;
HWND   hWndList;

BOOL  FirstMinMax    = TRUE;
BOOL  WindowActive   = FALSE;
BOOL  FirstActivate  = TRUE;

char  szAppName[]    = "vi";
char  szAppTitle[]   = "View Icon";

char  Directory[80]  = "c:";
char  TempStr  [80];

char  ProgInit [80];                   /* path to program .ini file           */

int   CharWidth;
int   CharHeight;

int   xSpacing;
int   ySpacing;
int   xMaxIcons;

int   yMaxIcons;
int   IconsInWindow;

int   FileCount;
int   CurrentIndex;

RECT  gClientRect;

KeyStruct key2scroll [] =
     {
     VK_HOME,  WM_VSCROLL, SB_TOP,
     VK_END,   WM_VSCROLL, SB_BOTTOM,
     VK_PRIOR, WM_VSCROLL, SB_PAGEUP,
     VK_NEXT,  WM_VSCROLL, SB_PAGEDOWN,
     VK_UP,    WM_VSCROLL, SB_LINEUP,
     VK_DOWN,  WM_VSCROLL, SB_LINEDOWN,
     };

#define NUMKEYS (sizeof key2scroll / sizeof key2scroll[0])

/*----------------------------------------------------------------------------*/
/* FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)                              */
/*                                                                            */
/* PURPOSE:  Calls initialization function, processes message loop            */
/*----------------------------------------------------------------------------*/
int PASCAL WinMain(HANDLE Inst, HANDLE hPrevInst, LPSTR lpCmdLine, int nCmdShow)
    {
    MSG  msg;

    if (!hPrevInst)
        {
        if(!AppInit(Inst))
            return(FALSE);
        }
    else
        return(FALSE);

    ghWnd = CreateWindow(szAppName,szAppTitle,
       WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_VSCROLL,
       CW_USEDEFAULT,                  /* x position                          */
       0,                              /* y position                          */
       CW_USEDEFAULT,                  /* width                               */
       0,                              /* height                              */
       NULL,                           /* parent handle                       */
       NULL,                           /* menu or child ID                    */
       Inst,                           /* instance                            */
       NULL);                          /* additional info                     */

    if (!ghWnd)
        return (FALSE);

    ShowWindow(ghWnd, nCmdShow);
    UpdateWindow(ghWnd);

    while (GetMessage(&msg, NULL, NULL, NULL))
        {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
        }

    return (msg.wParam);
    }

/*----------------------------------------------------------------------------*/
/* FUNCTION: AppInit(HANDLE)                                                  */
/*                                                                            */
/* PURPOSE: Initializes window data and registers window class                */
/*----------------------------------------------------------------------------*/
BOOL AppInit(HANDLE hInstance)
    {
    HDC hDC;
    WNDCLASS   rClass;
    TEXTMETRIC tm;

    hInst = hInstance;

    hDC = GetDC(ghWnd);                /* get char width and height           */
    GetTextMetrics(hDC,&tm);
    CharWidth  = tm.tmAveCharWidth;
    CharHeight = tm.tmHeight + tm.tmExternalLeading;
    xSpacing   = (CharWidth  * 9);
    ySpacing   = (CharHeight + 49);
    ReleaseDC (ghWnd,hDC);

    lpfnAppDlgProc = MakeProcInstance(AppDlgProc,hInst);

    rClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
    rClass.hIcon         = LoadIcon(hInstance, "ViIcon");
    rClass.lpszMenuName  = "AppMenu";
    rClass.lpszClassName = szAppName;
    rClass.hbrBackground = GetStockObject(WHITE_BRUSH);
    rClass.hInstance     = hInstance;
    rClass.style         = NULL;
    rClass.lpfnWndProc   = AppWndProc;

    return RegisterClass(&rClass);
    }

/*----------------------------------------------------------------------------*/
/* FUNCTION: AppWndProc(HWND, unsigned, WORD, LONG)                           */
/*                                                                            */
/* PURPOSE:  Processes messages for main window                               */
/*----------------------------------------------------------------------------*/
long FAR PASCAL AppWndProc(HWND hWnd, uint message,WORD wParam, LONG lParam)
    {
    PAINTSTRUCT ps;
    FARPROC     lpProcAbout;
    HMENU       hMenu;

    switch (message)
        {
        case WM_GETMINMAXINFO:         /* ----------------------------------- */

            if(FirstMinMax)
                {                      // Set maximum size for opening window
                ((LPPOINT)lParam+4)->x = (xSpacing * 6)+20;
                ((LPPOINT)lParam+4)->y = (ySpacing * 4);
                FirstMinMax = FALSE;
                }
            else                       // ensure enough room for at least 1 icon
                {
                ((LPPOINT)lParam+3)->x = (xSpacing)+20;
                ((LPPOINT)lParam+3)->y = (ySpacing * 2);
                }
            break;

        case WM_SYSCOMMAND:            /* ----------------------------------- */
            if (wParam == IDM_ABOUT)
                {
                lpProcAbout = MakeProcInstance(AppAbout, hInst);
                DialogBox(hInst, "vi_about", hWnd, lpProcAbout);
                FreeProcInstance(lpProcAbout);
                break;
                }
            else
                return (DefWindowProc(hWnd, message, wParam, lParam));

        case WM_SYSCOLORCHANGE:        /* ----------------------------------- */
            InvalidateRect(ghWnd,NULL,TRUE); // force repaint if colors change
            break;

        case WM_VSCROLL:               /* ----------------------------------- */
            VerticalScrollProcess(wParam, lParam);
            break;

        case WM_SIZE:                  /* ----------------------------------- */
            if(WindowActive && wParam != SIZEICONIC)
                {
                int xTmp, yTmp;

                GetClientRect (ghWnd,&gClientRect);
                xTmp = (4+ gClientRect.right  - gClientRect.left) / xSpacing;
                yTmp = (   gClientRect.bottom - gClientRect.top)  / ySpacing;

                gClientRect.bottom = gClientRect.top + (ySpacing * yTmp);

                if(xTmp != xMaxIcons || yTmp != yMaxIcons)
                    {
                    xMaxIcons = xTmp;
                    yMaxIcons = yTmp;
                    InvalidateRect(ghWnd,NULL,TRUE);/* force repaint          */
                    IconsInWindow = xTmp * yTmp;
                    if(CurrentIndex > 0)            /* force index to boundry */
                        CurrentIndex -= (CurrentIndex % IconsInWindow);
                    AdjustScrollBar();
                    }
                }
            break;

        case WM_PAINT:                 /* ----------------------------------- */
            ViewIcons(BeginPaint(hWnd,&ps));
            EndPaint(hWnd, &ps);
            break;

        case WM_COMMAND:               /* ----------------------------------- */
            AppCommand(wParam);
            break;

        case WM_USER:                  /* ----------------------------------- */
            StartApplication();
            break;

        case WM_CREATE:                /* ----------------------------------- */
            hMenu = GetSystemMenu(hWnd, FALSE);
            ChangeMenu(hMenu, NULL, NULL, NULL, MF_APPEND | MF_SEPARATOR);
            ChangeMenu(hMenu, NULL, "A&bout vi...", IDM_ABOUT,
                       MF_APPEND | MF_STRING);

            hWndList = CreateWindow("listbox", NULL,
                                    WS_CHILD | LBS_SORT,
                                    CharWidth,       CharHeight,
                                    CharWidth  * 16, CharHeight * 20,
                                    hWnd,  IDC_INVISIBLE, hInst, NULL);
            SetScrollRange(hWnd, SB_VERT, 0, 0, FALSE);
            break;

        case WM_ACTIVATE:              /* ----------------------------------- */
            if(wParam)
                {
                SetFocus(hWnd);
                WindowActive = TRUE;
                if(FirstActivate && !HIWORD(lParam))
                    {                   // if 1st activate and not iconic
                    FirstActivate = FALSE;
                    PostMessage(hWnd,WM_USER,NULL,(LONG)NULL);
                    }
                }
            else
                WindowActive = FALSE;
            break;

        case WM_KEYDOWN:
            {
            int i;

            for (i = 0 ; i < NUMKEYS ; i++)
                 if (wParam == key2scroll[i].wVirtKey)
                      {
                      SendMessage (hWnd, key2scroll[i].iMessage,
                           key2scroll[i].wRequest, 0L) ;
                      break ;
                      }
            }
            break;

        case WM_DESTROY:               /* ----------------------------------- */
            FreeProcInstance(lpfnAppDlgProc);
            PostQuitMessage(0);
            break;

        default:                       /* ----------------------------------- */
            return (DefWindowProc(hWnd, message, wParam, lParam));
        } /* end switch */
    return (NULL);
    }

/*----------------------------------------------------------------------------*/
/* FUNCTION: AppCommand(Word wParam)                                          */
/*                                                                            */
/* PURPOSE:  Processes WM_COMMAND messages from main window                   */
/*----------------------------------------------------------------------------*/
void AppCommand(WORD wParam)
    {
    switch(wParam)
        {
        case IDC_INVISIBLE:
            break;

        case IDM_FILE:
            DialogBox(hInst,"SELECT",ghWnd,lpfnAppDlgProc);
            break;
        }                              /* end switch                          */
    }

/*----------------------------------------------------------------------------*/
/* FUNCTION: VerticalScrollProcess(WORD wParam, LONG lParam)                  */
/*                                                                            */
/* PURPOSE:  Processes WM_VSCROLL messages from main window                   */
/*----------------------------------------------------------------------------*/
void VerticalScrollProcess(WORD wParam, LONG lParam)
    {
    HDC hDC;

    switch(wParam)
        {
        case SB_THUMBPOSITION:
            CurrentIndex = LOWORD(lParam);
            if(CurrentIndex > 0)                    /* force index to boundry */
                CurrentIndex -= (CurrentIndex % IconsInWindow);
            InvalidateRect(ghWnd, NULL, TRUE);
            break;

        case SB_TOP:
            if(CurrentIndex > 0)
                {
                CurrentIndex = 0;
                InvalidateRect(ghWnd, NULL, TRUE);
                }
            break;

        case SB_BOTTOM:
            if(CurrentIndex + IconsInWindow < FileCount)
                {
                CurrentIndex = FileCount - IconsInWindow;
                InvalidateRect(ghWnd, NULL, TRUE);
                }
            break;

        case SB_LINEUP:
            if(CurrentIndex > 0)
                {
                CurrentIndex -= xMaxIcons;
                ScrollWindow(ghWnd, 0, ySpacing,
                  (LPRECT)&gClientRect, (LPRECT)&gClientRect);
                ValidateRect(ghWnd, NULL);
                hDC = GetDC(ghWnd);
                ViewIconRow(hDC, 0);
                ReleaseDC(ghWnd, hDC);
                }
            break;

        case SB_LINEDOWN:
            if((CurrentIndex + IconsInWindow) < FileCount)
                {
                CurrentIndex += xMaxIcons;
                ScrollWindow(ghWnd, 0, -ySpacing,
                  (LPRECT)&gClientRect, (LPRECT)&gClientRect);
                ValidateRect(ghWnd, NULL);
                hDC = GetDC(ghWnd);
                ViewIconRow(hDC, yMaxIcons-1);
                ReleaseDC(ghWnd, hDC);
                }
            break;

        case SB_PAGEUP:
            if(CurrentIndex > 0)
                {
                if(CurrentIndex >= IconsInWindow)
                    CurrentIndex -= IconsInWindow;
                else
                    CurrentIndex = 0;
                InvalidateRect(ghWnd, NULL, TRUE);
                }
            break;

        case SB_PAGEDOWN:
            if((CurrentIndex + IconsInWindow) < FileCount)
                {
                CurrentIndex += IconsInWindow;
                InvalidateRect(ghWnd, NULL, TRUE);
                }
            break;

        default:
            break;
        }
    SetScrollPos(ghWnd, SB_VERT, (CurrentIndex > IconsInWindow ?
      CurrentIndex+IconsInWindow : CurrentIndex), TRUE);
    }

/*----------------------------------------------------------------------------*/
/* FUNCTION: AppAbout(HWND, uint, WORD, LONG)                                 */
/*                                                                            */
/* PURPOSE:  Processes messages for "About" dialog box                        */
/*----------------------------------------------------------------------------*/
BOOL FAR PASCAL AppAbout(HWND hDlg, uint message,WORD wParam, LONG lParam)
    {
    switch (message)
        {
        case WM_INITDIALOG:
            return (TRUE);

        case WM_COMMAND:
            if (wParam == IDOK)
                {
                EndDialog(hDlg, TRUE);
                return (TRUE);
                }
            break;
        }
    return (FALSE);
    }

/*----------------------------------------------------------------------------*/
/* FUNCTION: AppDlgProc(HWND, unsigned, WORD, LONG)                           */
/*                                                                            */
/* PURPOSE:  Processes Dialog Box Messages                                    */
/*----------------------------------------------------------------------------*/
BOOL FAR PASCAL AppDlgProc(HWND hDlg, uint message, WORD wParam, LONG lParam)
    {
    static BOOL ListBoxFocus = FALSE;

    switch(message)
        {
        case WM_INITDIALOG:
            strcpy(TempStr, Directory);
            UpdateListBox(hDlg);
            SetFocus(GetDlgItem(hDlg, IDC_LISTBOX));
            return FALSE;
            break;

        case WM_COMMAND:
            switch(wParam)
                {
                case IDC_LISTBOX:
                    switch(HIWORD(lParam))
                        {
                        case LBN_SETFOCUS:
                            ListBoxFocus = TRUE;
                            SendDlgItemMessage(hDlg, IDC_LISTBOX, LB_SETCURSEL,
                              GetListBoxIndex(hDlg), 0L);
                            return TRUE;

                        case LBN_KILLFOCUS:
                            ListBoxFocus = FALSE;
                            SendDlgItemMessage(hDlg, IDC_LISTBOX, LB_SETCURSEL,
                              -1, 0L);
                            return TRUE;

                        case LBN_DBLCLK:
DoubleClick:                DlgDirSelect(hDlg, TempStr, IDC_LISTBOX);
                            UpdateListBox(hDlg);
                            return (TRUE);
                            break;

                        } /* end IDC_LISTBOX switch */
                    break;

                case IDOK:
                    if(ListBoxFocus)
                        goto DoubleClick;

                    GetDlgItemText(hDlg, IDC_PATH, Directory, sizeof(Directory));
                    WritePrivateProfileString(szAppName, "Icon_Path",
                      Directory, ProgInit);
                    EndDialog(hDlg,TRUE);
                    MakeFileList();
                    return (TRUE);
                    break;

                case IDCANCEL:
                    EndDialog(hDlg,FALSE);
                    return (TRUE);
                    break;

                default:
                    return (FALSE);
                } /* end WM_COMMAND SWITCH */
        default:
            return (FALSE);
        }
    }

/*----------------------------------------------------------------------------*/
/* FUNCTION: UpdateListBox(HWND hDlg)                                         */
/*                                                                            */
/* PURPOSE:  Helper function for dialog window proc                           */
/*----------------------------------------------------------------------------*/
void UpdateListBox(HWND hDlg)
    {
    SetDlgItemText(hDlg, IDC_PATH, TempStr);
    DlgDirList(hDlg, TempStr, IDC_LISTBOX, IDC_PATH, 0xC010);
    SendDlgItemMessage(hDlg, IDC_LISTBOX, LB_SETCURSEL,
      GetListBoxIndex(hDlg), 0L);
    }

/*----------------------------------------------------------------------------*/
/* FUNCTION: GetListBoxIndex(HWND hDlg)                                       */
/*                                                                            */
/* PURPOSE:  Helper function for dialog window proc                           */
/*----------------------------------------------------------------------------*/
WORD GetListBoxIndex(HWND hDlg)
    {
    WORD rtn;

    rtn = (WORD)SendDlgItemMessage(hDlg, IDC_LISTBOX, LB_GETCURSEL, 0, 0L);
    if(rtn == LB_ERR)
        return 0;
    else
        return rtn;
    }

/*----------------------------------------------------------------------------*/
/* FUNCTION: StartApplication()                                               */
/*                                                                            */
/* PURPOSE:  Called when application is started                               */
/*----------------------------------------------------------------------------*/
void StartApplication()
    {
    GetModuleFileName(hInst, ProgInit, sizeof(ProgInit));
    strcpy(strchr(ProgInit, '.'), ".ini");
    GetPrivateProfileString(szAppName, "Icon_Path", "c:", Directory,
      sizeof(Directory), ProgInit);
    MakeFileList();
    }

/*----------------------------------------------------------------------------*/
/* FUNCTION: Message(uchr *fmt_string, ...)                                   */
/*                                                                            */
/* PURPOSE:  Simplify calls to MessageBox()                                   */
/*----------------------------------------------------------------------------*/
void Message(uchr *fmt_string, ...)
    {
    uchr sbuf[256];

    va_list argptr;
    va_start(argptr,fmt_string);
    vsprintf(sbuf, fmt_string, argptr);
    va_end(argptr);
    MessageBox(ghWnd, sbuf, szAppTitle, MB_OK);
    }

/*----------------------------------------------------------------------------*/
/* FUNCTION: MakeFileList()                                                   */
/*                                                                            */
/* PURPOSE:  Build a list of all all icon file names in a directory           */
/*----------------------------------------------------------------------------*/
void MakeFileList()
    {
    char temp[80];

    sprintf(temp, "%s\\*.ico", Directory);
    SendMessage(hWndList, LB_RESETCONTENT, 0, 0L);
    SendMessage(hWndList, LB_DIR, 0, (LONG)(LPSTR)temp);

    FileCount    = (WORD)SendMessage(hWndList, LB_GETCOUNT, 0, 0L);
    CurrentIndex = 0;
    AdjustScrollBar();
    InvalidateRect(ghWnd,NULL,TRUE);        /* force repaint                  */
    }

/*----------------------------------------------------------------------------*/
/* FUNCTION: AdjustScrollBar()                                                */
/*                                                                            */
/* PURPOSE:  Sets vertical scroll bar thumbtrack indicator                    */
/*----------------------------------------------------------------------------*/
void AdjustScrollBar()
    {
    if(IconsInWindow < FileCount)
        {
        SetScrollRange(ghWnd, SB_VERT, 0, FileCount, FALSE);
        SetScrollPos(ghWnd, SB_VERT, (CurrentIndex > IconsInWindow ?
          CurrentIndex+IconsInWindow : CurrentIndex), TRUE);
        }
    else
        ShowScrollBar(ghWnd, SB_VERT, 0);   /* hide scroll bar                */
    }

/*----------------------------------------------------------------------------*/
/* FUNCTION: DisplayIconBitMap(HANDLE hBuf, uint xLoc, uint yLoc)             */
/*                                                                            */
/* PURPOSE:  Display 1 icon from a bitmap                                     */
/*----------------------------------------------------------------------------*/
void DisplayIconBitMap(HANDLE hBuf, uint xLoc, uint yLoc)
    {
    HDC              hDC;
    LPSTR            pbuf;
    LPICONDIRECTORY  lpid;
    LPBITMAPINFO     lpbi;

    lpid = (LPICONDIRECTORY)GlobalLock(hBuf);
    lpbi = (LPBITMAPINFO)(lpid + 1);

    pbuf = (LPSTR)lpbi + (int)(lpbi->bmiHeader.biSize) +
           (lpid->ColorCount * sizeof(RGBQUAD));

    hDC = GetDC(ghWnd);
    SetDIBitsToDevice(hDC, xLoc, yLoc,
                      (WORD)lpid->Width,
                      (WORD)lpid->Height,
                      0, 0, 0,
                      (WORD)lpid->Width,
                      pbuf,
                      (LPBITMAPINFO)lpbi,
                      DIB_RGB_COLORS);
    ReleaseDC(ghWnd, hDC);

    GlobalUnlock(hBuf);
    GlobalFree(hBuf);
    }

/*----------------------------------------------------------------------------*/
/* FUNCTION: GetIconBitMap(LPSTR FileName)                                    */
/*                                                                            */
/* PURPOSE:  Get an icon bit map from an icon file                            */
/*----------------------------------------------------------------------------*/
HANDLE GetIconBitMap(LPSTR FileName)
    {
    int             IconFile;
    DWORD           szDir = sizeof(ICONDIRECTORY);
    HANDLE          hMem;
    OFSTRUCT        info;
    LPICONDIRECTORY lpbi;

    LONG retn;
    int  retn1;

    if((IconFile = OpenFile(FileName, &info, OF_READ)) == -1)
        return -1;

    _llseek(IconFile, sizeof(ICONFILEHDR), SEEK_SET);   /* seek to icon dir   */

    hMem = GlobalAlloc(GMEM_MOVEABLE, szDir);
    if(!hMem)
        {
        Message("GlobalAlloc failed");
        close(IconFile);
        return -1;
        }

    lpbi = (LPICONDIRECTORY)GlobalLock(hMem);
    _lread(IconFile, (LPSTR)lpbi, (WORD)szDir);        /* read icon directory */

    hMem = GlobalReAlloc(hMem, lpbi->DIBSize + szDir, GMEM_MOVEABLE);
    if(!hMem)
        {
        Message("GlobalReAlloc failed");
        close(IconFile);
        return -1;
        }
    else
        {
        retn = _llseek(IconFile, lpbi->DIBOffset, SEEK_SET);
        retn1 = _lread(IconFile, (LPSTR)(lpbi+1), (WORD)lpbi->DIBSize);
        GlobalUnlock(hMem);
        }

     close(IconFile);
     return hMem;
     }

/*----------------------------------------------------------------------------*/
/* FUNCTION: ViewIcons(HDC hDC)                                               */
/*                                                                            */
/* PURPOSE:  Display the max number of icons that will fit in current window  */
/*----------------------------------------------------------------------------*/
void ViewIcons(HDC hDC)
    {
    int row;

    if(FileCount == 0)
        return;

    for(row = 0; row < yMaxIcons ;row++)
        ViewIconRow(hDC, row);
    }

/*----------------------------------------------------------------------------*/
/* FUNCTION: ViewIconRow(HDC hDC, int row)                                    */
/*                                                                            */
/* PURPOSE:  Display 1 row of icons                                           */
/*----------------------------------------------------------------------------*/
void ViewIconRow(HDC hDC, int row)
    {
    int   i;
    int   index;
    int   xLoc = 4;
    int   yLoc = 0;
    char  Name[16];
    char  FileName[80];
    RECT  ClearRect;

    yLoc += (ySpacing * row);
    index = (CurrentIndex + (row * xMaxIcons));

    ClearRect.left   = xLoc;
    ClearRect.top    = yLoc;
    ClearRect.right  = xLoc + (xSpacing * xMaxIcons);
    ClearRect.bottom = yLoc + ySpacing;
    FillRect(hDC, (LPRECT)&ClearRect, GetStockObject(WHITE_BRUSH));

    for(i = 0; (i < xMaxIcons) && (index < FileCount) ;i++, index++)
        {
        if(index >= 0)
            {
            SendMessage(hWndList, LB_GETTEXT, index, (LONG)(LPSTR)Name);
            sprintf(FileName, "%s\\%s", Directory, Name);
            ShowIcon(FileName, xLoc, yLoc);
            *(strrchr(Name, '.')) = 0;
            TextOut(hDC, xLoc, yLoc+39, Name, strlen(Name));
            }
        xLoc += xSpacing;
        }
    }

/*----------------------------------------------------------------------------*/
/* FUNCTION: ShowIcon(uchr *FileName, uint xLoc, uint yLoc)                   */
/*                                                                            */
/* PURPOSE:  Display 1 icon from an icon file                                 */
/*----------------------------------------------------------------------------*/
void ShowIcon(uchr *FileName, uint xLoc, uint yLoc)
    {
    HANDLE hBuf;
    if((hBuf = GetIconBitMap(FileName)) == -1)
        Message("Error when opening file %s", FileName);
    else
        DisplayIconBitMap(hBuf, xLoc, yLoc+4);
    }


