2025-11-27 16:46:48 +09:00

448 lines
12 KiB
C++

// ResLibView.cpp : implementation of the CResLibView class
//
//----------------- Dundas Software ----------------------------------------
//========================================================================
#include "stdafx.h"
#include "ResFile.h"
#include "ResLibDoc.h"
#include "ResLibView.h"
#include "OXResourceLibrary.h"
#include "OXResourceFile.h"
#include "DlgResEdit.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
// we will use this value to distinguish a folder from a resource item in the tree view
// (a resource item stores an index that can be used to fetch type, name and language
// from GetDocument()->m_sTypes, m_sNames and m_nLangs, respectively).
#define TVI_DATA_FOLDER 0xFFFFFFFF
/////////////////////////////////////////////////////////////////////////////
// CResLibView
IMPLEMENT_DYNCREATE(CResLibView, CTreeView)
BEGIN_MESSAGE_MAP(CResLibView, CTreeView)
//{{AFX_MSG_MAP(CResLibView)
ON_NOTIFY_REFLECT(TVN_ITEMEXPANDED, OnItemexpanded)
ON_NOTIFY_REFLECT(NM_DBLCLK, OnDblclk)
ON_COMMAND(ID_EDIT_CUT, OnEditCut)
ON_UPDATE_COMMAND_UI(ID_EDIT_CUT, OnUpdateEditCut)
ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
ON_UPDATE_COMMAND_UI(ID_EDIT_COPY, OnUpdateEditCopy)
ON_COMMAND(ID_EDIT_PASTE, OnEditPaste)
ON_UPDATE_COMMAND_UI(ID_EDIT_PASTE, OnUpdateEditPaste)
ON_COMMAND(ID_EDIT_CLEAR, OnEditClear)
ON_UPDATE_COMMAND_UI(ID_EDIT_CLEAR, OnUpdateEditClear)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CResLibView construction/destruction
CResLibView::CResLibView()
{
m_ilFolder.Create(IDB_IMAGELIST, 16, 0, RGB(255,0,255));
m_hRootItem = NULL;
m_nClipFormat = 0;
m_bModifiable = FALSE;
}
CResLibView::~CResLibView()
{
}
BOOL CResLibView::PreCreateWindow(CREATESTRUCT& cs)
{
cs.style |= TVS_HASLINES | TVS_LINESATROOT | TVS_HASBUTTONS;
return CTreeView::PreCreateWindow(cs);
}
/////////////////////////////////////////////////////////////////////////////
// CResLibView drawing
void CResLibView::OnDraw(CDC* pDC)
{
UNREFERENCED_PARAMETER(pDC);
CResLibDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
}
void CResLibView::OnInitialUpdate()
{
CTreeView::OnInitialUpdate();
CTreeCtrl& rTree = GetTreeCtrl();
rTree.SetImageList(&m_ilFolder, TVSIL_NORMAL);
m_nClipFormat = ((CResFileApp*)AfxGetApp())->m_nClipFormat;
m_pDoc = GetDocument();
m_pResLib = &m_pDoc->m_ResLib;
// calling IsModifiable() once instead of repeatedly checking it
m_bModifiable = m_pResLib->IsModifiable();
if (m_pResLib == NULL)
return;
m_hRootItem = rTree.InsertItem(m_pResLib->GetFileName() +
(m_bModifiable ? _T("") : _T(" <<<READ ONLY>>>")));
rTree.SetItemData(m_hRootItem, TVI_DATA_FOLDER);
CString sType, sName, sPrevType;
HTREEITEM hTypeItem = NULL;
int iImage = 0;
for (int i = 0; i < m_pDoc->m_sTypes.GetSize(); i++)
{
sType = m_pDoc->m_sTypes[i];
sName = m_pDoc->m_sNames[i];
if (sType != sPrevType)
{
sPrevType = sType;
GetTypeOutput(sType, iImage);
hTypeItem = rTree.InsertItem(sType, m_hRootItem);
rTree.SetItemData(hTypeItem, TVI_DATA_FOLDER);
}
GetNameOutput(sName, m_pDoc->m_nLangs[i]);
ASSERT(hTypeItem);
HTREEITEM hResItem = rTree.InsertItem(sName, iImage, iImage, hTypeItem);
rTree.SetItemData(hResItem, (DWORD)i);
}
rTree.Expand(m_hRootItem, TVE_EXPAND);
rTree.SortChildren(m_hRootItem);
}
void CResLibView::GetTypeOutput(CString& sType, int& iImage)
{
// format output a little bit: incomplete (see WinUser.h)
switch (COXResourceFile::OXResCStringToInt(sType))
{
case (DWORD)RT_CURSOR: sType = _T("Cursor (hardware-dependent)");
iImage = 3; break;
case (DWORD)RT_BITMAP: sType = _T("Bitmap");
iImage = 4; break;
case (DWORD)RT_ICON: sType = _T("Icon (hardware-dependent)");
iImage = 5; break;
case (DWORD)RT_MENU: sType = _T("Menu");
iImage = 6; break;
case (DWORD)RT_DIALOG: sType = _T("Dialog");
iImage = 7; break;
case (DWORD)RT_STRING: sType = _T("String Table");
iImage = 8; break;
case (DWORD)RT_ACCELERATOR: sType = _T("Accelerator");
iImage = 9; break;
case (DWORD)RT_RCDATA: sType = _T("Raw Data");
iImage = 2; break;
case (DWORD)RT_VERSION: sType = _T("Version");
iImage = 10; break;
case (DWORD)RT_TOOLBAR: sType = _T("Toolbar");
iImage = 11; break;
case (DWORD)RT_GROUP_CURSOR:sType = _T("Cursor (hardware-independent)");
iImage = 3; break;
case (DWORD)RT_GROUP_ICON: sType = _T("Icon (hardware-independent)");
iImage = 5; break;
case 0: /* string */ sType = CString("\"") + sType + _T("\"");
default: iImage = 2;
}
}
void CResLibView::GetNameOutput(CString& sName, WORD nLang)
{
if (COXResourceFile::OXResCStringToInt(sName) == 0)
sName = CString("\"") + sName + _T("\"");
if (nLang != MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL) &&
nLang != MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT) &&
nLang != MAKELANGID(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT) &&
nLang != MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US))
{
CString sLang;
sLang.Format(_T(" (LangID: %d)"), nLang);
sName += sLang;
}
}
/////////////////////////////////////////////////////////////////////////////
// CResLibView diagnostics
#ifdef _DEBUG
void CResLibView::AssertValid() const
{
CTreeView::AssertValid();
}
void CResLibView::Dump(CDumpContext& dc) const
{
CTreeView::Dump(dc);
}
CResLibDoc* CResLibView::GetDocument() // non-debug version is inline
{
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CResLibDoc)));
return (CResLibDoc*)m_pDocument;
}
#endif //_DEBUG
/////////////////////////////////////////////////////////////////////////////
// CResLibView message handlers
void CResLibView::OnItemexpanded(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
CTreeCtrl& rTree = GetTreeCtrl();
HTREEITEM hItem = pNMTreeView->itemNew.hItem;
int iImage = ((pNMTreeView->itemNew.state & TVIS_EXPANDED) == TVIS_EXPANDED) ? 1 : 0;
rTree.SetItemImage(hItem, iImage, iImage);
rTree.SortChildren(hItem);
*pResult = 0;
}
void CResLibView::OnDblclk(NMHDR* pNMHDR, LRESULT* pResult)
{
UNREFERENCED_PARAMETER(pNMHDR);
CTreeCtrl& rTree = GetTreeCtrl();
CPoint pt; GetCursorPos(&pt); ScreenToClient(&pt); UINT nFlag;
HTREEITEM hItem = rTree.HitTest(pt, &nFlag);
if (hItem == NULL) return;
DWORD nIndex = rTree.GetItemData(hItem);
if (nIndex != TVI_DATA_FOLDER)
{
CWaitCursor wait;
// When dbl-click on a resource that was pasted from a previous copy,
// we need to fetch the temporary COXResourceFile we created during pasting
// (those we let m_pResLib handle their destruction in OnEditPaste()).
COXResourceFile* pResFile = m_pResLib->GetOpenedResFile(m_pDoc->m_sTypes[nIndex],
m_pDoc->m_sNames[nIndex], m_pDoc->m_nLangs[nIndex]);
BOOL bNeedDelete = FALSE; // if it was pasted and managed by the library
if(pResFile == NULL) // not those pasted items
{
pResFile = new COXResourceFile;
if (!pResFile->Open(CFile::modeReadWrite, m_pResLib, FALSE, m_pDoc->m_sTypes[nIndex],
m_pDoc->m_sNames[nIndex], m_pDoc->m_nLangs[nIndex]))
{
delete pResFile;
pResFile = NULL;
AfxMessageBox(_T("Failed to open the specified resource."));
}
else
// in this demo, the dlg won't edit the file, otherwise, we can leave an
// edited file without deleting it (but we have to specify AutoDeleteByLib
// in Open() above), and delete unchanged file (it's ok even if it's set
// bAutoDeleteByLib)
bNeedDelete = TRUE;
}
if(pResFile)
{
CDlgResEdit dlg(pResFile, this);
dlg.DoModal();
// this dlg can not really be used to edit binary data directly for simplicity
// of this demo program; for people who have interest in extending the
// functionality of this dialog, please refer to Developer Studio's editor as
// a good example (to view it, right click on a resource in your resource view,
// and choose "Open Binary Data")
// should we have edited the resource, we may have written something like:
// if(dlg.DoModal() == IDOK) dlg.m_ResFile.Flush();
}
if (bNeedDelete)
{
pResFile->Abort(); // destructor will call Flush(), which is not necessary here
delete pResFile;
}
}
*pResult = 0;
}
int CResLibView::GetSelectedResIndex()
{
CTreeCtrl& rTree = GetTreeCtrl();
HTREEITEM hItem = rTree.GetSelectedItem();
if (hItem)
{
DWORD nIndex = rTree.GetItemData(hItem);
if (nIndex != TVI_DATA_FOLDER) return (int)nIndex;
}
return -1; // if folder was selected
}
void CResLibView::OnUpdateEditCut(CCmdUI* pCmdUI)
{
pCmdUI->Enable(m_pResLib && m_bModifiable && GetSelectedResIndex() != -1);
}
void CResLibView::OnUpdateEditCopy(CCmdUI* pCmdUI)
{
pCmdUI->Enable(GetSelectedResIndex() != -1);
}
void CResLibView::OnUpdateEditClear(CCmdUI* pCmdUI)
{
pCmdUI->Enable(m_pResLib && m_bModifiable && GetSelectedResIndex() != -1);
}
void CResLibView::OnUpdateEditPaste(CCmdUI* pCmdUI)
{
COleDataObject oleObj;
pCmdUI->Enable(m_pResLib && m_bModifiable && oleObj.AttachClipboard() &&
oleObj.IsDataAvailable(m_nClipFormat));
}
void CResLibView::OnEditCut()
{
OnEditCopy();
OnEditClear();
}
void CResLibView::OnEditClear()
{
CTreeCtrl& rTree = GetTreeCtrl();
HTREEITEM hItem = rTree.GetSelectedItem();
if (hItem == NULL) return;
DWORD nIndex = rTree.GetItemData(hItem);
if (nIndex == TVI_DATA_FOLDER) return;
m_pResLib->DeleteResource(m_pDoc->m_sTypes[nIndex], m_pDoc->m_sNames[nIndex],
m_pDoc->m_nLangs[nIndex]);
// ... we set its name to empty to indicate it's deleted, useful when checking
// if a type folder has been added
m_pDoc->m_sNames[nIndex].Empty();
m_pDoc->SetModifiedFlag();
HTREEITEM hTypeItem = rTree.GetParentItem(hItem);
ASSERT(hTypeItem);
rTree.DeleteItem(hItem);
if (rTree.GetChildItem(hTypeItem) == NULL)
rTree.DeleteItem(hTypeItem);
}
void CResLibView::OnEditCopy()
{
int nIndex = GetSelectedResIndex();
if (nIndex == -1) return;
COXResourceFile resFile;
if (!resFile.Open(CFile::modeRead, m_pResLib, FALSE, m_pDoc->m_sTypes[nIndex],
m_pDoc->m_sNames[nIndex], m_pDoc->m_nLangs[nIndex]))
{
TRACE0("Failed to open the specified resource to copy.\n");
return;
}
// we definitely want to include tags (type, name and lang) in the hGlobal
HGLOBAL hGlobal = resFile.DetachEx();
if (hGlobal == NULL)
{
TRACE0("Failed to load data from the specified resource to copy.\n");
return;
}
COleDataSource* pOleSource = new COleDataSource;
pOleSource->CacheGlobalData(m_nClipFormat, hGlobal);
pOleSource->SetClipboard();
}
void CResLibView::OnEditPaste()
{
COleDataObject oleObj;
if (!oleObj.AttachClipboard() || !oleObj.IsDataAvailable(m_nClipFormat))
return;
HGLOBAL hGlobal = oleObj.GetGlobalData(m_nClipFormat);
if (hGlobal == NULL)
return;
COXResourceFile* pResFile = new COXResourceFile;
if(!pResFile->SetHandleEx(hGlobal))
{
TRACE0("Failed to load resource from clipboard.\n");
delete pResFile;
return;
}
// 1st TRUE to make sure when m_pResLib goes out of scope, pResFile will be deleted;
// 2nd TRUE to make sure we are adding, not replacing
pResFile->SetResourceLibrary(m_pResLib, TRUE, TRUE);
// you could ask user to input a unique name here, but
// the default implementation is quite exhaustive already
m_pDoc->SetModifiedFlag();
BOOL bTypeExist = FALSE;
UINT i = 0;
for (i = 0; i < (UINT)m_pDoc->m_sTypes.GetSize(); i++)
{
if (!m_pDoc->m_sNames[i].IsEmpty() &&
m_pDoc->m_sTypes[i] == pResFile->GetResType())
{
bTypeExist = TRUE;
break;
}
}
DWORD nIndexNewItem = (DWORD)m_pDoc->m_sTypes.Add(pResFile->GetResType());
m_pDoc->m_sNames.Add(pResFile->GetResName());
m_pDoc->m_nLangs.Add(pResFile->GetResLanguage());
CString sType, sName; int iImage;
sType = pResFile->GetResType();
sName = pResFile->GetResName();
GetTypeOutput(sType, iImage);
GetNameOutput(sName, pResFile->GetResLanguage());
CTreeCtrl& rTree = GetTreeCtrl();
HTREEITEM hTypeItem = NULL;
if (bTypeExist)
{
hTypeItem = FindTypeHItem(i);
}
else
{
hTypeItem = rTree.InsertItem(sType, m_hRootItem);
rTree.SetItemData(hTypeItem, TVI_DATA_FOLDER);
}
ASSERT(hTypeItem);
HTREEITEM hResItem = rTree.InsertItem(sName, iImage, iImage, hTypeItem);
rTree.SortChildren(hTypeItem);
rTree.EnsureVisible(hResItem);
rTree.SelectItem(hResItem);
rTree.SetItemData(hResItem, (DWORD)nIndexNewItem);
}
HTREEITEM CResLibView::FindTypeHItem(UINT nIndex)
{
CTreeCtrl& rTree = GetTreeCtrl();
for (HTREEITEM hTypeIter = rTree.GetChildItem(m_hRootItem); hTypeIter;
hTypeIter = rTree.GetNextSiblingItem(hTypeIter))
{
for (HTREEITEM hResIter = rTree.GetChildItem(hTypeIter); hResIter;
hResIter = rTree.GetNextSiblingItem(hResIter))
{
if (rTree.GetItemData(hResIter) == nIndex)
return hTypeIter;
}
}
return NULL;
}