How to add element(s) to an existing group without ungrouping

Explorer ,
Aug 19, 2019 Aug 19, 2019

Copy link to clipboard

Copied

I would like to add, one or more elements, to an existing group by script, without ungrouping the group, as this is possible to do by hand, dragging new elements into a group in Layers Panel – see the srceens. The initial group has label, name and appliedObjectStyle, wich wanish during ungrouping ...

I'll be very pleased for any sugestion or solution ... I have no idea where to start ... 🙂

Many thanks

a.

  1.PNG

2.PNG

TOPICS
Scripting

Views

3.0K

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 1 Correct Answer

Adobe Community Professional , Aug 20, 2019 Aug 20, 2019
Hi platm72 ,to add an element to an existing group by dragging it inside the group using the Layers panel is the one method.The other method is:1. Convert the selected group to a new Multistate Object (MSO).2. Select the MSO and the item you want to add to a group. Add it to the active state of the MSO.3. Use Shift + Esc to select the active state instead the whole MSO.4. Hold the alt key and drag the selected group part of the active state as duplicate out of the MSO.Sounds complicated, but one...

Likes

Translate

Translate
Adobe Community Professional ,
Aug 20, 2019 Aug 20, 2019

Copy link to clipboard

Copied

I don't think you can add a page item to a group without ungrouping the group. But you can store the group's applied object style, label, and name, and its page items, ungroup, add the item to be added to the stored page-item array, re-group, and re-apply the properties you stored. Isn't that a possibility?

P.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Community Professional ,
Aug 20, 2019 Aug 20, 2019

Copy link to clipboard

Copied

Hi platm72 ,

to add an element to an existing group by dragging it inside the group using the Layers panel is the one method.

The other method is:

1. Convert the selected group to a new Multistate Object (MSO).

2. Select the MSO and the item you want to add to a group. Add it to the active state of the MSO.

3. Use Shift + Esc to select the active state instead the whole MSO.

4. Hold the alt key and drag the selected group part of the active state as duplicate out of the MSO.

Sounds complicated, but one could do it in seconds.

And it can be scripted without using any menu actions. 🙂

By help of a temporary MSO.

Example:

Here we have two objects on the first spread of an InDesign document. The only items in the document:

A group of yellow rectangles where an effect is applied to the group, a drop shadow.

The second object is a magenta circle without any effect applied.

AddCircleToGroup-WITHOUT-ungrouping-1.PNG

Then run this script snippet below that will add the magenta circle to the group WITHOUT ungrouping the group:

/**

* @@@BUILDINFO@@@ AddItemToGroup-WITHOUT-Ungrouping-USING-TEMP-MSO.jsx !Version! Tue Aug 20 2019 13:34:21 GMT+0200

*/

/*

    Script snippet by Uwe Laubender

    Posted at Adobe InDesign Scripting in thread:

   

    How to add element(s) to an existing group without ungrouping

    platm72 Aug 19, 2019

    https://forums.adobe.com/message/11219570#11219570

   

   

    Just an example to add one item to an existing group without ungrouping the group.

    The trick is to use a temp multistate object for this.

   

    All properties of the initial group are maintained.

   

    Two exceptions:

   

    1. name

    That could be restored easily.

   

    2. id

    Why? In the end we will work with a duplicate of the group.

   

*/

// Active document:

var doc = app.documents[0];

// First group on the first spread of the active document:

var myGroup = doc.spreads[0].groups[0];

// Name of that group ( OPTIONAL 😞

var myGroupName = myGroup.name;

// First oval, the circle is an oval, on the first spread of the active document:

var myItemToAdd = doc.spreads[0].ovals[0];

// Add a new MSO on the same spread where the group and the circle is.

// Make its dimensions the same as the group's ( OPTIONAL ).

var myTempMSO = doc.spreads[0].multiStateObjects.add

(

    { geometricBounds : myGroup.geometricBounds }

);

// Add the group as new state to the MSO:

// It will add this as the third state in the MSO because the added MSO has to have two states if not specified otherwise:

myTempMSO.addItemsAsState( [myGroup] );

// Add the circle to the third state of the added MSO:

// FWIW: a state consists of two top items in scripting, a state object and a group object:

myTempMSO.states[2].addItemsToState( [myItemToAdd] );

