Drawings SDK Developer Guide > Working with .dwg Files > Working with Databases > Working with Database Containers > Working with Dictionaries of Objects > Working with Specific Dictionaries > Layout Dictionary > Creating and Manipulating Layouts
Creating and Manipulating Layouts

A program can create a new layout, add it in the dictionary, rename any layout, get a layout of the dictionary, get the number of layouts that exist, check whether a layout exists in the dictionary, and remove and delete an existing layout.

Layouts can be created and manipulated using either the database object or dictionary object. The database object interface performs the required operations automatically, while the dictionary object, with its standard interface of a container, requires that some operations be performed by the program. For example, each layout has a tab order which the database interface sets automatically but the dictionary interface requires setting this order.

In the following examples, the pDb variable stores a pointer to the database object, the pLayout variable stores a pointer to the layout object, and the pLayouts variable stores a pointer to the layout dictionary which must be opened in write mode. For example:


OdDbDictionaryPtr pLayouts = pDb->getLayoutDictionaryId().safeOpenObject(OdDb::kForWrite);

Number of layouts

The default database contains one model layout and two paper layouts. The model layout is associated with model space. At least one paper layout is associated with paper space. The database must contain at least two layouts: a model layout and a paper layout, which cannot be deleted from the database.

To get the number of layouts in the database, use the countLayouts() method of the database object which returns it as an Integer value. For example:


odPrintConsoleString(L"\nDatabase contains %d layouts", pDb->countLayouts());

Naming a layout object

The layout object has two names:

  • External — The external layout name is the keyword (key) that identifies the layout object in the dictionary. It must be unique.
  • Internal — The internal layout name is the actual name stored inside the layout object.

Both names must coincide, otherwise errors occur. The layout name is an arbitrary nonempty string that can be up to 255 characters long and can contain letters, digits, blank spaces, underscores, and some special characters, but cannot contain inadmissible letters (see Naming objects). The layout name is an unique identifier of a layout in a drawing.

Note: "Model" is the reserved name used for the model layout, which is hard coded and associated with model space. This name cannot be changed or assigned to new layouts.

Creating and adding a layout object through the database interface

To create a new layout through the database, use the createLayout() method of the database object which requires the layout name as the first argument and the ID of an existing block record object with which the new layout must be associated as the second argument. The second argument is optional and if missing, this method automatically creates a new block record object in the block table object and associates it with new layout object.

This method automatically creates a new layout object, adds it in the layout dictionary, associates it with an object ID, associates it with a block record object, creates a new block record object if necessary, sets the tab order to the last layout number in the dictionary, and synchronizes internal and external names. The createLayout() method returns the object ID of the added layout. For example:


OdDbObjectId idLayout;
try {
  idLayout = pDb->createLayout(L"Layout_A");
}
catch(OdError& e) { odPrintConsoleString(L"\nError: %d - %s\n", e.code(), e.description().c_str()); }

Note: If the new name coincides with the name of an existing layout, the createLayout() method still creates a new layout object, assigns the name "Layout" with the next available number, and does not throw exceptions. The specified name must be correct.

Creating and adding a layout object through the dictionary interface

To create a new layout as an instance, declare a variable of the OdDbLayoutPtr type and use the pseudo-constructor of the layout object. This pseudo-constructor is the static method that creates a new dynamic instance of the layout object and returns the smart pointer to it. For example:


OdDbLayoutPtr pLayout = OdDbLayout::createObject();

The created instance exists independently from the database. Many properties of the layout object are invalid initially, and the layout instance does not get an ID until the layout is added to the database. The database cannot identify a layout object in its own container and cannot manipulate it without a key and ID. Therefore, after creation, the program must assign a unique name for the new layout object and add it to the layout dictionary of the database using the same name.

To add the layout object in the dictionary, use the setAt() method of the dictionary which requires the layout name as the first argument and a pointer to the layout instance as the second argument. The setAt() method does not set the tab order which must be set using the setTabOrder() method. Use the countLayouts() method to set its ordinal number correctly. The new layout object is not associated with a block record object, and its block property is set to OdDb::kNull. The setAt() method returns the object ID of the added layout. For example:


OdInt16 number;
OdString sName;
OdDbObjectId idSheet;

number = pDb->countLayouts();
sName.format(L"Sheet_%d", number);
pLayout = OdDbLayout::createObject();

try {
  pLayout->setLayoutName(sName);
  pLayout->setTabOrder(number);
  idSheet = pLayouts->setAt(sName, pLayout);
}
catch(OdError& e) { odPrintConsoleString(L"\nError: %d - %s\n", e.code(), e.description().c_str()); }

Note: Both names must coincide. Tab order must be equal to the first unused number.

