Author Topic: Question: Delete a named layout from Database  (Read 10032 times)

0 Members and 1 Guest are viewing this topic.

Dream.Fei

  • Guest
Question: Delete a named layout from Database
« on: January 25, 2008, 04:26:39 AM »
I'm try to delete a named layout from a file.

Becasue the file maybe not opend in th Application. so , I'm try to delete it by a AcDbDatabase object.

Code: [Select]
Acad::ErrorStatus DwgTool::delFromFile(__in const CString &sLayoutName, __in const CString &sFileName)
{
Acad::ErrorStatus es = Acad::eInvalidInput;

AcApDocument *pDoc = FeiDocument::FindDocumentNamed(sFileName);
if (pDoc)
{
acDocManager->lockDocument(pDoc);
es = delFromDatabase(sLayoutName, pDoc->database());
if (pDoc == acDocManager->mdiActiveDocument()) {
AcApLayoutManager *pLayoutMan = (AcApLayoutManager*)acdbHostApplicationServices()->layoutManager();
pLayoutMan->updateLayoutTabs();
}
acDocManager->unlockDocument(pDoc);
}
else
{
AcDbDatabase *pDb = new AcDbDatabase(false, true);
pDb->readDwgFile(sFileName);
if ((es = delFromDatabase(sLayoutName, pDb)) == Acad::eOk)
pDb->saveAs(sFileName);
delete pDb;
pDb = 0;
}
return (es);
}

Code: [Select]
Acad::ErrorStatus DwgTool::delFromDatabase(__in const CString &sLayoutName, __in AcDbDatabase *pDb)
{
Acad::ErrorStatus es = Acad::eNoDatabase;

if ((es = delEntity(sLayoutName, pDb)) == Acad::eOk)
es = delFromDictionary(sLayoutName, pDb);

return (es);
}

The first thing it's delete all entity on this named layout:
Code: [Select]
Acad::ErrorStatus DwgTool::delEntity(const CString &sLayoutName, AcDbDatabase *pDb)
{
AcDbObjectIdArray removeIds;
removeIds.removeAll();
if (!pDb)
pDb = acdbHostApplicationServices()->workingDatabase();
if (!pDb)
return (Acad::eNoDatabase);

Acad::ErrorStatus es = Acad::eInvalidInput;

AcDbBlockTable *pTab = 0;
if ((es = pDb->getSymbolTable((AcDbBlockTable*&)pTab, AcDb::kForWrite)) == Acad::eOk)
{
AcDbBlockTableIterator *pTabItr = 0;
if ((es = pTab->newIterator(pTabItr)) == Acad::eOk)
{
for (pTabItr->start(); !pTabItr->done(); pTabItr->step())
{
AcDbBlockTableRecord *pRcd = 0;
if ((es = pTabItr->getRecord(pRcd, AcDb::kForWrite)) == Acad::eOk)
{
AcDbObjectId rcdId = pRcd->getLayoutId();
AcDbLayout *pLayout = 0;
if (acdbOpenObject((AcDbObject*&)pLayout, rcdId, AcDb::kForWrite) == Acad::eOk)
{
TCHAR* layoutName = _T("");
if ((es = pLayout->getLayoutName(layoutName)) == Acad::eOk)
{
if (_tcscmp(sLayoutName, layoutName) == 0)
{
AcDbBlockTableRecordIterator *pRcdItr = 0;
if ((es = pRcd->newIterator(pRcdItr)) == Acad::eOk)
{
AcDbEntity *pEnt = 0;
if ((es = pRcdItr->getEntity(pEnt, AcDb::kForWrite)) == Acad::eOk)
{
if ((es = pEnt->erase()) != Acad::eOk)
acutPrintf(_T("\nError: DwgTool::delEntity -> AcDbEntity::erase -> %s on %s."), acadErrorStatusText(es), pEnt->isA()->name());
else
removeIds.append(pEnt->id());

pEnt->close();
if (pEnt->isErased())
pEnt = 0;
}
else
acutPrintf(_T("\nError: DwgTool::delEntity -> AcDbBlockTableRecordIterator::getEntity -> %s"), acadErrorStatusText(es));

delete pRcdItr;
pRcdItr = 0;
}
if(pRcd->erase() == Acad::eOk)
removeIds.append(pRcd->id());
}
}
else
acutPrintf(_T("\nError: DwgTool::delEntity -> AcDbLayout::getLayoutName -> %s"), acadErrorStatusText(es));
pLayout->close();
}
pRcd->close();
if (pRcd->isErased())
pRcd = 0;
}
else
acutPrintf(_T("\nError: DwgTool::delEntity -> AcDbBlockTableRecord::getRecord -> %s"), acadErrorStatusText(es));
}
delete pTabItr;
pTabItr = 0;
}
else
acutPrintf(_T("\nError: DwgTool::delEntity -> AcDbBlockTable::newIterator -> %s"), acadErrorStatusText(es));
pTab->close();
}
else
acutPrintf(_T("\nError: DwgTool::delEntity -> AcDbDatabase::getSymbolTable -> %s"), acadErrorStatusText(es));

if (!removeIds.isEmpty())
pDb->purge(removeIds);

if (es == Acad::eOk)
pDb->save();

return (es);
}

