Data changed design

From Emergent

Jump to: navigation, search

Contents

DataChanged Notification System

TA/CSS v4 introduces a new system for "subscribing to" and "publishing" notifications of data changes. This system typically obviates the need for individual data items to keep track of what other things will need to be updated when they themselves change -- this eliminates a lot of complicated and difficult to maintain code from v3.x.

In addition to proving information about data changes, the system provides an element of data abstraction that provides common properties and services that are needed throughout the gui system, such as diplay names, child counts, and clipboard capabilities and actions. The system is designed to be independent of existing class hiearchies -- therefore, the objects and classes involved are used either by delegation, encapsulation, or mixin interfaces. This allows, for example, the non-congruent types in the ta_type system to express the same basic functionality as those in taBase.

Here are the basic classes/interfaces used in this system:

  • taDataLinkClient_PtrList -- contains a list of clients of a data item
  • taDataLink -- classes that can provide DataLink services must provide one of these
  • IDataLinkClient -- classes that wish to be notified of changes must implement this interface
  • taiViewType -- a "bidding" class that provides datalinks, and panels, for types

taDataLink is specialized as follows:

taDataLink -- (ta_type.h) generic capability to do notifies, works in TA_GUI or TA_NON_GUI
  taiDataLink -- (ta_qtviewer.h) adds gui capabilities, particularly clipboard handling capabilities
    tabDataLink -- for taBase-derived objects
      tabListDataLink -- adds support for the children of lists
        tabGroupDataLink -- adds support for groups
    taClassDataLink -- (ta_classbrowse.h) common subclass for all the ta_type guys
      taTypeInfoDataLink -- handles TypeDef, MemberDef, etc.
      taTypeSpaceDataLink_Base -- common subclass for the spaces
        taTypeSpaceDataLink -- for TypeSpace objects
        taMethodSpaceDataLink -- for MethodSpace objects
        taMemberSpaceDataLink -- for MemberSpace objects

Note that the type-based links don't provide any notifications, since the type catalog is basically static (they support browsing of the types.)

ITypedObject -- provides a TypeDef and a "this" pointer -- used in many interfaces 
  IDataLinkClient -- provides notifications of changes and deletes

IDataLinkClient Methods

  • DataDestroying() -- signals to client that the data item is destroying
  • DataChanged() -- communicates the type of change, and up to 2-change-specific parameters

Data Changes

from ta_list.h, here are the reason codes -- note that deletion is communicated on its own method (not via a code), for various reasons

enum DataChangedReason { /* reason why DataChanged being called, as well as defining ops (also used by taBase and other classes) --
 some data change operations will emit multiple DataChanged calls */
  DCR_ITEM_UPDATED = 0, // after user edits (or load) ex. taBase::UpdateAfterEdit call; ops not used
  DCR_ITEM_REBUILT, // for complex items that support the STRUCT updates

  DCR_CHILD_ITEM_UPDATED, // op1=item; can optionally be invoked by an owned object (usually a member, usually not list/group items) -- owner can ignore this, or do something with it
  
  DCR_LIST_INIT,
  DCR_LIST_ITEM_INSERT,	// op1=item, op2=item_after, null=at beginning
  DCR_LIST_ITEM_UPDATE,	// op1=item
  DCR_LIST_ITEM_REMOVE,	// op1=item -- note, item not DisOwned yet, but has been removed from list
  DCR_LIST_ITEM_MOVED,	// op1=item, op2=item_after, null=at beginning
  DCR_LIST_ITEMS_SWAP,	// op1=item1, op2=item2
  DCR_LIST_SORTED,	// after sorting; ops not used

  DCR_GROUP_INSERT,	// op1=group, op2=group_after, null=at beginning
  DCR_GROUP_UPDATE,	// op1=group, typically called for group name change
  DCR_GROUP_REMOVE,	// op1=group -- note, item not DisOwned yet, but has been removed from list
  DCR_GROUP_MOVED,	// op1=group, op2=group_after, null=at beginning
  DCR_GROUPS_SWAP,	// op1=group1, op2=group2
  
  DCR_GROUP_ITEM_INSERT,	// op1=item, op2=item_after, null=at beginning
  DCR_GROUP_ITEM_UPDATE,	// op1=item
  DCR_GROUP_ITEM_REMOVE,	// op1=item -- note, item not DisOwned yet, but has been removed from list
  DCR_GROUP_ITEM_MOVED,	// op1=item, op2=item_after, null=at beginning
  DCR_GROUP_ITEMS_SWAP,	// op1=item1, op2=item2
  DCR_GROUP_LIST_SORTED,	// after sorting; ops not used

  DCR_UPDATE_VIEWS, // no ops; sent for UpdateAllViews
  
  DCR_STRUCT_UPDATE_BEGIN, // for some updating, like doing Layer->Build, better for gui to just do one
  DCR_STRUCT_UPDATE_END,  // update operation at the end of everything
  DCR_DATA_UPDATE_BEGIN, // for some data changes, like various log updates, better for gui to just do one
  DCR_DATA_UPDATE_END,  // update operation at the end of everything
  