Note: To associate the layout with a block, use the setBlockTableRecordId() method of the layout object which requires the ID of the block record object. However, this method creates a single-sided association from the layout to the block. To associate the block with a layout, use the setLayoutId() method of the block record object which requires the ID of the layout object. Continuing example:


OdDbBlockTableRecordPtr pBlock = OdDbBlockTableRecord::createObject();

sName.format(L"*Sheet_Block_%d", number);

try {
  pBlock->setName(sName);     
  pDb->getBlockTableId().safeOpenObject(OdDb::kForWrite)->add(pBlock);
  
  pLayout->setBlockTableRecordId( pBlock->objectId() );
  pBlock->setLayoutId( pLayout->objectId() );
}
catch(OdError& e) { odPrintConsoleString(L"\nError: %d - %s\n", e.code(), e.description().c_str()); }

You also must control the names of layouts and blocks for uniqueness and correctness. One block cannot be associated with multiple layouts. It is easier to use the createLayout() method of the database object.

Renaming a layout object through the database interface

To rename an existing layout object in the database, use the renameLayout() method of the database object which requires the old name as the first nonempty argument of the OdString type and the new name as the second nonempty argument of the OdString type. This method checks whether the layout name is found before it is changed. For example:


try {
  pDb->renameLayout(L"Layout_A", L"Sheet_1");
}
catch(OdError& e) { odPrintConsoleString(L"\nError: %d - %s\n", e.code(), e.description().c_str()); }

Note: If the old name is absent, the renameLayout() method throws the eNullObjectId exception, and errors occur. For example:


pDb->renameLayout(L"Noname", L"Layout_B");
// Error: 21 - Null object Id object: (0)

Note: If the new name already exists for another layout, the renameLayout() method throws the eRenameLayoutAlreadyExists exception, and errors occur. For example:


pDb->renameLayout(L"Noname", L"Layout_B");
// Error: 236 - Layout already exists

Note: If the new name is incorrect, the renameLayout() method throws the eRenameInvalidLayoutName exception, and errors occur. For example:


pDb->renameLayout(L"*?;", L":\/");
// Error: 237 - Invalid Layout name

Renaming a layout object itself

To get the name of the layout object, use its getLayoutName() method which returns the name as an OdString value. For example:


odPrintConsoleString(L"\nLayout name =\"%s\"", pLayout->getLayoutName().c_str());

To set the name of the layout object, use its setLayoutName() method which requires the new name as a nonempty OdString argument. For example:


try {
  pLayout->setLayoutName(L"Section A-A");
}
catch(OdError& e) { odPrintConsoleString(L"\nError: %d - %s\n", e.code(), e.description().c_str()); }

Note: If new name is incorrect, the setLayoutName() method throws the eRenameInvalidLayoutName exception, and errors occur.

Note: When internal and external names are different, the setLayoutName() method throws the eRenameLayoutAlreadyExists exception, and errors occur. For example:


// Names are different
pLayout = OdDbLayout::createObject();
pLayouts->setAt(L"AAA", pLayout);
pLayout->setLayoutName(L"BBB");
// Try renaming
pLayout->setLayoutName(L"CCC");
// Error: 236 - Layout already exists

In this case, synchronize both names using the setName() method of the dictionary object and then perform the renaming. For example:


pLayouts->setName( pLayouts->nameAt(pLayout->objectId()), pLayout->getLayoutName() );
pLayout->setLayoutName(L"CCC");

Finding a layout object through the database interface

To find the layout and get it in the database, use the findLayoutNamed() method of the database object which requires the layout name as a non-empty string argument and returns an ID of the layout object if it exists, or returns OdDb::kNull if the layout name is not found. For example:


OdString sName = L"Section A-A";
OdDbObjectId idLayout = pDb->findLayoutNamed(sName)

if(idLayout.isNull())
  odPrintConsoleString(L"\nLayout is not found");
else
  odPrintConsoleString(L"\nLayout \"%s\" has ID=%s", sName, idLayout.getHandle().ascii().c_str());

Getting a layout object through the dictionary interface

To get an existing layout object, use the getAt() method of the dictionary object in the first implementation; it requires two arguments — the layout name as a non-empty string and open mode as an integer value (kForRead, kForWrite, or kForNotify) — and returns the smart pointer to the layout object if it exists in the dictionary, or returns NULL if the layout name is not found or the layout object cannot be opened in the specified mode. For example:


OdString sName = L"Section A-A";
OdDbLayoutPtr pLayout = pLayouts->getAt(sName, OdDb::kForRead);

if(pLayout.isNull())
  odPrintConsoleString(L"\nLayout is not found");
else
  odPrintConsoleString(L"\nLayout \"%s\" has ID=%s", sName, pLayout->handle().ascii().c_str());

To get the layout ID, use the getAt() method in the second implementation; it requires the layout name as a non-empty string argument and returns an object ID of the layout object if it exists, or returns OdDb::kNull if the name is not found. For example:


OdDbObjectId idLayout = pLayouts->getAt(L"Section A-A");

Checking whether a layout object exists in the dictionary

To check whether a layout exists in the dictionary, use the has() method of the dictionary object in the first implementation; it requires the layout name as a non-empty string argument and returns True if the layout object exists in the dictionary or False if the layout name is not found. For example:


odPrintConsoleString(L"\nSection A-A %s", ((pLayouts->has(L"Section A-A")) ? "exists" : "is absent"));

To check whether a layout ID exists in the dictionary, use the has() method in the second implementation; it requires an existing ID as an argument and returns True if the layout object exists in the dictionary or False if the ID is absent. For example:


odPrintConsoleString(L"\nLayout ID %s", ((pLayouts->has(idLayout)) ? "exists" : "is absent"));

Listing all layout objects

To display a list of all layouts, use an iterator for traversing through layout objects. To create the iterator, use the newIterator() method of the dictionary object. To move the iterator, use the next() method of the iterator. To check whether the traverse is completed, use the done() method of the iterator. For example:


OdDbDictionaryIteratorPtr itLayout = pLayouts->newIterator();

odPrintConsoleString(L"\nList of layouts");
while(!itLayout->done())
{
  odPrintConsoleString(L"\n%s-\"%s\"", itLayout->objectId().getHandle().ascii(), itLayout->name().c_str());
  itLayout->next();
}

Deleting a layout object through the database interface

To delete a layout object, use the deleteLayout() method of the database object which requires the layout name as an argument of the OdString type. This method finds the layout using specified name, checks whether the layout is not predefined, removes the layout object from the dictionary, deletes it, and changes the tab order of other layouts. If the name is not found or the layout object is predefined, this method cannot delete it and errors occur. For example, correct deleting:


OdString sName = L"Section A-A";

try {
  pDb->deleteLayout(sName);
}
catch(OdError& e) { odPrintConsoleString(L"\nError: %d - %s\n", e.code(), e.description().c_str()); }

Note: If the name is absent, the renameLayout() method throws the eInvalidKey exception, and errors occur. For example:


pDb->deleteLayout(L"SheetX");
// Error: 38 - Invalid key

Note: If the name is "Model", the renameLayout() method throws the eDelIsModelSpace exception, and errors occur. For example:


pDb->deleteLayout(L"Model");
// Error: 229 - Model Space Layout can't be deleted

Note: If deleting the last paper layout, the renameLayout() method throws the eDelLastLayout exception, and errors occur. For example:


OdDbDictionaryIteratorPtr itLayout = pLayouts->newIterator();
OdString sName;

while(!itLayout->done())
{
  if(((OdDbLayout*)(itLayout->getObject().get()))->modelType()) itLayout->next();
  else
  {   sName = itLayout->name();
      itLayout->next();
      pDb->deleteLayout(sName);
  }
}
// Error: 230 - Last Paper Space Layout can't be deleted

Note: If the paper layout is set as paper space and it is deleted, the renameLayout() method deletes it and makes other paper layout as paper space. Errors occur only if it is the last paper layout. The database must contain the model layout and at least one paper layout.

Removing and deleting a layout object through the dictionary interface

To remove a layout object from the dictionary, use the remove() method of the dictionary object which checks whether the layout object exists in the dictionary, removes the layout object from the dictionary, erases the external layout name, but does not delete the removed layout object. The removed layout object continues existing outside of the layout dictionary and stores the internal layout name. In the first implementation, this method requires the external layout name as an argument and returns the ID associated with the removed layout object or OdDb::kNull if the name is not found. In the second implementation, this method requires the object ID as an argument and does not return a value. For example:


// Remove the layout object using the layout name
OdDbObjectId idLayout = pLayouts->remove(L"Section A-A");

// Remove the layout object using the ID
pLayouts->remove(idSheet);

To delete the removed layout object, open it in write mode, call its erase() method, and pass it a True value as an argument. The erase() method returns an error code if the layout object is not deleted or returns zero if the layout object is deleted successfully. For example:


OdDbObjectPtr pLayout = idLayout.safeOpenObject(OdDb::kForWrite);

OdResult result = pLayout->erase(true);

if(result != eOk) 
  odPrintConsoleString(L"\nError: %d - %s", result, pLayout->database()->appServices()->getErrorDescription(result));

Note: The remove() and erase() methods don't check whether the layout is predefined and don't throw exceptions. These methods also don't change the tab order of remaining layouts. As a result, it may delete the model layout and the last paper layout, which makes the database invalid and errors occur.

See Also

Working with Layouts

Other Properties of the Layout Object

Making a Layout Active

Example of Working with the Layout Dictionary

Copyright © 2002 – 2020. Open Design Alliance. All rights reserved.