if susceefuly , delete the namedlayout from LayoutDictionary
Code: [Select]
Acad::ErrorStatus DwgTool::delFromDictionary(const CString &sLayoutName, AcDbDatabase *pDb)
{
AcDbObjectIdArray removeIds;
removeIds.removeAll();

Acad::ErrorStatus es =  Acad::eNoDatabase;
if (!pDb)
pDb = acdbHostApplicationServices()->workingDatabase();
if (!pDb)
return (es);

AcDbDictionary *pDict = 0;
if ((es = pDb->getLayoutDictionary(pDict, AcDb::kForWrite)) == Acad::eOk)
{
AcDbDictionaryIterator *pItr = pDict->newIterator();
if (pItr)
{
for (; !pItr->done(); pItr->next())
{
AcDbObject *pObj = 0;
if ((es = pItr->getObject(pObj, AcDb::kForWrite)) == Acad::eOk)
{
if (pObj->isKindOf(AcDbLayout::desc()) == Adesk::kTrue)
{
if (_tcscmp(sLayoutName, pItr->name()) == 0)
{
if ((es = pObj->erase()) != Acad::eOk)
acutPrintf(_T("\nError: DwgTool::delFromDictionary -> AcDbObject::erase -> %s"), acadErrorStatusText(es));
else
{
pDict->remove(pObj->id());
removeIds.append(pObj->id());
}
}
}
pObj->close();
if (pObj->isErased())
pObj = 0;
}
else
acutPrintf(_T("\nError: DwgTool::delFromDictionary -> AcDbDictionaryIterator::getObject -> %s"), acadErrorStatusText(es));
}
delete pItr;
pItr = 0;
}
pDict->close();
}

if (!removeIds.isEmpty())
pDb->purge(removeIds);

if (es == Acad::eOk)
pDb->save();

return (es);
}

however, this work fine. but when I open this file on CAD2008, I received a error message: "ePermanentlyErased"

so.... any help?
« Last Edit: January 25, 2008, 04:35:49 AM by Dream.Fei »

It's Alive!

  • BricsCAD
  • Needs a day job
  • Posts: 6940
  • AKA Daniel
Re: Question: Delete a named layout from Database
« Reply #1 on: January 25, 2008, 09:03:06 AM »
Just a thought, For Arx applications you might get more responses to your post, if you attach a “ready to compile solution” with a test command already setup.
Just makes it easier for people to test  :-)

Glenn R

  • Water Moccasin
  • Posts: 1932
  • What idiot child of married cousins wrote this?!
Re: Question: Delete a named layout from Database
« Reply #2 on: January 25, 2008, 10:29:08 AM »
You're probably erasing either the last active layout, or, even worse, modelspace...check those.
Me

It's Alive!

  • BricsCAD
  • Needs a day job
  • Posts: 6940
  • AKA Daniel
Re: Question: Delete a named layout from Database
« Reply #3 on: January 26, 2008, 02:47:10 PM »
Also you might try AcDbLayout::getBlockTableRecordId()
to get the id of the block table record that contains all the entities for the layout.

Dream.Fei

  • Guest
Re: Question: Delete a named layout from Database
« Reply #4 on: January 27, 2008, 06:49:27 AM »
Thanks all for reply.

This is a simply project to test .
Becasue I'm use it on my project don't need to select file. so, this project may not Perfect.

To Daniel:
  I have't try AcDbLayout::get..ID(). I'll do this after. thank you~