// Duplicate the first group of the third state of the MSO that now contains the circle as well:

var newGroup = myTempMSO.states[2].groups[0].duplicate();

// Rename that new group ( OPTIONAL ) to the name of the inital group:

newGroup.name = myGroupName;

// Finally remove the MSO. It is not needed anymore:

myTempMSO.remove();

The result is this: The circle is added to the group. Hm. In fact it is a duplicate of the group. The effect on the group, the drop shadow, is applied as well:

AddCircleToGroup-WITHOUT-ungrouping-2.PNG

Regards,
Uwe

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Aug 20, 2019 Aug 20, 2019

Copy link to clipboard

Copied

Hi Uwe,

Awesome!!! This finally solves an antediluvian problem.

Now—based on the same idea—what don't we just use the method State.releaseAsObject() to simply restore the original group? This seems to work:

function addItemsToGroup(/*Group*/grp,/*PageItem|PageItem[]*/items,  gName,mso,sta)

// --------------------------------

// Based on Uwe Laubender's brilliant trick at forums.adobe.com/message/11220608#11220608

// Use `State.releaseAsObject()` instead, which preserves the original Group specifier.

// => grp

{

    // Backup the name of the Group.

    // ---

    gName = grp.name;

    // Create a MSO and convert `grp` into a new state.

    // ---

    mso = grp.parent.multiStateObjects.add();

    mso.addItemsAsState( [grp] );

    sta = mso.states.lastItem();

    // Inject `item` in the state.

    // ---

    sta.addItemsToState( items instanceof Array ? items : [items] );

   

    // Release the state "as object".

    // -> The `grp` specifier is fully restored (except for the name.)

    // ---

    sta.releaseAsObject();

    mso.remove();

    grp.name = gName;

}

// Test

// =======

var grp = app.selection[0];        // Group (first selected obj.)

var item = app.selection.slice(1); // Next object(s)

alert( "Group size before: " + grp.pageItems.length );

addItemsToGroup(grp,item);

alert( "Group size after: "  + grp.pageItems.length );

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Community Professional ,
Aug 20, 2019 Aug 20, 2019

Copy link to clipboard

Copied

Hi Marc,

State.releaseAsObject()

would have been the next thing I had no time for testing.

Thanks for the function and the tests!

The best thing: This trick can be used with InDesign CS5 and above.

Hi David,

why is this working?

Because a state of an MSO as seen in the Layers panel of the UI is indeed always a combination of two objects in scripting:

The State object and a Group object ( that contains all other objects of the state ) as first and only main object of that State object.

Regards,
Uwe

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Feb 04, 2021 Feb 04, 2021

Copy link to clipboard

Copied

Just brilliant, Uwe, Marc! My two cents, states may be named too, so I'd suggest after adding  a group to the MSO, name the last state:

//...
sta = mso.states.lastItem();
sta.name = gName
//...

The name of the state remains after the release the state "as object", so the last line "grp.name = gName;" is no more necessary. This helps me in debugging and feels more like manual converting the group into the MSO state object through the Object States panel > New State.

Cheers!

Anton

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Feb 27, 2021 Feb 27, 2021

Copy link to clipboard

Copied

Anton hi! I tried implementing this but I am useless (designer, not scripter), can you help?

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Feb 27, 2021 Feb 27, 2021

Copy link to clipboard

Copied

Sure, how can I help? What is your goal?

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Feb 27, 2021 Feb 27, 2021

Copy link to clipboard

Copied

I would love if you could share the full script with your addition? I can’t
figure out what lines to replace! Thanks in advance --


[image: Carla Lomónaco]
[image: carlalomonaco.com]
[image: hello@carlalomonaco.com] [image: 07473414213]
<+447473414213> <+447473414213>

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Feb 27, 2021 Feb 27, 2021

Copy link to clipboard

Copied

Hi, Carla,

The commented method is just a fragment targeting a specific task: How to insert object O into group G without ungrouping the G by script. The aim is to keep the group G id, name, label etc. properties, which will be lost if you ungroup first, select elements again (including O) and regroup again.

According to your goal, this fragment may be used as is in the full script. So again, what is your goal?

It would be much easier if you explain your case in detail.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Feb 27, 2021 Feb 27, 2021

