/*++

    Implements IShellView.

--*/

#include <windows.h>
#include <windowsx.h>
#include <objbase.h>
#include <shlobj.h>

#include <docobj.h>
#include <shellapi.h>

#include "pstore.h"

#include "shfolder.h"

#include "shview.h"
#include "guid.h"
#include "resource.h"
#include "tools.h"

#include "utility.h"
#include "enumid.h"


#include "listu.h"


extern HINSTANCE  g_hInst;
extern LONG       g_DllRefCount;



BOOL
AddListItem(
    HWND hWnd,
    DWORD dwType,
    PST_KEY KeyType,
    LPCWSTR szName,
    LPITEMIDLIST pidl
    );



#define TOOLBAR_ID   (L"TBar")
#define INITIAL_COLUMN_POS 100

MYTOOLINFO g_Buttons[] =
   {
   IDM_MESSAGE1, 0, IDS_TB_MESSAGE1, IDS_MI_MESSAGE1, TBSTATE_ENABLED, TBSTYLE_BUTTON,
   IDM_MESSAGE2, 0, IDS_TB_MESSAGE2, IDS_MI_MESSAGE2, TBSTATE_ENABLED, TBSTYLE_BUTTON,
   -1, 0, 0, 0, 0,
   };


CShellView::CShellView(
    CShellFolder *pFolder,
    LPCITEMIDLIST pidl
    )
{

    m_hMenu = NULL;
    ZeroMemory(&m_MenuWidths, sizeof(m_MenuWidths));

    m_nColumn1 = INITIAL_COLUMN_POS;
    m_nColumn2 = INITIAL_COLUMN_POS;

    m_pSFParent = pFolder;
    if(m_pSFParent)
        m_pSFParent->AddRef();

    m_pidl = (LPITEMIDLIST)pidl;

    m_uState = SVUIA_DEACTIVATE;

    m_ObjRefCount = 1;
    InterlockedIncrement(&g_DllRefCount);
}


CShellView::~CShellView()
{
    if(m_pSFParent)
        m_pSFParent->Release();

    InterlockedDecrement(&g_DllRefCount);
}


STDMETHODIMP
CShellView::QueryInterface(
    REFIID riid,
    LPVOID *ppReturn
    )
/*++

    IUnknown::QueryInterface

--*/
{
    *ppReturn = NULL;

    if(IsEqualIID(riid, IID_IUnknown))
        *ppReturn = (IUnknown*)(IShellView*)this;
    else if(IsEqualIID(riid, IID_IOleWindow))
        *ppReturn = (IOleWindow*)this;
    else if(IsEqualIID(riid, IID_IShellView))
        *ppReturn = (CShellView*)this;
    else if(IsEqualIID(riid, IID_IOleCommandTarget))
        *ppReturn = (IOleCommandTarget*)this;

    if(*ppReturn == NULL)
        return E_NOINTERFACE;

    (*(LPUNKNOWN*)ppReturn)->AddRef();
    return S_OK;
}


STDMETHODIMP_(DWORD)
CShellView::AddRef()
/*++

    IUnknown::AddRef

--*/
{
    return InterlockedIncrement(&m_ObjRefCount);
}


STDMETHODIMP_(DWORD)
CShellView::Release()
/*++

    IUnknown::Release

--*/
{
    LONG lDecremented = InterlockedDecrement(&m_ObjRefCount);

    if(lDecremented == 0)
        delete this;

    return lDecremented;
}


STDMETHODIMP
CShellView::GetWindow(
    HWND *phWnd
    )
/*++

    IOleWindow::GetWindow

--*/
{
    *phWnd = m_hWnd;

    return S_OK;
}


STDMETHODIMP
CShellView::ContextSensitiveHelp(
    BOOL fEnterMode
    )
/*++

   Inherited from IOleWindow::ContextSensitiveHelp.

--*/
{
    return E_NOTIMPL;
}


STDMETHODIMP
CShellView::QueryStatus(
    const GUID *pguidCmdGroup,
    ULONG cCmds,
    OLECMD prgCmds[],
    OLECMDTEXT *pCmdText
    )
