TheSwamp

Code Red => ARX Programming => Topic started by: Dream.Fei on January 25, 2008, 04:26:39 AM

Title: Question: Delete a named layout from Database
Post by: Dream.Fei 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?
Title: Re: Question: Delete a named layout from Database
Post by: It's Alive! 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  :-)
Title: Re: Question: Delete a named layout from Database
Post by: Glenn R on January 25, 2008, 10:29:08 AM
You're probably erasing either the last active layout, or, even worse, modelspace...check those.
Title: Re: Question: Delete a named layout from Database
Post by: It's Alive! 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.
Title: Re: Question: Delete a named layout from Database
Post by: Dream.Fei 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~
Title: Re: Question: Delete a named layout from Database
Post by: It's Alive! 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
Title: Re: Question: Delete a named layout from Database
Post by: It's Alive! on January 27, 2008, 09:00:49 AM
Oops here is the solution  :-o

edit: flawed code removed
Title: Re: Question: Delete a named layout from Database
Post by: Dream.Fei 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~
Title: Re: Question: Delete a named layout from Database
Post by: It's Alive! 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
Title: Re: Question: Delete a named layout from Database
Post by: Dream.Fei on January 27, 2008, 09:20:22 AM
soo cool~

your code work very very fine...........
Title: Re: Question: Delete a named layout from Database
Post by: It's Alive! 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
Title: Re: Question: Delete a named layout from Database
Post by: It's Alive! 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;
  }
Title: Re: Question: Delete a named layout from Database
Post by: Glenn R 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.
Title: Re: Question: Delete a named layout from Database
Post by: Glenn R 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.
Title: Re: Question: Delete a named layout from Database
Post by: It's Alive! 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;
  }
Title: Re: Question: Delete a named layout from Database
Post by: Glenn R on January 28, 2008, 09:14:36 AM
You're welcome Dan.

You could still change this:

Code: [Select]
AcDbObjectPointer<AcDbDictionary> pDictionary(pDb->layoutDictionaryId(),AcDb::kForRead);

to this though:

Code: [Select]
AcDbDictionaryPointer pDictionary(pDb->layoutDictionaryId(),AcDb::kForRead);

As I mentioned previously, AcDbDictionaryPointer is already defined for you...just a thought.

Cheers,
Glenn.
Title: Re: Question: Delete a named layout from Database
Post by: It's Alive! on January 28, 2008, 09:18:46 AM
Doh! You did say it was already defined
Edited code.

Thanks Again
Title: Re: Question: Delete a named layout from Database
Post by: Glenn R on January 28, 2008, 09:25:50 AM
Dan,

Have you tried just deleting the layout itself and NOT iterating over all the entities in the layout and erasing each one first? I would have thought just deleting the layout would do the trick...

Cheers,
Glenn.
Title: Re: Question: Delete a named layout from Database
Post by: It's Alive! on January 28, 2008, 09:37:48 AM
Well…Um…No…I just assumed…I’ll need to test this  :-o
Title: Re: Question: Delete a named layout from Database
Post by: It's Alive! on January 28, 2008, 09:56:07 AM
new code  :lol:

Code: [Select]
  static Acad::ErrorStatus deletelayout(const CString &FileName , const CString &LayoutName)
  {
    Acad::ErrorStatus es;
    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;

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

    //closeup
    es = pDb->saveAs(FileName);
    return es;
  }
Title: Re: Question: Delete a named layout from Database
Post by: It's Alive! on January 28, 2008, 09:59:31 AM
Dan,

Have you tried just deleting the layout itself and NOT iterating over all the entities in the layout and erasing each one first? I would have thought just deleting the layout would do the trick...

Cheers,
Glenn.

You were right, just delete the layout.  :kewl:
Title: Re: Question: Delete a named layout from Database
Post by: Glenn R on January 28, 2008, 10:05:57 AM
Try this:

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

    //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;

    es = pDictionary->remove(LayoutId); // remove the layout
    if (es != Acad::eOk)
       acutPrintf("\nError: Unable to remove layout. Error code = %s.", acadErrorStatusText(es));

    //closeup
    es = pDb->saveAs(FileName);
    return es;
  }