Copy link to clipboard

Copied

LATEST

@carlalomonaco If you mean the whole function code, scroll to the bottom of the page, but it is still a fragment. You need to pass some arguments like group and item(s) references to the main function addItemsToGroup and there are some other helper functions to keep the original z-index order.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Aug 20, 2019 Aug 20, 2019

Copy link to clipboard

Copied

I love Uwe's crazy but effective approach to this. Amazing use of MSO.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Aug 20, 2019 Aug 20, 2019

Copy link to clipboard

Copied

Hi platm72,

Uwe Laubender should have been credited for the correct answer, as my snippet is nothing but a slight enhancement of his original post. That's a bit unfair to him! The key idea (and the great discovery!) was to use a MSO state as a temporary container for addressing the historical problem of augmenting a group.

Marc

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guru ,
Aug 20, 2019 Aug 20, 2019

Copy link to clipboard

Copied

I don't think Uwe really need the points for keeping his ACP but I marked his question as correct because as you wrote he certainly deserves credit.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Aug 20, 2019 Aug 20, 2019

Copy link to clipboard

Copied

Hi Trevor,

I don't think Uwe really need the points for keeping his ACP but I marked his question as correct because as you wrote he certainly deserves credit.

Indeed. [Moreover, increasing my points would be futile since our informed Adobe commissioners only reward valuable participants, experts and true professionals. If I had such a profile, they would have already realized it in 10 years.]

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Community Professional ,
Aug 20, 2019 Aug 20, 2019

Copy link to clipboard

Copied

document.groups.add([o, g]);

where o is any PageItem (text frame, etc.) and g is an existing Group.

This seems to add PageItem o to Group g.

Ariel

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Aug 20, 2019 Aug 20, 2019

Copy link to clipboard

Copied

Hi Ariel,

