Selection System Overview

From The Foundry MODO SDK wiki
Jump to: navigation, search

The selection system in nexus keeps track of the components, items, elements -- in short, the objects -- that the user has selected. Each selection type corresponds to an object type, and there can be multiple selected objects of each type. Selections of one type can also have a sub-type. The selection order matters, and the system maintains a history for finding recently deselected objects.


  • Select (lx-select.hpp)
    • CLxUser_SelectionService
    • CLxImpl_SelectionListener
  • Seltypes (lx-seltypes.hpp)
    • CLxUser_CenterPacketTranslation
    • CLxUser_ChannelPacketTranslation
    • CLxUser_EdgePacketTranslation
    • CLxUser_ItemPacketTranslation
    • CLxUser_LinkPacketTranslation
    • CLxUser_PivotPacketTranslation
    • CLxUser_PolygonPacketTranslation
    • CLxUser_ScenePacketTranslation
    • CLxUser_VertexPacketTranslation
    • CLxUser_VMapPacketTranslation

Basic Concepts


Each selection type has a name and numeric code, both of which are unique. The selection service allows you to find the code from the name and vice versa. The numeric code is used in the packet methods for performance reasons.

        CLxUser_SelectionService      svcSel;
        const char                   *name;
        LXtID4                        type;

        type = svcSel.LookupType (LXsSELTYP_ITEM);     // get the code for item selection
        name = svcSel.LookupName (type);               // get the name back from the type

Packets & Translators

Every selection is stored as a packet. This is a small chunk of memory allocated for recording a single selected element, and are referenced as void pointers. Selection types define the content of the packets and provide a translator interface. The methods in the selection service all act on and return packets, and the packets have to be encoded and decoded using the translator.

The various PacketTranslation user classes all have an autoInit() method that initializes the user wrapper with its object. Technically this comes from querying the translation interface from the SelectionType Object, but you never need to worry about that.

Service Methods

Most methods operate on packets, which are defined as void pointers. Since packet pointers can never be null, a null return value indicates an invalid or empty query.

        CLxUser_SelectionService	 svcSel;
        void				*pkt;

Current Selection

The most recent selection of a given type is the last one the user clicked on to select it.

        pkt = svcSel.Recent (type_code);

The first selection of a type was the first one the user clicked on after dropping all others. This can be read as index zero in the selection order.

        pkt = svcSel.ByIndex (type_code, 0);

Walking the List

Packets can be accessed by index.

        n = svcSel.Count (type_code);
        for (i = 0; i < n; i++) {
                pkt = svcSel.ByIndex (type_code, i);

They can also be iterated.

        LXtScanInfoID           scan;

        scan = 0;
        while (scan = svcSel.ScanLoop (scan, type_code, &pkt)) {

Testing Packets

In order to test selection you have to create a temporary packet with the translator. You can then test if the element is selected.

        if (svcSel.Test (type_code, pkt) == LXe_TRUE) {
                // packet selected

You can also perform more complex (but more expensive) tests about the full status of the element.

        stateFlags = svcSel.State (type_code, pkt);
        if (stateFlags & LXf_SELECTION_PRIMARY) {
                // first selected


The current time is also part of the selection system state. This code advances time by 1 second.

        double                     now;

        now = svcSel.GetTime ();
        svcSel.GetTime (now + 1.0);

Using Translators


For dealing with item selections we'll need the item type code and a packet translator. We can initialize these at some point during startup.

        CLxUser_Item			 item;
        CLxUser_ItemPacketTranslation	 itemPkt;
        LXtID4				 type_item;

        type_item = svcSel.LookupType (LXsSELTYP_ITEM);
        itemPkt.autoInit ();

To test an item for selection, we get a temporary packet initialized to the item.

        pkt = itemPkt.Packet (item);
        if (svcSel.Test (type_item, pkt) == LXe_TRUE) {

This same temporary packet can be used for selection. The selection system will copy the packet into the selection state.

        pkt = itemPkt.Packet (item);
        svcSel.Select (type_item, pkt);

To read an item selection we have to decode the packet we get from the selection query.

        pkt = svcSel.Recent (type_item);
        if (pkt == NULL) {
                // no selected item

        } else {
          if (!itemPkt.GetItem (pkt, item)) {
                // invalid packet (shouldn't happen)

          } else {
                // do something with the item


Channel selections use basically the same principle, except that the translation methods are different. A channel selection consists of an item and a channel index, so the translator lets us encode or extract both from a packet.

        CLxUser_Item			 item;
        CLxUser_ChannelPacketTranslation	 chanPkt;
        LXtID4				 type_chan;
        int				 idxChan;

        type_chan = svcSel.LookupType (LXsSELTYP_CHANNEL);
        chanPkt.autoInit ();

Testing or selecting channels requires a temp packet comprising the item and the channel index.

        pkt = chanPkt.Packet (item, idxChan);
        if (svcSel.Test (type_chan, pkt) == LXe_TRUE) {

        pkt = chanPkt.Packet (item, idxChan);
        svcSel.Select (type_chan, pkt);

Getting the first selected channel gets the packet, then the item, then the channel.

        pkt = svcSel.ByIndex (selID_chan, 0);
        if (pkt == NULL) {
                // no selected channel

        } else if (!chanPkt.GetItem (pkt, item)) {
                // invalid packet (shouldn't happen)

        } else {
                idxChan = chanPkt.Index (pkt);

See also