/*++

    IOleCommandTarget::QueryStatus

--*/
{
    ULONG i;

    //
    // only process the commands for our command group
    //

    if(pguidCmdGroup && (*pguidCmdGroup != CLSID_CmdGrp))
        return OLECMDERR_E_UNKNOWNGROUP;

    //
    // make sure prgCmds is not NULL
    //

    if(prgCmds == NULL)
        return E_POINTER;

    //
    // run through all of the commands and supply the correct information
    //

    for(i = 0; i < cCmds;i++)
    {
        switch(prgCmds[i].cmdID)
        {
            case IDM_MESSAGE1:
                prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
                break;

            case IDM_MESSAGE2:
                prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
                break;
        }
    }

    return S_OK;
}


STDMETHODIMP
CShellView::Exec(
    const GUID *pguidCmdGroup,
    DWORD nCmdID,
    DWORD nCmdExecOpt,
    VARIANTARG *pvaIn,
    VARIANTARG *pvaOut
    )
/*++

    IOleCommandTarget::Exec

--*/
{
    //
    // only process the commands for our command group
    //

    if(pguidCmdGroup && (*pguidCmdGroup == CLSID_CmdGrp))
    {
        OnCommand(nCmdID, 0, NULL);
        return S_OK;
    }

    return OLECMDERR_E_UNKNOWNGROUP;
}


STDMETHODIMP
CShellView::TranslateAccelerator(
    LPMSG pMsg
    )
/*++

  Same as the IOleInPlaceFrame::TranslateAccelerator, but will be
  never called because we don't support EXEs (i.e., the explorer has
  the message loop). This member function is defined here for possible
  future enhancement.

--*/
{
    return E_NOTIMPL;
}


STDMETHODIMP
CShellView::EnableModeless(
    BOOL fEnable
    )
/*++

   Same as the IOleInPlaceFrame::EnableModeless.

--*/
{
    return E_NOTIMPL;
}


STDMETHODIMP
CShellView::UIActivate(
    UINT uState
    )
/*++

    IShellView::UIActivate

--*/
{
    //
    // don't do anything if the state isn't really changing
    //

    if(m_uState == uState)
        return S_OK;

    //
    // Always do this because we will cause problems if we are going from
    // SVUIA_ACTIVATE_FOCUS to SVUIA_ACTIVATE_NOFOCUS or vice versa.
    //

    m_uState = SVUIA_DEACTIVATE;

    m_pShellBrowser->SetMenuSB(NULL, NULL, NULL);

    if(m_hMenu) {
        DestroyMenu(m_hMenu);
        m_hMenu = NULL;
    }

    //
    // only do this if we are active
    //

    if(uState != SVUIA_DEACTIVATE) {
        m_uState = uState;

        //
        // update the Explorer's menu
        //

        if(m_hMenu == NULL)
            m_hMenu = CreateMenu();

        if(m_hMenu) {
            MENUITEMINFO   mii;
            TCHAR szText[MAX_PATH];

            m_pShellBrowser->InsertMenusSB(m_hMenu, &m_MenuWidths);

            //
            // get the menu item's text
            //
            LoadString(g_hInst, IDS_MI_REGISTRY, szText, sizeof(szText));

            //
            // build our menu
            //

            ZeroMemory(&mii, sizeof(mii));
            mii.cbSize = sizeof(mii);
            mii.fMask = MIIM_SUBMENU | MIIM_TYPE | MIIM_STATE;
            mii.fType = MFT_STRING;
            mii.fState = MFS_ENABLED;
            mii.dwTypeData = szText;
            mii.hSubMenu = BuildMenu();

            //
            // insert our menu
            //

            InsertMenuItem(m_hMenu, FCIDM_MENU_HELP, FALSE, &mii);

            m_pShellBrowser->SetMenuSB(m_hMenu, NULL, m_hWnd);
        }

        //
        // TODO: update the status bar
        //

/*

        TCHAR szName[MAX_PATH];

        LoadString(g_hInst, IDS_PSTORE_TITLE, szName, sizeof(szName));

        m_pSFParent->GetPidlFullText(m_pidl, szName + lstrlen(szName), sizeof(szName) - lstrlen(szName));

        LRESULT  lResult;
        int      nPartArray[1] = {-1};

        //set the number of parts
        m_pShellBrowser->SendControlMsg(FCW_STATUS, SB_SETPARTS, 1, (LPARAM)nPartArray, &lResult);

        //set the text for the parts
        m_pShellBrowser->SendControlMsg(FCW_STATUS, SB_SETTEXT, 0, (LPARAM)szName, &lResult);

 */
    }

    return S_OK;
}