The effect of doc.groups.add([o, g]) is to create a new group G formed of o and g, that is, G = { o ; g }. But the original group g is not extended. The challenge is to insert o into g itself, without destroying g in terms of specifier and id. The MSO trick solves that specific question in almost any context, including when g is already part of a supergroup. (The trick doesn't work if g is an anchored object though.)

Marc

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Community Professional ,
Aug 21, 2019 Aug 21, 2019

Copy link to clipboard

Copied

Hi Marc,

Good point. So it's not identical to the UI drag-in-layer panel move.

Still, it does delete the old group, and seems to preserve the formatting of individual items in the old group, as well as their original IDs.

It also returns the new group, so it should be easy in most scripts to update the variable that is keeping track of the specifier or ID of the old group.

Although I haven't tested it, performance-wise it's almost certainly going to be considerably quicker.

So, although it has none of the brilliance of Uwe's brainwave, I think for mundane uses it's probably a better approach to adding an item to an existing group.

Ariel

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Community Professional ,
Aug 21, 2019 Aug 21, 2019

Copy link to clipboard

Copied

Hi Ariel,

if you add an item to an existing group the main point is:

All properties applied to the group, effects like drop shadows etc.pp., will be also added to the new member.

If that's the main intention of the operation, grouping a group with a different item is no option.

That's why I tested my group with an effect applied.

Regards,
Uwe

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Aug 21, 2019 Aug 21, 2019

Copy link to clipboard

Copied

Hi Ariel,

I understand your reasoning, as there are probably many cases where creating a new group over existing objects is not an issue. However, there are also scripts where preserving the document structure is clearly a requirement. Hence the original question: How to add element(s) to an existing group? Until now we had no practical solution for that specific goal, and existing workarounds were both complex and unsafe—see e.g. my 2012 post “How to Augment and Process Nested Groups” (and the associated comments.) Many of us have faced for years this limitation of the API: not being able to move an existing object into an existing group without destroying temporarily this very container. I could give dozens of examples where this problem occured, including for “mundane uses.” The present thread provides a valuable, unprecedented answer to that question.

Best,

Marc

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Community Professional ,
Feb 04, 2021 Feb 04, 2021

Copy link to clipboard

Copied

Hi Anton,

and there is another use case with this method:

You could also move any object inside a graphic frame as well.

Without the need of a duplicate of the original object that has a new ID number automatically assigned to it.

 

Here comes yet another trick how to move an object out of a group or a graphic frame:

Simply add a new text frame to the spread, anchor the object inside the group or a graphic frame to an insertion point of that helper text frame, release the anchored object and remove the helper text frame. This method will also retain the ID number of the object.

 

Regards,
Uwe Laubender

( ACP )

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Feb 06, 2021 Feb 06, 2021

Copy link to clipboard

Copied

Two more cents about z-index. As a side effect of using this method, the augmented group keeps the original ID, but is put as a first child of it's parent. Moreover, the newly added item becomes top item in the group (on top of others in z-index). Lets assume following structure in the Layers:

extGroup

|-- subgroup2

|-- subgroup1

|-- subgroup0

|--|-- item1

|--|-- item0

itemToAdd

 

After addItemsToGroup(subgroup0, itemToAdd), the stack becomes:

 

extGroup

|-- subgroup0

|--|-- itemToAdd

|--|-- item1

|--|-- item0

|-- subgroup2

|-- subgroup1

 

I'd like 

extGroup

|-- subgroup2

|-- subgroup1

|-- subgroup0

|--|-- item1

|--|-- item0

|--|-- itemToAdd

 

Added some code for maintaining the z-orders. Not sure in case of adding 3 or more items in a one step, some above and others bellow the group in their relative z-indices, but should keep correct item-to-group and group-to-parent relations. Credits to commenters in the thread https://community.adobe.com/t5/indesign/stacking-order-of-pageitems-in-cs5/td-p/3845337 about stacking order.

 

function addItemsToGroup(/*Group*/grp,/*PageItem|PageItem[]*/items,  gName,mso,sta) {

  // --------------------------------
  // Based on Uwe Laubender's brilliant trick at forums.adobe.com/message/11220608#11220608
  // Use `State.releaseAsObject()` instead, which preserves the original Group specifier.
  // => grp

  // Backup the name of the Group.

  gName = grp.name;
  
  // Get initial siblings z-order
  var prevZOrderObj = getPrevZOrderObj(grp)

  // Create a MSO and convert `grp` into a new state.
  mso = grp.parent.multiStateObjects.add();
  mso.addItemsAsState( [grp] );
  sta = mso.states.lastItem();
  
  // States empty string name not allowed, so keep the default state name
  sta.name = gName || sta.name

  // In case of a single item
  items = items instanceof Array ? items : [items]
  // Inject `item` in the state.
  sta.addItemsToState( items );
  
  // Maintain item-in-group z-index
  // If an item has initially bigger z-index than the group, sandToBack in the sta
  for (var i=0; i<items.length; i++ ){
    if (prevZOrderObj[items[i].id] > prevZOrderObj[grp.id]){
      items[i].sendToBack()
    }
  }

  // Release the state "as object".
  // -> The `grp` specifier is fully restored (except for the name.)
  sta.releaseAsObject();
  mso.remove();
  // If grp name not specified, revert the default "State 3" name to empty string
  !gName && grp.name = gName;
  
  restoreZOrder(grp, prevZOrderObj)
  
}

// Return:  {id:zIndex} object
function getPrevZOrderObj(item){
  var result = {}
  var parent = item.parent
  var piIds = parent.pageItems.everyItem().id
  var piIndices = parent.pageItems.everyItem().index
  for (var i = 0; i < parent.pageItems.length; i++){
    result[piIds[i]] = piIndices[i]
  }
  return result
}

function restoreZOrder(item, prevZOrderObj){
  var newZOrderArr = getZOrderArr(item)

  for (var i = 0; i<newZOrderArr.length; i++){
    if (newZOrderArr[i].id == item.id) {continue}
    if (prevZOrderObj[newZOrderArr[i].id] < prevZOrderObj[item.id]){
      item.sendBackward()
    } else return
  }
}

// Return: Array of {id:number, zIndex: number} objects
function getZOrderArr(item){
  var result = []
  var parent = item.parent
  var piIds = parent.pageItems.everyItem().id
  var piIndices = parent.pageItems.everyItem().index
  for (var i = 0; i < parent.pageItems.length; i++){
    result.push({id:piIds[i], zIndex:piIndices[i]})
  }
  result.sort(sortByZOrder)
  return result
}

function sortByZOrder(a,b){
  return a.zIndex - b.zIndex
}

 

 

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines