Overview

Query Dialog Sidebar and Model Import

Query dialog side tools are now represented by QueryTabBehavior and QueryTabWindow objects managed by a QueryTabContainer. This replaces the single-purpose rightWindow and moves part-import behavior out of QueryDialogBehavior into QueryPartImportTabBehavior.

Extensions that customize the Query dialog can return additional tab behaviors from QueryDialogBehavior.tabBehaviors() or replace the standard part/model import behaviors by overriding the corresponding behavior factory method.

Part Owner Identification

In 17.0 sourceId was introduced at the core level to give parts a snapper specific unique identifier to help identify the owning object and ItemTag associated with the part. A gap was found from the changes where merged parts didn't have enough information to link changes from a part back to all ItemTags across all owners of the part. The main issue was that itemTagKey could be changed to not be the sourceId in a customization. To help solve this issue, PartSouceInfo was created as an object to own all the unique information that identifies the part. Currently this is Snapper owner, str sourceId, and str itemTagKey.

Preserving Part Adjustments When Resetting Item Tags

Resetting a user-modified Ind. Tag can change a part's adjustment key. CET now detects adjusted parts before the reset and can migrate their adjustments to the new key. This prevents price, quantity, and other part adjustments from unexpectedly becoming orphaned when an Ind. Tag returns to its default value.

Compile Time Changes

Query Dialog Sidebar and Model Import

QueryDialog no longer exposes a generic right-side window. Use the tab API instead:

Removed: public SubWindow rightWindow;
Removed: extend public SubWindow rightWindow();

Added: public QueryTabContainer tabContainer;
Added: extend public str activeTabKey();
Added: extend public QueryTabWindow activeTab();
Added: extend public void openTab(str key);
Added: extend public void closeActiveTab();

The old import window has been renamed and now participates in the tab framework:

Old: public class QueryImportWindow extends QuerySubWindow
New: public class QueryPartImportTabWindow extends QueryTabWindow

Old: extend public void setImportFileUrl(Url url)
New: extend public void setImportFileUrl(Url url, bool invokeCb=true)

The constructor no longer accepts an initial Url. Pass fileFilters when constructing the tab, then call setImportFileUrl() if an initial file is needed.

Part-import methods formerly overridden on QueryDialogBehavior have moved to QueryPartImportTabBehavior. This includes file-filter and importer factories, validation and cleanup, file-change handlers, and grid drag/drop handling. Migrate custom overrides to a QueryPartImportTabBehavior subclass and return it from queryPartImportTabBehavior():

public class MyPartImportTabBehavior extends QueryPartImportTabBehavior {
    public <str, str>[] getImportFileLabelsAndSuffixes() {
        return super(..) + [<"My Parts", "*.myparts">];
    }
}

public class MyQueryDialogBehavior extends QueryDialogBehavior {
    public QueryPartImportTabBehavior queryPartImportTabBehavior() {
        return MyPartImportTabBehavior();
    }
}

The following QueryDialogBehavior entry points have been replaced by the tab API:

Removed: extend public SubWindow initImportWindow(QueryDialog dialog, Url url=null)
Removed: extend public void initImportWindowEvents(SubWindow importWindow)
Removed: extend public SubWindow rightWindow(QueryDialog dialog)

Added: extend public QueryTabBehavior[] tabBehaviors()
Added: extend public QueryPartImportTabBehavior queryPartImportTabBehavior()
Added: extend public QueryModelImportTabBehavior queryModelImportTabBehavior()
Added: extend public SubWindow activeTab(QueryDialog dialog)

QueryControlWindow.importButton and its importBtnClicked event have been removed. Open the part-import tab with dialog.openTab(corePartImportTabBehavior().key()), or provide the desired tabs through QueryDialogBehavior.tabBehaviors().

The QuerySubWindow constructor now exposes the standard window arguments. Calls that passed a brush positionally must switch to a named argument because the second positional argument is now Font:

Old: QuerySubWindow(parent, myBrush)
New: QuerySubWindow(parent, brush=myBrush)

QuerySubWindow now calls the new overridable initEvents() method during construction. Subclasses should override that method for event wiring and call super(..) where appropriate.

Part Hierarchy Depth and Physical Dimensions

The recursive hierarchy-depth method on Part has been renamed so AbsPart.depth() can represent a physical measurement:

Old: final public int Part.depth()
New: final public int Part.maxDepth()

Replace calls that inspect the deepest child level with maxDepth(). Do not replace physical-dimension calls: AbsPart.depth() is a separate double value measured in meters.

Generic Part Codes and Export Data

The following interfaces have been added:

Added: public str BasicPartData._genericCode
Added: public str BasicPartData.genericCode()
Added: public void BasicPartData.setGenericCode(str value)
Added: extend public str PartData.genericCode()
Added: extend public void PartData.setGenericCode(str value)
Added: extend public str Part.genericCode(Space space=null)
Added: extend public str Part.adjustedGenericCode(Space space=null)
Added: extend public str PartProxy.genericCode(Part part)
Added: extend public PartTag Part.tag(str categoryLbl, World world=null, bool ignoreCase=false)

The OFDA hooks xmlMeasurements(str prefix, XmlStreamBuf buf) and xmlUserDefined(str prefix, XmlStreamBuf buf) have moved from ProdPart to the base Part class. A new xmlMeasurement() helper writes a measurement value and its type, system, and unit attributes.

Query Dialog API Follow-up

Query tab factories now receive their owning dialog. Update overrides and direct calls:

Old: extend public QueryTabBehavior[] tabBehaviors()
New: extend public QueryTabBehavior[] tabBehaviors(QueryDialog dialog)

Old: extend public QueryPartImportTabBehavior queryPartImportTabBehavior()
New: extend public QueryPartImportTabBehavior queryPartImportTabBehavior(QueryDialog dialog)

Old: extend public QueryModelImportTabBehavior queryModelImportTabBehavior()
New: extend public QueryModelImportTabBehavior queryModelImportTabBehavior(QueryDialog dialog)

The first argument to alignCenterY() is now the reference window rather than part of the rest sequence:

Old: public void alignCenterY(rest: Window[] controls)
New: public void alignCenterY(Window this, rest: Window[] controls)
Added: public void alignCenterX(Window this, rest: Window[] controls)
Added: public void alignCenter(Window this, rest: Window[] controls)

Part Owner Identification

BasicPartData

Old: public Snapper snapper : copy=reference;
New: private PartSourceInfo _sourceInfo : copy=reference;
Old: public void setOwner(Snapper z) 
New: public void setSourceInfo(PartSourceInfo info) {
Old: public Snapper owner()
New: public PartSourceInfo sourceInfo()
Old: public Snapper{} owners()
New: public PartSourceInfo{} sourceInfos() 

All direct access of the snapper field have been changed to owner method.

FlattenedPartData

Old: public Snapper{} snappers : copy=shallow;
Old: private str{} _sourceIds;
New: private PartSourceInfo{} _sourceInfos;
Old: public Snapper{} owners()
Old: final public str{} sourceIds()
New: public PartSourceInfo{} sourceInfos()
Old: final private void appendOwnersFrom(PartData data)
New: final private void appendSourceInfosFrom(PartData data)

Part

Removed:  private str _sourceId; // sourceId is stored on PartData now.
Added: extend public PartSourceInfo{} sourceInfos()

PartData

Added: extend public void setSourceInfo(PartSourceInfo info) : abstract
Added: extend public PartSourceInfo sourceInfo() : abstract
Added: extend public PartSourceInfo{} sourceInfos() : abstract
Added: extend public void setSourceId(str id)
Added: extend public str sourceId()
Added: extend public str{} sourceIds() 

The following methods have been updated to use PartSourceInfo and are no longer abstract

extend public void setOwner(Snapper z)
extend public Snapper owner()
extend public Snapper{} owners() 

Preserving Part Adjustments When Resetting Item Tags

The following public interfaces have been added:

Added: public bool Part.blockAdjustmentMigration
Old: final public void Part.migrateAdjusmentKeys(str oldKey)
New: final public void Part.migrateAdjusmentKeys(str oldKey, bool markOldPending=false)
Added: final public str Part.userItemTagInfoKey()
Added: extend public ItemTagInfo Part.userItemTagInfo()
Added: extend public ItemTagInfo Part.defaultItemTagInfo(bool putToWorld=false)
Added: final public bool SinglePartAdjustment.pendingSilentRemoval()
Added: final public void SinglePartAdjustment.markPendingSilentRemoval(bool value)
Added: public str untokenizedArticleCodeStr(Part[] parts, str breakS)
Added: public Part[] adjustedParts(Part[] parts, bool updatePartTags=true, Space space=null)
Added: public Part[] indTagModifiedParts(Part[] parts)

Existing one-argument calls to migrateAdjusmentKeys() remain valid. Pass markOldPending=true when the old adjustment may still be used by another flattened part; CET will remove it silently once it is no longer active.

Runtime/Behavior Changes

Query Dialog Sidebar and Model Import

Only one Query sidebar tab is expanded at a time. A tab's window is cached while the Query dialog remains open, so switching tabs preserves its contents; closing the dialog discards that state.

Applying an imported model creates or updates the queried snapper's graphical special after the user confirms the alignment options. Revert removes that graphical special and restores the owner's current graphics in the preview.

QuerySubWindow now initializes with stdLightFrame/frameStateDown defaults and invokes initEvents() automatically. Subclasses that require an unframed window must pass frameStyle=noFrame, and subclasses that previously wired events outside construction should avoid registering the same observers twice.

Generic Part Codes and Export Data

Generic-code lookup now prefers an adjusted value, then allows PartProxy.genericCode() to provide a value, and otherwise uses the value stored on PartData. Flattening a part preserves the stored generic code. Importing part data now stores item.genericCode directly instead of converting it into an adjusted value.

The Generic Code calculation column uses Part.genericCode() first and retains TAG2 as a fallback. Extensions that previously treated TAG2 as the only generic-code source should use the new interface.

Part Special Handling Helpers

containsSpecial(PropObj) now returns true only when the object's PartSpecialHolder contains data, rather than merely when a holder exists. Code that used an empty holder as a presence flag must use a different check.

applyCustomParts() now returns immediately for a null part sequence, applies additions, and then applies overrides through the two new helper functions.

Query Dialog API Follow-up

QueryButton.press() now invokes the inherited Button.press() before its onClick event. A constructor callback and an onClick observer can therefore both run for one press; verify that handlers do not perform the same action twice.

QueryTabWindow.setVisible(true) no longer calls alignControls() automatically. Custom tab windows that depend on realignment when shown must do so from their visibility handler or another appropriate lifecycle method.

The model-import preview now restores its initial camera location and recenters the extents whenever the preview is refreshed.

Part Owner Identification

Part

PartSourceInfo will be set during construction of a Part. The itemTagKey field on PartSourceInfo for a given Part after first setting the sourceId and when sourceId changes.

setUserItemTagInfo(str text) has been updated to use loop over PartSourceInfos instead of Snapper owners.

WorldPart

Added loaded0 call to initialize empty partSourcInfo.

Preserving Part Adjustments When Resetting Item Tags

Part.resetItemTagInfo() now restores the default ItemTagInfo across every owner, clears the cached final key, and returns the default info. Code that relied on the returned object representing the former user-modified info must be updated.

When an adjustment key changes, migrateAdjusmentKeys() now copies the adjustment to the new key instead of changing the existing adjustment in place. With markOldPending=true, the old adjustment remains available while it is active on another part and is silently removed after it becomes inactive rather than appearing as an orphan adjustment.

Refreshing a SinglePartAdjustment no longer replaces a previously stored adjustment currency with null. A non-null currency from the current part still takes precedence.