HMENU
CShellView::BuildMenu(
    void
    )
{
    HMENU hSubMenu = CreatePopupMenu();

    if(hSubMenu == NULL)
        return NULL;

    TCHAR          szText[MAX_PATH];
    MENUITEMINFO   mii;
    int            nButtons, i;

    //
    // get the number of items in our global array
    //

    for(nButtons = 0; g_Buttons[nButtons].idCommand != -1; nButtons++) {
        ;
    }

    //
    // add the menu items
    //

    for(i = 0; i < nButtons; i++) {

        LoadString(g_hInst, g_Buttons[i].idMenuString, szText, sizeof(szText));

        ZeroMemory(&mii, sizeof(mii));
        mii.cbSize = sizeof(mii);
        mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;

        if(TBSTYLE_SEP != g_Buttons[i].bStyle) {

            mii.fType = MFT_STRING;
            mii.fState = MFS_ENABLED;
            mii.dwTypeData = szText;
            mii.wID = g_Buttons[i].idCommand;
        } else {
            mii.fType = MFT_SEPARATOR;
        }

        //
        // tack this item onto the end of the menu
        //

        InsertMenuItem(hSubMenu, (UINT)-1, TRUE, &mii);
    }

    //
    // add a couple more that aren't in the button array
    //

    LoadString(g_hInst, IDS_MI_VIEW_ISTB, szText, sizeof(szText));

    ZeroMemory(&mii, sizeof(mii));
    mii.cbSize = sizeof(mii);
    mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
    mii.fType = MFT_STRING;
    mii.fState = MFS_ENABLED;
    mii.dwTypeData = szText;
    mii.wID = IDM_VIEW_ISTB;

    //
    // tack this item onto the end of the menu
    //

    InsertMenuItem(hSubMenu, (UINT)-1, TRUE, &mii);

    LoadString(g_hInst, IDS_MI_VIEW_IETB, szText, sizeof(szText));

    ZeroMemory(&mii, sizeof(mii));
    mii.cbSize = sizeof(mii);
    mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
    mii.fType = MFT_STRING;
    mii.fState = MFS_ENABLED;
    mii.dwTypeData = szText;
    mii.wID = IDM_VIEW_IETB;

    //
    // tack this item onto the end of the menu
    //

    InsertMenuItem(hSubMenu, (UINT)-1, TRUE, &mii);

    return hSubMenu;
}


STDMETHODIMP
CShellView::Refresh(
    void
    )
/*++

    IShellView::Refresh

--*/
{
    //
    // empty the list
    //

    ListView_DeleteAllItems(m_hwndList);

    //
    // refill the list
    //

    FillList();

    return S_OK;
}

STDMETHODIMP
CShellView::CreateViewWindow(
    LPSHELLVIEW pPrevView,
    LPCFOLDERSETTINGS lpfs,
    LPSHELLBROWSER psb,
    LPRECT prcView,
    HWND *phWnd
    )
/*++

    CreateViewWindow creates a view window. This can be either the right pane
    of the Explorer or the client window of a folder window.

--*/
{
    WNDCLASS wc;

    *phWnd = NULL;

    //
    // if our window class has not been registered, then do so
    //

    if(!GetClassInfo(g_hInst, NS_CLASS_NAME, &wc)) {

        ZeroMemory(&wc, sizeof(wc));
        wc.style          = CS_HREDRAW | CS_VREDRAW;
        wc.lpfnWndProc    = WndProc;
        wc.cbClsExtra     = 0;
        wc.cbWndExtra     = 0;
        wc.hInstance      = g_hInst;
        wc.hIcon          = NULL;
        wc.hCursor        = LoadCursor(NULL, IDC_ARROW);
        wc.hbrBackground  = (HBRUSH)(COLOR_WINDOW + 1);
        wc.lpszMenuName   = NULL;
        wc.lpszClassName  = NS_CLASS_NAME;

        if(!RegisterClass(&wc))
            return E_FAIL;
    }

    //
    // set up the member variables
    //

    m_pShellBrowser = psb;
    m_FolderSettings.ViewMode = lpfs->ViewMode;
    m_FolderSettings.fFlags = lpfs->fFlags;

    //
    // get our parent window
    //

    m_pShellBrowser->GetWindow(&m_hwndParent);

    GetSettings();

    *phWnd = CreateWindowEx(   0,
                               NS_CLASS_NAME,
                               NULL,
                               WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS,
                               prcView->left,
                               prcView->top,
                               prcView->right - prcView->left,
                               prcView->bottom - prcView->top,
                               m_hwndParent,
                               NULL,
                               g_hInst,
                               (LPVOID)this);

    if(*phWnd == NULL)
        return E_FAIL;

    //
    // addref the IShellBrowser interface to allow communication with Explorer
    //

    m_pShellBrowser->AddRef();

    return S_OK;
}