  DCR_ITEM_DELETING  // NOTE: not used in standard DataChanged calls, but may be used by forwarders, ex. taDataMonitor

#ifndef __MAKETA__
 ,DCR_LIST_MIN		= DCR_LIST_INIT,
  DCR_LIST_MAX		= DCR_LIST_SORTED,
  DCR_GROUP_MIN		= DCR_GROUP_INSERT,
  DCR_GROUP_MAX		= DCR_GROUPS_SWAP,
  DCR_LIST_ITEM_MIN	= DCR_LIST_ITEM_INSERT,
  DCR_LIST_ITEM_MAX	= DCR_LIST_SORTED,
  DCR_GROUP_ITEM_MIN	= DCR_GROUP_ITEM_INSERT,
  DCR_GROUP_ITEM_MAX	= DCR_GROUP_LIST_SORTED
#endif
};

List and Group Changes

Extensive support for Lists and Groups has been included in the change system. Note that these notifications are emitted from the list/group object -- the objects in the list/group do not emit any particular change notifications as a result of being in a list/group. Also note that changes to the child objects themselves do not automatically result in notifications from the parent list/group; however, a special change code (DCR_CHILD_ITEM_UPDATED) is provided if a class wants to manually provide a notification when a child changes -- this must normally be implemented by overriding the DataChanged of the child class, and forwarding it as a CHILD_ITEM_UPDATE throught the parent's DC routine.

Lists provide a notification upon all major changes to list membership: add/insert, delete, and move (including sorts).

Groups inherit the List notifications, and provide two additional sets of notifications:

  • GROUP_xxx -- for groups themselves (subgroup add/insert, subgroup delete, etc.) ; similar semantics to List items
  • GROUP_ITEM_xxx -- these are similar to List operations, except they all occur on the root group -- this lets you monitor all item changes, anywhere in the group hierarchy, by simply monitoring the root group itself

DataLink Providers

  • taBase defines methods for interacting with data links
  • taOBase is the first class that implements support for data links

In general, no taDataLink object exists until a client "subscribes" to a data object. At that point, the following happens:

  1. . if the object already has a datalink, then skip the next step
  2. . the correct taiViewType object will create a datalink of the right type for the object Type (ex a tabListDataLink for a taList)
  3. . the client will get added to the list of clients kept in the datalink

Once a datalink is created for an object, it is typically never destroyed (until the object destroys); the list object in which clients are kept is dynamically created/deleted as needed.

When a change occurs in the data object:

  1. . it calls the correct routine (Delete or Change) on its datalink (if it has one)
  2. . the DataLink iterates over the list of clients, and sends the message to the client object

In taBase, the UpdateAfterEdit routine automatically sends a ITEM_UPDATED notification to all subscribers.

It is always permissible to manually issue notifications. This should be done, for example, if program code changes the values in an object, but doesn't call UAE. In this case, on taBase, it can manually call DataChanged(DCR_ITEM_UPDATED).

Batch Nested Updates

In addition to simple notifications (1 change -- 1 notify), the taDataView class also supports nesting of Structural and Data updates. These operations ALWAYS occur in pairs. (Issuing a Start without an End will result in runaway expansion of the universe; issuing an End without a Start will result in runaway contraction of the universe!)

A Data Update is a change to parameters of an object. An example might be a routine that updates the activations of an entire layer -- it could issue a StartDataUpdate, make the changes, then issue EndDataUpdate. A Structural Update involves adding or removing objects, such as adding a layer or projection, or resizing a layer. A Struct update occuring withing the scope of a Data Update turns the eventual change notification into a Struct update.

In order to avoid unnecessary updates in T3 (which can be slow because of the complexity of Inventor/OpenGL), only the final result of a series of changes is acted on. Therefore, taDataView has logic to track the Struct and Data changes, and also to see if they are within such changes in the parents -- the end result is that only changes necessary are actually rendered. The T3 system handles Data Updates by updating all the parameters of the Inventor objects (it issues a recursive Render_impl); T3 handles a Struct update by nuking and then rebuilding the Inventor objects.

Handling DataChanged

When handling DataChanged keep in mind that you are still inside a possibly deeply nested block of code involving changes to objects -- therefore, you should avoid making any changes to objects while handling notifications from those objects (or related objects, such as their parents, siblings, etc.)

Related Classes

taSmartRef

A SmartRef acts just like a smart pointer, but in addition, will automatically null itself if the object to which it points destroys. These can hold refs to any taBase object, and do not affect the lifetime of the object (they don't do refs). If embedded in a taBase, you can "own" the ref in the InitLinks in which case your UpdateAfterEdit will automatically get invoked if the pointer deletes. There is also a stub method on taBase that receives notifications from owned SmartRefs, so you can monitor all the DataChanged calls issues by the object if you want.

Becoming a DataLink client

A cheap way is described previously, namely, using a SmartRef to an object.

Many objects in the system implement the IDataLinkClient interface. This has only a few methods (DataChanged, DataDestroying) which you need to reimplement.

Personal tools