It's Alive!

  • BricsCAD
  • Needs a day job
  • Posts: 6940
  • AKA Daniel
Re: Question: Delete a named layout from Database
« Reply #5 on: January 27, 2008, 07:45:05 AM »
I wrote this before I saw your post, see if there is anything you can use  :-)

Code: [Select]
edit: flawed code removed
« Last Edit: January 27, 2008, 08:54:38 PM by Daniel »

It's Alive!

  • BricsCAD
  • Needs a day job
  • Posts: 6940
  • AKA Daniel
Re: Question: Delete a named layout from Database
« Reply #6 on: January 27, 2008, 09:00:49 AM »
Oops here is the solution  :-o

edit: flawed code removed
« Last Edit: January 27, 2008, 08:54:16 PM by Daniel »

Dream.Fei

  • Guest
Re: Question: Delete a named layout from Database
« Reply #7 on: January 27, 2008, 09:05:20 AM »
I haven't see function like this before you post. :oops:, it's support for Lisp?

after look you posted, I find some object didn't close before you's function return, and I didn't see where the layout delete form dicrtionary?

thanks~

It's Alive!

  • BricsCAD
  • Needs a day job
  • Posts: 6940
  • AKA Daniel
Re: Question: Delete a named layout from Database
« Reply #8 on: January 27, 2008, 09:16:16 AM »
I haven't see function like this before you post. :oops:, it's support for Lisp?

after look you posted, I find some object didn't close before you's function return, and I didn't see where the layout delete form dicrtionary?

thanks~

How about I comment the code and repost it

Dream.Fei

  • Guest
Re: Question: Delete a named layout from Database
« Reply #9 on: January 27, 2008, 09:20:22 AM »
soo cool~

your code work very very fine...........

It's Alive!

  • BricsCAD
  • Needs a day job
  • Posts: 6940
  • AKA Daniel
Re: Question: Delete a named layout from Database
« Reply #10 on: January 27, 2008, 11:46:20 AM »
See if this is any better for you, I am still new to ARX so I will let you add your error checking  :laugh:

Code: [Select]
  edit: flawed code removed

Kerry , Tony, Chuck, Glenn, … bail me out here  :-D
« Last Edit: January 27, 2008, 10:17:59 PM by Daniel »

It's Alive!

  • BricsCAD
  • Needs a day job
  • Posts: 6940
  • AKA Daniel
Re: Question: Delete a named layout from Database
« Reply #11 on: January 27, 2008, 10:20:04 PM »
Ok this one is better  :lol:

Code: [Select]
  static Acad::ErrorStatus deletelayout(const CString &FileName , const CString &LayoutName)
  {
    Acad::ErrorStatus es;

    AcDbEntity *pEnt;
    AcDbObjectId LayoutId;
    AcDbBlockTableRecordIterator *pBlkTblRcdItr;

    //a Smart Pointer to the database
    std::auto_ptr<AcDbDatabase> pDb(new AcDbDatabase(Adesk::kFalse));
    es = pDb->readDwgFile(FileName);
    if(es != Acad::eOk)
      return es;

    //Get the layout Dictionary from the Database
    AcDbDictionary *pDict = NULL;
    es = pDb->getLayoutDictionary(pDict, AcDb::kForRead);////////
    if(es != Acad::eOk)
      return es;

    //Get the Layout
    es = pDict->getAt(LayoutName, LayoutId);
    pDict->close();
    if(es != Acad::eOk)
      return es;

    AcDbObjectPointer<AcDbLayout> pLayout(LayoutId, AcDb::kForWrite);
    if(pLayout.openStatus() != Acad::eOk)
      return es;

    //Get the BlockTableRecord from the layout (smart pointer)
    AcDbBlockTableRecordPointer blockTableRecordPointer
      (pLayout->getBlockTableRecordId(),AcDb::kForWrite);
    if(blockTableRecordPointer.openStatus() != Acad::eOk)
      return es;

    //erase entities in the layout
    blockTableRecordPointer->newIterator(pBlkTblRcdItr);
    for (pBlkTblRcdItr->start(); !pBlkTblRcdItr->done(); pBlkTblRcdItr->step())
    {
      es = pBlkTblRcdItr->getEntity(pEnt, AcDb::kForRead);
      if (es == Acad::eOk)
      {
        pEnt->upgradeOpen();
        pEnt->erase();
        pEnt->close();
      }
    }

    //erase the layout
    es = pLayout->erase();

    //closeup
    es = pDb->closeInput(Adesk::kTrue);
    es = pDb->saveAs(FileName);
    return es;
  }