STDMETHODIMP
CShellView::DestroyViewWindow(
    void
    )
/*++

    The Explorer calls this method when a folder window or the Explorer
    is being closed.

--*/
{
    if(m_hMenu)
        DestroyMenu(m_hMenu);

    DestroyWindow(m_hWnd);

    //
    // release the shell browser object
    //

    m_pShellBrowser->Release();

    return S_OK;
}

STDMETHODIMP
CShellView::GetCurrentInfo(
    LPFOLDERSETTINGS lpfs
    )
/*++

    The Explorer uses GetCurrentInfo to query the view for standard settings.

--*/
{
// TODO, check proper approach.

    *lpfs = m_FolderSettings;
//    lpfs->ViewMode = m_FolderSettings.ViewMode;
//    lpfs->fFlags = m_FolderSettings.fFlags;

    return NOERROR;
}


STDMETHODIMP
CShellView::AddPropertySheetPages(
    DWORD dwReserved,
    LPFNADDPROPSHEETPAGE lpfn,
    LPARAM lParam
    )
/*++
    The Explorer calls this method when it is opening the View.Options...
    property sheet. Views can add pages by creating them and calling the
    callback function with the page handles.
--*/
{
    OutputDebugStringA("IShellView::AddPropertySheetPages\n");
    return E_NOTIMPL;
}



STDMETHODIMP
CShellView::SaveViewState(
    void
    )
/*++

    IShellView::SaveViewState

--*/
{
    SaveSettings();
    return S_OK;
}


STDMETHODIMP
CShellView::SelectItem(
    LPCITEMIDLIST pidlItem,
    UINT uFlags
    )
/*++

    IShellView::SelectItem

--*/
{
    return E_NOTIMPL;
}


STDMETHODIMP
CShellView::GetItemObject(
    UINT uItem,
    REFIID riid,
    LPVOID *ppvOut
    )
/*++

    Used by the common dialogs to get the selected items from the view.

--*/
{
    return E_NOTIMPL;
}



LRESULT
CALLBACK
CShellView::WndProc(
    HWND hWnd,
    UINT uMessage,
    WPARAM wParam,
    LPARAM lParam
    )
{
    CShellView  *pThis = (CShellView*)GetWindowLongPtr(hWnd, GWLP_USERDATA);

    switch (uMessage)
    {
        case WM_NCCREATE:
        {
            LPCREATESTRUCT lpcs = (LPCREATESTRUCT)lParam;
            pThis = (CShellView*)(lpcs->lpCreateParams);
            SetWindowLongPtr(hWnd, GWLP_USERDATA, (LPARAM)pThis);

            //set the window handle
            pThis->m_hWnd = hWnd;
            break;
        }

        case WM_SIZE:
            return pThis->OnSize(LOWORD(lParam), HIWORD(lParam));

        case WM_CREATE:
            return pThis->OnCreate();

        case WM_SETFOCUS:
            return pThis->OnSetFocus();

        case WM_ACTIVATE:
            return pThis->OnActivate(wParam, lParam);

        case WM_COMMAND:
            return pThis->OnCommand( GET_WM_COMMAND_ID(wParam, lParam),
                                     GET_WM_COMMAND_CMD(wParam, lParam),
                                     GET_WM_COMMAND_HWND(wParam, lParam));

        case WM_INITMENUPOPUP:
            return pThis->UpdateMenu((HMENU)wParam);

        case WM_NOTIFY:
            return pThis->OnNotify((UINT)wParam, (LPNMHDR)lParam);
    }

    return DefWindowProc(hWnd, uMessage, wParam, lParam);
}


LRESULT
CShellView::OnSetFocus(
    void
    )
{
    //
    // tell the browser we got the focus
    //

    m_pShellBrowser->OnViewWindowActive(this);

    return 0;
}


LRESULT
CShellView::OnActivate(
    WPARAM wParam,
    LPARAM lParam
    )
{
    //
    // tell the browser we got the focus
    //

    if(wParam)
        m_pShellBrowser->OnViewWindowActive(this);

    return 0;
}