Cheers,
Glenn.
Title: Re: Question: Delete a named layout from Database
Post by: It's Alive! on January 28, 2008, 10:20:54 AM
Hmm got this
Title: Re: Question: Delete a named layout from Database
Post by: Glenn R on January 28, 2008, 10:27:36 AM
That code I posted was done in Visual Notepad (read not tested at all), so maybe you do need to cast the dictionary entry to a layout and erase it, rather than just blatantly deleting the dictionary entry...was just a thought.

My suggestion/modification was taken directly from some old code of mine which was just blatantly removing the layout from the dictionary as I showed...

Cheers,
Glenn.
Title: Re: Question: Delete a named layout from Database
Post by: Glenn R on January 28, 2008, 10:32:05 AM
Dan,

Try changing the open status of pDictionary from AcDb::kForRead to AcDb::kForWrite or just before the 'remove', use upgradeOpen adn see how that goes...

Cheers,
Glenn.
Title: Re: Question: Delete a named layout from Database
Post by: It's Alive! on January 28, 2008, 10:51:20 AM
Dan,

Try changing the open status of pDictionary from AcDb::kForRead to AcDb::kForWrite or just before the 'remove', use upgradeOpen adn see how that goes...

Cheers,
Glenn.

I had changed that., I wonder if Erase() does some internal mojo such as removing all the objects that the layout owns..
Title: Re: Question: Delete a named layout from Database
Post by: It's Alive! on January 28, 2008, 10:51:57 AM
...Visual Notepad...
 
:lmao:
Title: Re: Question: Delete a named layout from Database
Post by: Glenn R on January 28, 2008, 10:55:23 AM
Dan,

Try changing the open status of pDictionary from AcDb::kForRead to AcDb::kForWrite or just before the 'remove', use upgradeOpen adn see how that goes...

Cheers,
Glenn.

I had changed that., I wonder if Erase() does some internal mojo such as removing all the objects that the layout owns..

Possibly...The old fucntion I mentioned was working on a drawing that was currently loaded into the editor, so maybe opening the dbase in memory as you've done makes a difference...small difference at the end of the day the extra cast to a layout and open...

Cheers,
Glenn.
Title: Re: Question: Delete a named layout from Database
Post by: Dream.Fei on January 29, 2008, 01:11:32 AM
Try this:

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

Cheers,
Glenn.
If didn't erase all entity on this layout?
Title: Re: Question: Delete a named layout from Database
Post by: MickD on January 29, 2008, 06:14:30 AM
... I wonder if Erase() does some internal mojo such as removing all the objects that the layout owns..

As the layout is an AcDbObject and it's another block table record (i.e. it holds all of its own ent data), 'erase' should do the trick. IOW, is you set the erase flag to true, all the ent's int the layout btr should also be flagged and therefore deleted (not saved) on the save.
I don't know if it would make much difference but setting modelspace as current after the erase may make a difference (I'd like to think that if the layout didn't exist it would open in mspace or one that existed at least anyway).
Title: Re: Question: Delete a named layout from Database
Post by: MickD on January 29, 2008, 06:22:26 AM
whoops, I see Dan already tried erase, I was looking at Glenn's visual notepad code (which looks like it could work) :D

Was that successful?
Title: Re: Question: Delete a named layout from Database
Post by: Glenn R on January 29, 2008, 06:25:16 AM
whoops, I see Dan already tried erase, I was looking at Glenn's visual notepad code (which looks like it could work) :D

Was that successful?

As stated by Dan, no - it didn't seem to work...but as I said earlier, I thought it would.
Title: Re: Question: Delete a named layout from Database
Post by: MickD on January 29, 2008, 06:30:39 AM
Sorry Glenn, I didn't mean your VN code, I was refering to  Dan's 'erase' code (the smiley was in the wrong place :) )
Title: Re: Question: Delete a named layout from Database
Post by: Glenn R on January 29, 2008, 06:34:24 AM
Oh yes, the erase certainly worked - I was just trying to shorten it by removing from the dictionary directly, rather than open the dict entry and cast to a layout.

Cheers,
Glenn.
Title: Re: Question: Delete a named layout from Database
Post by: MickD on January 29, 2008, 06:41:13 AM
Oh yes, the erase certainly worked - I was just trying to shorten it by removing from the dictionary directly, rather than open the dict entry and cast to a layout.

Cheers,
Glenn.

Yep, I see that and I agree, why have a remove method otherwise??

You're right about getting back into arx though, some (even most) things are a lot easier/more direct to do than in the COM or .net languages even if there's twice as many lines of code.