Glenn R

  • Water Moccasin
  • Posts: 1932
  • What idiot child of married cousins wrote this?!
Re: Question: Delete a named layout from Database
« Reply #12 on: January 28, 2008, 08:00:02 AM »
Dan,

Nice use of the std lib smart pointer for the dbase.

About the only thing I would have done differently relates to my use of smart pointers everywhere. So, for instance, I would declare, based on your example, a smart dictionary pointer thusly:

Code: [Select]
typedef AcDbObjectPointer<AcDbDictionary> AcDbDictionaryPointer;

(actually, this is already declared for you, along with AcDbEntityPointer, but gives my intent)

and wield it like so:
Code: [Select]
Acad::ErrorStatus es = Acad::eOk;
...
AcDbObjectId layoutDictId = AcDbObjectId::kNull;
layoutDictId = pDb->layoutDictionaryId();
if (layoutDictId == AcDbObjectId::kNull)
    return;

AcDbDictionaryPointer pLayourDict(layoutDictId, AcDb::kForRead);
es = pLayoutDict.openStatus();
if (es != Acad::eOk)
    return;
...

I wish I still did ARX...might get back into it, but C# seems to be taking all my time lately...sigh.

Cheers,
Glenn.
Me

Glenn R

  • Water Moccasin
  • Posts: 1932
  • What idiot child of married cousins wrote this?!
Re: Question: Delete a named layout from Database
« Reply #13 on: January 28, 2008, 08:02:38 AM »
Also, closeInput() is for internal use only according to the docs and can have undesirable results being called from an ARX app.

Cheers,
Glenn.
Me

It's Alive!

  • BricsCAD
  • Needs a day job
  • Posts: 6940
  • AKA Daniel
Re: Question: Delete a named layout from Database
« Reply #14 on: January 28, 2008, 09:07:54 AM »
YES!!  That’s exactly what I was looking for. Thank you, Glenn.
I definitely think that smart pointers are the way to go, especially trying to handle open/closed objects. 

Revised code  8-)

Code: [Select]
  static Acad::ErrorStatus deletelayout(const CString &FileName , const CString &LayoutName)
  {
    Acad::ErrorStatus es;

    AcDbEntity *pEnt = NULL;
    AcDbObjectId LayoutId = AcDbObjectId::kNull;
    AcDbBlockTableRecordIterator *pBlkTblRcdItr = NULL;

    //a Smart Pointer to the database
    std::auto_ptr<AcDbDatabase> pDb(new AcDbDatabase(Adesk::kFalse));
    es = pDb->readDwgFile(FileName);
    if(es != Acad::eOk)
      return es;

    //Get the layout Dictionary from the Database
    AcDbDictionaryPointer pDictionary(pDb->layoutDictionaryId(),AcDb::kForRead);
    if(pDictionary.openStatus() != Acad::eOk)
      return es;

    //Get the Layout
    es = pDictionary->getAt(LayoutName, LayoutId);
    if(es != Acad::eOk)
      return es;

    AcDbObjectPointer<AcDbLayout> pLayout(LayoutId, AcDb::kForWrite);
    if(pLayout.openStatus() != Acad::eOk)
      return es;

    //Get the BlockTableRecord from the layout (smart pointer)
    AcDbBlockTableRecordPointer blockTableRecordPointer
      (pLayout->getBlockTableRecordId(),AcDb::kForWrite);
    if(blockTableRecordPointer.openStatus() != Acad::eOk)
      return es;

    //erase entities in the layout
    blockTableRecordPointer->newIterator(pBlkTblRcdItr);
    for (pBlkTblRcdItr->start(); !pBlkTblRcdItr->done(); pBlkTblRcdItr->step())
    {
      es = pBlkTblRcdItr->getEntity(pEnt, AcDb::kForRead,Adesk::kFalse);
      if (es == Acad::eOk)
      {
        pEnt->upgradeOpen();
        pEnt->erase();
        pEnt->close();
      }
    }

    //erase the layout
    es = pLayout->erase();

    //closeup
    es = pDb->saveAs(FileName);
    return es;
  }
« Last Edit: January 28, 2008, 09:16:33 AM by Daniel »