LRESULT
CShellView::OnCommand(
    DWORD dwCmdID,
    DWORD dwCmd,
    HWND hwndCmd
    )
{
    switch(dwCmdID)
    {
       case IDM_MESSAGE1:
            MessageBox(m_hWnd, TEXT("Message 1 was selected"), TEXT("Notice"), MB_OK | MB_ICONEXCLAMATION);
            break;

       case IDM_MESSAGE2:
            MessageBox(m_hWnd, TEXT("Message 2 was selected"), TEXT("Notice"), MB_OK | MB_ICONEXCLAMATION);
            break;

        case IDM_VIEW_ISTB:
            m_bShowISTB = ! m_bShowISTB;
            UpdateToolbar();
            break;

        case IDM_VIEW_IETB:
            m_bShowIETB = ! m_bShowIETB;
            UpdateToolbar();
            break;
    }

    return 0;
}


HRESULT
CShellView::SaveSettings(
    void
    )
/*++

    Called by IShellView::SaveViewState to store the relevant setting data
    to the browser stream.

--*/
{
    HRESULT  hr;
    LPSTREAM pStream;

    hr = m_pShellBrowser->GetViewStateStream(STGM_WRITE, &pStream);

    if(SUCCEEDED(hr))
    {
        ULONG uWritten;

        pStream->Write(&m_bShowISTB, sizeof(m_bShowISTB), &uWritten);
        pStream->Write(&m_bShowIETB, sizeof(m_bShowIETB), &uWritten);
        pStream->Write(&m_nColumn1, sizeof(m_nColumn1), &uWritten);
        pStream->Write(&m_nColumn2, sizeof(m_nColumn2), &uWritten);

        pStream->Release();
    }

    return hr;
}


HRESULT
CShellView::GetSettings(
    void
    )
{
    HRESULT  hr;
    LPSTREAM pStream;

    hr = m_pShellBrowser->GetViewStateStream(STGM_READ, &pStream);

    if(SUCCEEDED(hr)) {

        ULONG uRead;

        if(S_OK != pStream->Read(&m_bShowISTB, sizeof(m_bShowISTB), &uRead))
            m_bShowISTB = FALSE;

        if(S_OK != pStream->Read(&m_bShowIETB, sizeof(m_bShowIETB), &uRead))
            m_bShowIETB = FALSE;

        if(S_OK != pStream->Read(&m_nColumn1, sizeof(m_nColumn1), &uRead))
            m_nColumn1 = INITIAL_COLUMN_POS;

        if(S_OK != pStream->Read(&m_nColumn2, sizeof(m_nColumn2), &uRead))
            m_nColumn2 = INITIAL_COLUMN_POS;

        pStream->Release();
    }

    return hr;
}

LRESULT
CShellView::UpdateMenu(
    HMENU hMenu
    )
{
    return 0;
}


void
CShellView::UpdateToolbar()
{
    // nothing to update yet
}


