Share via


© 2004 Microsoft Corporation. All rights reserved.

Figure 2 OpenDlg

MyDlg.h
  ////////////////////////////////////////////////////////////////
// MSDN Magazine — January 2002
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// Compiles with Visual C++ 6.0 for Windows XP and probably Windows 2000 
// too.
// Set tabsize = 3 in your editor.
//
#include "FileDlgHelper.h"
//////////////////
// My open dialog — customized to add preview, debug panes, and Select All
// button.
//
class CMyOpenDlg : public CFileDialog {
protected:
   CFileDlgHelper m_dlghelper;          // helper for file dialogs
   CEdit m_edit1;                       // edit control for preview
   CEdit m_edit2;                       // another for debug info
   // Heplers
   CString GetTextPreview(LPCTSTR pszPath);
   void AddText(CEdit& wndEdit, LPCTSTR lpText, BOOL bClear=FALSE);
   void ShowFileInfo();
public:
   CMyOpenDlg();
   // Handle CDN_ notifications
   virtual void OnFileNameChange();
   virtual void OnFolderChange();
   virtual void OnTypeChange();
   // Command/message/UI handlers/overrides
   virtual int DoModal();
   virtual BOOL OnInitDialog();
   afx_msg void OnSize(UINT nType, int cx, int cy);
   afx_msg void OnSelectAll();
   afx_msg void OnUpdateSelectAll(CCmdUI* pCmdUI);
   DECLARE_DYNAMIC(CMyOpenDlg)
   DECLARE_MESSAGE_MAP()
};

MyDlg.cpp
  ////////////////////////////////////////////////////////////////
// MSDN Magazine — January 2002
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// Compiles with Visual C++ 6.0 for Windows XP and probably Windows 2000 
// too.
// Set tabsize = 3 in your editor.
//
#include "StdAfx.h"
#include "MyDlg.h"
#include "FileDlgHelper.h"
#include "Resource.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
// IDs of controls in Explorer-style dialog, from Spy
#define ID_FILESOFTYPE     0x0441
#define ID_FILENAME        0x0442
#define ID_LOOKIN          0x0443
IMPLEMENT_DYNAMIC(CMyOpenDlg, CFileDialog)
BEGIN_MESSAGE_MAP(CMyOpenDlg, CFileDialog)
   ON_COMMAND(ID_SELECT_ALL,OnSelectAll)
   ON_UPDATE_COMMAND_UI(ID_SELECT_ALL,OnUpdateSelectAll)
   ON_WM_SIZE()