LRESULT
CShellView::OnNotify(
    UINT CtlID,
    LPNMHDR pNotify
    )
{
    LPNM_LISTVIEW pNotifyLV = (LPNM_LISTVIEW)pNotify;
    LV_DISPINFO *pNotifyDI = (LV_DISPINFO *)pNotify;
    LPTBNOTIFY pNotifyTB = (LPTBNOTIFY)pNotify;
    LPTOOLTIPTEXT pNotifyTT = (LPTOOLTIPTEXT)pNotify;

    switch(pNotify->code)
    {

		//
		// HDN_BEGINTRACK
		// HDN_DIVIDERDBLCLICK
		// HDN_ENDTRACK
		// HDN_ITEMCHANGED
		// HDN_ITEMCHANGING
		// HDN_ITEMCLICK
		// HDN_ITEMDBLCLICK
		// HDN_TRACK
		//

        case HDN_ENDTRACK:
        {
            m_nColumn1 = ListView_GetColumnWidth(m_hwndList, 0);
            m_nColumn2 = ListView_GetColumnWidth(m_hwndList, 1);

            break;
        }
		
		//
		// LVN_BEGINDRAG
		// LVN_BEGINLABELEDIT
		// LVN_BEGINRDRAG
		// LVN_COLUMNCLICK
		// LVN_DELETEALLITEMS
		// LVN_DELETEITEM
		// LVN_ENDLABELEDIT
		// LVN_GETDISPINFO
		// LVN_INSERTITEM
		// LVN_ITEMCHANGED
		// LVN_ITEMCHANGING
		// LVN_KEYDOWN
		// LVN_SETDISPINFO
		//

        case LVN_DELETEITEM:
        {
            LPITEMIDLIST pidl = (LPITEMIDLIST)(pNotifyLV->lParam);

            //
            // free the memory for the pidl associated with this item.
            //

            if(pidl != NULL)
                FreePidl(pidl);

            break;
        }

		// NM_CLICK
		// NM_DBLCLK
		// NM_KILLFOCUS
		// NM_LISTVIEW
		// NM_OUTOFMEMORY
		// NM_RCLICK
		// NM_RDBLCLK
		// NM_RETURN
		// NM_SETFOCUS
		// NM_TREEVIEW
		// NM_UPDOWN
		//

		case NM_RETURN:
		case NM_DBLCLK:
		{
            UINT wFlags = SBSP_RELATIVE | SBSP_DEFMODE;
            int iItem = -1;

            //
            // if we are in explorer mode, start out in DEFBROWSER mode,
            // otherwise, start in NEWBROWSER mode.
            //

            HWND hwnd = NULL;
            m_pShellBrowser->GetControlWindow(FCW_TREE, &hwnd);

            if(hwnd != NULL) {
                wFlags |= SBSP_DEFBROWSER;
            } else {
                wFlags |= SBSP_NEWBROWSER;
            }


			//
			// loop through all the selected items.
			// TODO: if more than two are selected, we only get to view
            // two.
			//
			 
            do {
	            LV_ITEM item;

				iItem = ListView_GetNextItem(m_hwndList, iItem, LVNI_SELECTED | LVNI_ALL);
				ZeroMemory(&item, sizeof(item));
				item.mask = LVIF_PARAM;
				item.iItem = iItem;

				if(!ListView_GetItem(m_hwndList, &item))
					break;

	            LPCITEMIDLIST pidl = (LPCITEMIDLIST)(item.lParam);
				
	            if(pidl != NULL) {
				
					//
					// don't browse into items.
					//

					if(GetLastPidlType(pidl) > PIDL_TYPE_SUBTYPE)
						break;

					m_pShellBrowser->BrowseObject(pidl, wFlags);
				}
				
				//
				// if multiple items selected, bring up a new browser
				//

				wFlags &= ~SBSP_DEFBROWSER;
				wFlags |= SBSP_NEWBROWSER;
            } while (iItem != -1);

		}

		//
		// TBN_BEGINADJUST
		// TBN_BEGINDRAG
		// TBN_CUSTHELP
		// TBN_ENDADJUST
		// TBN_ENDDRAG
		// TBN_GETBUTTONINFO
		// TBN_QUERYDELETE
		// TBN_QUERYINSERT
		// TBN_RESET
		// TBN_TOOLBARCHANGE
		//

        case TBN_BEGINDRAG:
        default:
            return 0;
    }

    return 0;
}


LRESULT
CShellView::OnSize(
    WORD wWidth,
    WORD wHeight
    )
{
    //
    // resize the ListView to fit our window
    //

    if(m_hwndList)
        MoveWindow(m_hwndList, 0, 0, wWidth, wHeight, TRUE);

    return 0;
}


LRESULT
CShellView::OnCreate(
    void
    )
{
    //
    // create the ListView
    //

    if(CreateList())
    {
        if(InitList())
        {
            FillList();
        }
    }

    return 0;
}


BOOL
CShellView::CreateList(
    void
    )
{
    DWORD dwStyle;

    //
    // fetch the prior listview style and use
    // that as the new listview style.
    //


    switch (m_FolderSettings.ViewMode) {
        case FVM_ICON:
            dwStyle = LVS_ICON;
            break;
        case FVM_SMALLICON:
            dwStyle = LVS_SMALLICON;
            break;
        case FVM_LIST:
            dwStyle = LVS_LIST;
            break;
        case FVM_DETAILS:
            dwStyle = LVS_REPORT;
            break;
        default:
            dwStyle = LVS_ICON;
            break;
    }

    m_hwndList = CreateWindowEx(
                        WS_EX_CLIENTEDGE,
                        WC_LISTVIEW,
                        NULL,
                        WS_TABSTOP |
                        WS_VISIBLE |
                        WS_CHILD |
                        WS_BORDER |
                        LVS_SORTASCENDING |
                        dwStyle |
                        LVS_NOSORTHEADER |
                        0,
                        0,
                        0,
                        0,
                        0,
                        m_hWnd,
                        (HMENU)ID_LISTVIEW,
                        g_hInst,
                        NULL
                        );

    return (BOOL)(NULL != m_hwndList);
}


BOOL
CShellView::InitList(
    void
    )
{
    LV_COLUMN   lvColumn;
    HIMAGELIST  himlSmall;
    HIMAGELIST  himlLarge;
    RECT        rc;
    TCHAR       szString[MAX_PATH];

    //
    // empty the list
    //

    ListView_DeleteAllItems(m_hwndList);

    //
    // initialize the columns
    //

    lvColumn.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
    lvColumn.fmt = LVCFMT_LEFT;
    lvColumn.pszText = szString;

    lvColumn.cx = m_nColumn1;
    LoadString(g_hInst, IDS_COLUMN1, szString, sizeof(szString));
    ListView_InsertColumn(m_hwndList, 0, &lvColumn);

    GetClientRect(m_hWnd, &rc);

    lvColumn.cx = m_nColumn2;
    LoadString(g_hInst, IDS_COLUMN2, szString, sizeof(szString));
    ListView_InsertColumn(m_hwndList, 1, &lvColumn);

	int cxSmall;
	int cySmall;
	int cxLarge;
	int cyLarge;

	SHFILEINFO sfi;
	HIMAGELIST himl;

	//
	// get large and small icon size info from shell
	//

	himl = (HIMAGELIST)SHGetFileInfo(
			NULL,
			0,
			&sfi,
			sizeof(sfi),
			SHGFI_LARGEICON | SHGFI_SYSICONINDEX
			);

	if(!ImageList_GetIconSize(himl, &cxLarge, &cyLarge)) {
    	cxLarge = GetSystemMetrics(SM_CXICON);
    	cyLarge = GetSystemMetrics(SM_CYICON);
	}

	himl = (HIMAGELIST)SHGetFileInfo(
			NULL,
			0,
			&sfi,
			sizeof(sfi),
			SHGFI_SMALLICON | SHGFI_SYSICONINDEX
			);

	if(!ImageList_GetIconSize(himl, &cxSmall, &cySmall)) {
    	cxSmall = GetSystemMetrics(SM_CXSMICON);
    	cySmall = GetSystemMetrics(SM_CYSMICON);
	}


    //
    // Create the full-sized and small icon image lists.
    //

    himlLarge = ImageList_Create(
            cxLarge,
            cyLarge,
            ILC_COLORDDB | ILC_MASK,
            4,  // initial image count
            0
            );

    himlSmall = ImageList_Create(
            cxSmall,
            cySmall,
            ILC_COLORDDB | ILC_MASK,
            4,  // initial image count
            0
            );

    //
    // Add icons to each image list.
	// TODO cache icons
    //

    HICON hIconSmall;
	HICON hIconLarge;

    // icon 0: Provider icon

    hIconSmall = (HICON)LoadImage(g_hInst, MAKEINTRESOURCE(IDI_PROVIDER), IMAGE_ICON, cxSmall, cySmall, LR_DEFAULTCOLOR | LR_SHARED);
    hIconLarge = (HICON)LoadImage(g_hInst, MAKEINTRESOURCE(IDI_PROVIDER), IMAGE_ICON, cxLarge, cyLarge, LR_DEFAULTCOLOR | LR_SHARED);

    ImageList_AddIcon(himlSmall, hIconSmall);
    ImageList_AddIcon(himlLarge, hIconLarge);


    // icon 1: type icon

    hIconSmall = (HICON)LoadImage(g_hInst, MAKEINTRESOURCE(IDI_FOLDER), IMAGE_ICON, cxSmall, cySmall, LR_DEFAULTCOLOR | LR_SHARED);
    hIconLarge = (HICON)LoadImage(g_hInst, MAKEINTRESOURCE(IDI_FOLDER), IMAGE_ICON, cxLarge, cyLarge, LR_DEFAULTCOLOR | LR_SHARED);

    ImageList_AddIcon(himlSmall, hIconSmall);
    ImageList_AddIcon(himlLarge, hIconLarge);

    // icon 2: item icon

    hIconSmall = (HICON)LoadImage(g_hInst, MAKEINTRESOURCE(IDI_ITEM), IMAGE_ICON, cxSmall, cySmall, LR_DEFAULTCOLOR | LR_SHARED);
    hIconLarge = (HICON)LoadImage(g_hInst, MAKEINTRESOURCE(IDI_ITEM), IMAGE_ICON, cxLarge, cyLarge, LR_DEFAULTCOLOR | LR_SHARED);

    ImageList_AddIcon(himlSmall, hIconSmall);
    ImageList_AddIcon(himlLarge, hIconLarge);

    // icon 3: global type

    hIconSmall = (HICON)LoadImage(g_hInst, MAKEINTRESOURCE(IDI_GLOBAL), IMAGE_ICON, cxSmall, cySmall, LR_DEFAULTCOLOR | LR_SHARED);
    hIconLarge = (HICON)LoadImage(g_hInst, MAKEINTRESOURCE(IDI_GLOBAL), IMAGE_ICON, cxLarge, cyLarge, LR_DEFAULTCOLOR | LR_SHARED);

    ImageList_AddIcon(himlSmall, hIconSmall);
    ImageList_AddIcon(himlLarge, hIconLarge);


    //
    // Assign the image lists to the list view control.
    //

    ListView_SetImageList(m_hwndList, himlLarge, LVSIL_NORMAL);
    ListView_SetImageList(m_hwndList, himlSmall, LVSIL_SMALL);

    return TRUE;
}