END_MESSAGE_MAP()
//////////////////
// does path ends in ".txt" ?
//
static BOOL IsTextFileName(CString fn)
{
   fn.MakeLower();
   return fn.Right(4)==".txt";
}
////////////////
// Constructor
//
CMyOpenDlg::CMyOpenDlg() : CFileDialog(TRUE,
   NULL,                                 // no default extension
   NULL,                                 // ..or file name
   OFN_HIDEREADONLY|OFN_ALLOWMULTISELECT,
   _T("Text Files (*.txt)|*.txt|All Files (*.*)|*.*||"))
{
   m_ofn.lpstrTitle = _T("Select a file or folder");
}
//////////////////
// Initialize dialog.
// For Explorer, this is actually a child of the main dialog.
//
BOOL CMyOpenDlg::OnInitDialog()
{
   // subclass controls...
   m_edit1.SubclassDlgItem(IDC_MYDLGINFO1, this);
   m_edit2.SubclassDlgItem(IDC_MYDLGINFO2, this);
   // initialize helper
   m_dlghelper.Init(this);
   return CFileDialog::OnInitDialog();
}
//////////////////
// Select all text files. This won't work if user has checked "Hide
// extensions for known file types" because then the item name in the list
// box doesn't end in .txt !!
//
void CMyOpenDlg::OnSelectAll()
{
   CFileDlgHelper& fdh = m_dlghelper;
   CListCtrl* plc = fdh.GetListCtrl();
   for (int i=0; i<plc->GetItemCount(); i++) {
      CString fn = fdh.GetItemName(i);
      if (IsTextFileName(fn)) {
         plc->SetItemState(i,LVIS_SELECTED,LVIS_SELECTED);
      }
   }
   plc->SetFocus();
}
//////////////////
// Update "Select All" button: disable if no .txt files. This won't work if
// user has checked "Hide extensions for known file types" because then the
// item name in the list box doesn't end in .txt !!
//
void CMyOpenDlg::OnUpdateSelectAll(CCmdUI* pCmdUI)
{
   CFileDlgHelper& fdh = m_dlghelper;
   CListCtrl* plc = fdh.GetListCtrl();
   for (int i=0; i<plc->GetItemCount(); i++) {
      CString fn = fdh.GetItemName(i);
      if (IsTextFileName(fn)) {
         pCmdUI->Enable(TRUE);
         return;
      }
   }
   pCmdUI->Enable(FALSE);
}
//////////////////
// DoModal override: use my template
//
int CMyOpenDlg::DoModal()
{
   // Add my custom dialog to bottom
   m_ofn.lpTemplateName = MAKEINTRESOURCE(IDD_MYOPENDLG);
   m_ofn.Flags |= OFN_ENABLETEMPLATE;
   return CFileDialog::DoModal();
}
//////////////////
// User selected new file (CDN_SELCHANGE)
//
void CMyOpenDlg::OnFileNameChange()
{
   ShowFileInfo();
}
//////////////////
// User selected new folder (CDN_SELCHANGE)
//
void CMyOpenDlg::OnFolderChange()
{
   ShowFileInfo();
}
//////////////////
// User selected new file type (from drop-down)
//
void CMyOpenDlg::OnTypeChange()
{
   ShowFileInfo();
}
//////////////////
// Common helper: show information in the preview and debug panes
//
void CMyOpenDlg::ShowFileInfo()
{
   CFileDlgHelper& fdh = m_dlghelper;
   CString path = GetPathName();
   CString fldr = GetFolderPath();
   // Create debug message
   //
   CString s;
   s.Format("GetPathName=%s\nGetFolderPath=%s\n",
      (LPCTSTR)path, (LPCTSTR)fldr);
   int nSelected = 0;
   s += "Selected:\n";
   CListCtrl* plc = fdh.GetListCtrl();
   POSITION pos = plc->GetFirstSelectedItemPosition();
   while (pos) {
      int i = plc->GetNextSelectedItem(pos);
      CString name = fdh.GetItemName(i);
      CString type;
      if (fdh.IsItemFolder(i))
         type = "(FOLDER)";
      CString s2;
      s2.Format(" %s %s\n", (LPCTSTR)name, (LPCTSTR)type);
      s += s2;
      nSelected++;
   }
   AddText(m_edit2, s, TRUE);
   // Create preview text
   //
   s.Empty();
   if (nSelected==1 && IsTextFileName(path)) {
      s = GetTextPreview(path);
   } else if (nSelected>1) {
      s = "[Multiple selected]";
   } else if (nSelected==0) {
      s = "[None selected]";
   }
   AddText(m_edit1,s,TRUE);
}
//////////////////
// Preview: Read first 256 characters of text file
//
CString CMyOpenDlg::GetTextPreview(LPCTSTR pszPath)
{
   CString buf;
   CFile f;
   if (f.Open(pszPath,CFile::modeRead)) {
      int len = f.Read(buf.GetBuffer(256),256);
      buf.ReleaseBuffer();
      if (len==256)
         buf += "...";
   }
   return buf;
}
//////////////////
// Helper adds text to an edit control that I've added to the open dialog
//
void CMyOpenDlg::AddText(CEdit& wndEdit, LPCTSTR lpText, BOOL bClear)
{
   if (wndEdit) {
      // Convert \n to \n\r for Windows brain-damaged edit control !&#%!
      // It's 2001, and I'm still writing code like this!
      //
      LPCTSTR src = lpText;
      TCHAR buf[1024];
      TCHAR* dst = buf;
      TCHAR* endbuf = buf + sizeof(buf) - 1;
      while (*src && dst < endbuf) {
         if (*src == '\n')
            *dst++ = '\r';
         *dst++ = *src++;
      }
      *dst = 0;
      wndEdit.SetSel(bClear ? 0 : -1, -1);   // end of edit text
      wndEdit.ReplaceSel(buf);               // append string..
      wndEdit.SendMessage(EM_SCROLLCARET);   // ..and show caret
   }
}
//////////////////
// Dialog was sized: reposition my controls. Zzzzz.
//
void CMyOpenDlg::OnSize(UINT nType, int cx, int cy)
{
   CWnd* pDlg = GetParent();
   CRect rcDlg;
   pDlg->GetWindowRect(&rcDlg);
   CWnd* pCancel = pDlg->GetDlgItem(IDCANCEL);
   ASSERT(pCancel);
   CRect rcCancel;
   pCancel->GetWindowRect(&rcCancel);
   int cxRightMargin = rcDlg.right-rcCancel.right;
   CWnd* pSelAll = GetDlgItem(ID_SELECT_ALL);
   ASSERT(pSelAll);
   CRect rc;
   pSelAll->GetWindowRect(&rc);
   rc.left = rcDlg.right - cxRightMargin - rcCancel.Width();
   rc.right = rc.left + rcCancel.Width();
   rc.bottom= rc.top  + rcCancel.Height();
   ScreenToClient(&rc);
   pSelAll->SetWindowPos(NULL,rc.left,rc.top,rc.Width(),rc.Height(),0);
   for (int i = IDC_MYDLGINFO1; i<= IDC_MYDLGINFO2; i++) {
      CWnd* pInfoWnd = GetDlgItem(i);
      ASSERT(pInfoWnd);
      pInfoWnd->GetWindowRect(&rc);
      rc.right = rcDlg.right - cxRightMargin;
      ScreenToClient(&rc);
      pInfoWnd->SetWindowPos(NULL,rc.left,rc.top,rc.Width(),rc.Height(),0);
   }
}

FileDlgHelper.h
  ////////////////////////////////////////////////////////////////
// MSDN Magazine — January 2002
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// Compiles with Visual C++ 6.0 for Windows XP and probably Windows 2000 
// too.
// Set tabsize = 3 in your editor.
//
#pragma once
#include "SubClass.h"
class CFileDlgHelper;
//////////////////
// Used to subclass the file dialog
//
class CFileDialogHook : public CSubclassWnd {
protected:
   CFileDlgHelper* m_pHelper;           // ptr to helper class
   virtual LRESULT WindowProc(UINT msg, WPARAM wp, LPARAM lp);
   friend CFileDlgHelper;
};
//////////////////
// Used to subclass the file dialog's parent/owner
//
class CFileDialogOwnerHook : public CSubclassWnd {
protected:
   CFileDlgHelper* m_pHelper;           // ptr to helper class
   CFileDialog* m_pDlg;                 // ptr your CFileDialog dialog derivate
   HWND m_hwndParent;                   // HWND of real open file dialog
   virtual LRESULT WindowProc(UINT msg, WPARAM wp, LPARAM lp);
   void Init(CWnd* pOwner, CFileDialog* pDlg);
   friend CFileDlgHelper;
};
//////////////////
// Class to help parsing file open dialog stuff
//
class CFileDlgHelper {
protected:
   CFileDialogHook m_dlghook;           // subclass dialog
   CFileDialogOwnerHook m_ownerhook;    // subclass dialog's owner
   CFileDialog* m_pDlg;                 // your CFileDialog
   BOOL m_bUpdateUI;                    // do idle UI update?
public:
   CFileDlgHelper();
   ~CFileDlgHelper();
   // You must call this from your dialog's OnInitDialog
   void Init(CFileDialog* pDlg);
   // usefull fns for you to call
   CListCtrl* GetListCtrl();              // get list control
   CString GetItemName(int i);            // get filename w/o path
   BOOL IsItemFolder(int i);              // is this item a folder?
   friend CFileDialogHook;
   friend CFileDialogOwnerHook;
};