BOOL
CShellView::FillList(
    void
    )
{
    LPENUMIDLIST pEnumIDList;
    ULONG ulFetched;
    LPITEMIDLIST pidl = NULL;

    pEnumIDList = new CEnumIDList(m_pidl, TRUE);
    if(pEnumIDList == NULL)
        return FALSE;

    //
    // enumerate the sub folders or items, based on the parent m_pidl.
    // note that storage is allocated for the pidlNew, which is a copy
    // of the current path + pidl - that storage is later freed during
    // the processing of LVN_DELETEITEM
    //

    while( NOERROR == pEnumIDList->Next(1, &pidl, &ulFetched) ) {

        LPCWSTR szText = GetPidlText(pidl);
        PST_KEY KeyType = GetPidlKeyType(pidl);
        DWORD dwPidlType = GetPidlType(pidl);

        //
        // make a fully qualified (absolute) pidl
        //

//        LPITEMIDLIST pidlNew = CopyCatPidl(m_pidl, pidl);

        //
        // free pidl associated with Next enumeration
        //

//        FreePidl(pidl);

        //
        // add item with absolute pidl to listview
        //

        AddListItem(m_hwndList, dwPidlType, KeyType, szText, pidl);
    }

    pEnumIDList->Release();

    return TRUE;
}

BOOL
AddListItem(
    HWND hWnd,
    DWORD dwType,
    PST_KEY KeyType,
    LPCWSTR szName,
    LPITEMIDLIST pidl
    )
/*++

    This function is a temporary hack to support image list add/set data.

    This will be replaced by an Unicode interface that operates on WinNT and
    Win95 in the near future.

--*/
{
    LVITEM lvItem;
    int nIndex;

    DWORD dwIconIndex;

    //
    // determine which icon to use.
    // when PST_KEY is not PST_KEY_LOCAL_MACHINE, use a different folder
    // icon at the type and subtype level.
    //

    switch(dwType) {
        case PIDL_TYPE_PROVIDER:
            dwIconIndex = 0;
            break;
        case PIDL_TYPE_TYPE:
        case PIDL_TYPE_SUBTYPE:
            if(KeyType == PST_KEY_CURRENT_USER)
                dwIconIndex = 1;
            else
                dwIconIndex = 3;    // global (shared) icon
            break;
        case PIDL_TYPE_ITEM:
            dwIconIndex = 2;
            break;
        default:
            dwIconIndex = 0;
    }

    ZeroMemory(&lvItem, sizeof(lvItem));

    lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
    lvItem.iItem = 0;
    lvItem.lParam = (LPARAM)pidl;
    lvItem.pszText = (LPTSTR)szName;
    lvItem.iImage = dwIconIndex; // image index

    nIndex = ListView_InsertItemU(hWnd, &lvItem);

//    ListView_SetItemTextU(hWnd, nIndex, 1, (LPWSTR)szValue);

    return TRUE;
}