FileDlgHelper.cpp
  ////////////////////////////////////////////////////////////////
// MSDN Magazine — January 2002
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// Compiles with Visual C++ 6.0 for Windows XP and probably Windows 2000
// too.
// Set tabsize = 3 in your editor.
//
#include "StdAfx.h"
#include "FileDlgHelper.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////
// test whether pathname is directory
//
static BOOL IsFolder(LPCTSTR pathname)
{
   struct stat st;
   return stat(pathname, &st)==0 && (st.st_mode & _S_IFDIR);
}
CFileDlgHelper::CFileDlgHelper()
{
}
CFileDlgHelper::~CFileDlgHelper()
{
}
//////////////////
// Initialize: subclass appropriate windows
//
void CFileDlgHelper::Init(CFileDialog* pDlg)
{
   // cubclass dialog
   ASSERT(pDlg);
   m_pDlg = pDlg; 
   m_dlghook.HookWindow(pDlg);
   // subclass dialog's owner/parent
   CWnd* pWnd = pDlg->GetParent();
   ASSERT(pWnd);
   pWnd = pWnd->GetParent();
   ASSERT(pWnd);
   m_ownerhook.Init(pWnd, pDlg);
   // hook up
   m_dlghook.m_pHelper = this;
   m_ownerhook.m_pHelper = this;
}
//////////////////
// Get name of item — what you see in the list control
//
CString CFileDlgHelper::GetItemName(int i)
{
   return GetListCtrl()->GetItemText(i,0);
}
//////////////////
// Find out if item is a folder.
//
BOOL CFileDlgHelper::IsItemFolder(int i)
{
   // Concatenate current folder name with item name to get full path name.
   // This won't work in general—the item could be a link or shell
   // object—but then IsFolder returns FALSE anyway, correctly.
   //
   CString path = m_pDlg->GetFolderPath();
   int len = path.GetLength();
   if (len==0 || path[len-1] != '\\')
      path += '\\';
   path += GetItemName(i);
   return IsFolder(path);
}
//////////////////
// Get underlying list control
//
CListCtrl* CFileDlgHelper::GetListCtrl()
{
   ASSERT(m_pDlg);
   CWnd* pWnd = m_pDlg->GetParent()->GetDlgItem(lst2);
   ASSERT(pWnd);
   CListCtrl* plc = (CListCtrl*)pWnd->GetDlgItem(1);
   ASSERT(plc);
#ifdef DEBUG
   char classname[32];
   ::GetClassName(plc->m_hWnd, classname, sizeof(classname));
   ASSERT(strcmp(classname,"SysListView32")==0);
#endif
   return plc;
}
//////////////////
// virtual window proc to trap dialog messages
//
LRESULT CFileDialogHook::WindowProc(UINT msg, WPARAM wp, LPARAM lp)
{
   if (msg==WM_COMMAND || msg==WM_NOTIFY) {
      m_pHelper->m_bUpdateUI = TRUE;
   }
   return CSubclassWnd::WindowProc(msg, wp, lp);
}
void CFileDialogOwnerHook::Init(CWnd* pOwner, CFileDialog* pDlg)
{
   HookWindow(pOwner);
   m_pDlg = pDlg;
   m_hwndParent = pDlg->GetParent()->m_hWnd;
   ASSERT(m_hwndParent);
}
//////////////////
// virtual window proc to trap dialog parent messages
//
LRESULT CFileDialogOwnerHook::WindowProc(UINT msg,
   WPARAM wp, LPARAM lp)
{
   if (msg==WM_ENTERIDLE && (HWND)lp==m_hwndParent) {
      if (m_pHelper->m_bUpdateUI) {
         m_pDlg->UpdateDialogControls(m_pDlg, FALSE);
         m_pHelper->m_bUpdateUI=FALSE;
      }
   }
   return CSubclassWnd::WindowProc(msg, wp, lp);
}