Helper for using version controls in LiveCode binary stacks.

The SmartCrumbs library allows the use of version control systems on binary stacks.

Flow of SmartCrumbs

This library allows the export and import of LiveCode binary stacks to a plain text file structure and some binaries, the latter of which will only be created for the embedded images, audioClips and videoClips. Note: *Video clips were discontinued in version 8 of LiveCode.

Flow of SmartCrumbs

How does SmartCrumbs do it?

The SmartCrumbs library assigns a GUID (Globally Unique ID) to each object which will be used to reference the objects in SmartCrumbs. With that GUID all the ‘crumbs’ corresponding to that object will be created. Not all properties will be saved, only those necessary to restore an exact copy of the stack being exported.

SmartCrumbs aims to create a JSON file with the grand total of properties needed to restore the stack. Within this JSON, the properties of all objects of the mainStack are stored. Each sub-stack is given a key that links to the mainStack and the mainStack key. In addition to the JSON file, other files will be created, for specific properties such as the script, embedded images, audioClips and videoClips*.

These files are here called ‘crumbs’, and they are the ones that allow us to use version control. Since version control is done on these files and not on the LiveCode binary, it is easier to keep track of projects and at any time see what was changed.

Why use SmartCrumbs?

SmartCrumbs gives the convenience of working with version control systems (VCS) without sacrificing the benefits of working with LiveCode's binary stacks. Thanks to the fact that this library exports binary stacks to plain text files and some binaries, this library adds several functions to a projects workflow. Some of them are:

How to use the SmartCrumbs library?

SmartCrumbs can be used in various ways - from the message box, its own script or the plugin (found in the MagicPalette). This guide will cover how to use the library from a script.

Characteristics that a LiveCode project must meet to use SmartCrumbs.

For SmartCrumbs to export a LiveCode stack to its corresponding crumbs, the stack (1) MUST be a binary stack, (2) MUST NOT be password protected and (3) MUST be saved. These are the only requirements that stacks must meet.

If there is more than one binary stack in a LiveCode project, when restoring the crumbs we must make sure to restore ALL of the binary stacks. The order in which it is done doesn't matter, as long as all stack and script-only stacks are loaded.

When restoring one or more binary stacks to a project, SmartCrumbs will first restore the objects with the majority of their properties. Lastly it will restore the properties of behaviors, icons and patterns in addition to the scripts. The objects are restored first because references to them are likely to be made in the behaviours and scripts, so avoids errors when rebuilding the stack.

How to export a LiveCode object with SmartCrumbs?

In order for version control systems to do its job effectively, the LiveCode objects need to be converted to plain text files. The SmartCrumbs library does this with the scExportObject command.

The scExportObject command

This command exports a LiveCode object, to its equivalent in SmartCrumbs. It expects one parameter, which is the reference to the long id of the object. Here are some examples:

scExportObject the long id of stack "Untitled 1"
scExportObject the long id of this card
scExportObject the long id of the target
scExportObject the long id of widget 1 of this card

The command creates a folder structure with the plain text files which is explained next.

Advice: Export the mainStack first, then each of the objects only as you modify them. When exporting the mainStack for the first time, SmartCrumbs creates the crumbs of all its children or objects. This way nothing is left unsaved. Then export any other mainStacks needed for your project which will be added to the SmartCrumbs file structure correctly.

Structure of folders and files created by the scExportObject command.

SmartCrumbs creates a folder called smartcrumbs which will be at the same level as the original mainStack binary being exported. This is why the mainStack binary file MUST be saved before exporting a LiveCode object.

Inside the smartcrumbs folder, there is a series of subfolders for each additional mainStack exported as part of the project with SmartCrumbs. These folders have the same name as their binary file with a .sc at the end. If, for example, the mainStack file is named test.livecode, the folder of its crumbs is named something like "../smartcrumbs/test.livecode.sc/".

Within those mainStack folders is created a file named stackFileProperties.json which defines most of the properties of the stacks objects. This includes substacks and all of their object references. Other folders are also created within to store specific properties such as one named scripts for the scripts of all objects, shareTexts for field object content, and medias which contains the binaries for any images, audioClips and videoClips used in the stack. These folders will only be created if an object with one of these properties exists.

Explaining a little more what the subfolders will contain

script: Contains a livecodescript file for each object whose script has at least one word. The file will be named after the GUID of the object they belong to.

medias: Contains the binary files of any images embedded in the stack and of the stacks stored audioClips and videoClips.

shareTexts: The HTMLtext of text fields are stored in this folder. Text fields can have different text on each card, so here a folder is created with the GUID of the text field, and inside that folder is a .HTML file named using the GUID of the Card which contains the HTMLText of the field on that card. Note: The SmartCrumbsVCW widget will create this structure if the field does not have the shareText property active. If this property is on, a single HTML with the field's GUID will be created at the root of the shareTexts folder.

How importing smart crumbs from a mainStack to LiveCode?

MainStacks are restored or imported back into Livecode using the scImportMainStack command. This command imports only the mainStack into LiveCode using its crumbs and recreating the binary file, unlike scExportObject which could export any object type.

The scImportMainStack command

The scImportMainStack command expects as parameters (1) the path to the folder where the crumbs of our mainStack are and (2) the name of the stack to open when it finishes restoring everything. If the name of the stack that should be opened on completion is not specified, the command will open all stacks in alphabetical order. Note: It is recommend that you always specify which stack to call unless you only have a single mainStack that needs opening, otherwise all stacks will be opened perhaps making it difficult to navigate.

To restore the main stack named test.livecode from the example above, use:

scImportMainStack "path/smartcrumbs/"

or scImportMainStack "path/smartcrumbs/test.livecode.sc/" In the previous example, only one mainStack was added so there is no need to specify the name of the stack to use.

Note: The path must always be to the smartcrumbs folder or one of its sub-folders. If the address to the smartcrumbs folder is used, SmartCrumbs restores all mainStacks that have the crumbs created inside this folder. While the other way only restores the mainStack to which those crumbs belong.

Note: When restoring a mainStack, SmartCrumbs checks that there is not currently open a stack with the identical name of any being restored. If so, SmartCrumbs will destroy the currently open stack to restore the new one. Therefore, it is recommended to close or save other stacks before importing a mainStack.

Version control systems can ignore certain files, for example the .livecode files are no longer needed to save a project. However, a stack with a dynamic UI often has a lot of changes that do not need keeping track of on the VCS. These false positives should be avoided as follows.

False positives in SmartCrumbs

False positives are those changes in the stack we are not interested in being detected and recorded by the version control systems.

For example, a stack of variable dimensions and objects arranged by code generates a lot of changes every time the stack is resized and the stack is exported. Since the objects are accommodated by code, there is no need to save those changes.

SmartCrumbs has several mechanisms to avoid us having to deal with these false positives.

Remove false positive from stack rectangle.

Version controllers will detect the slightest change. If a stack is stored in a position on the screen and the crumbs are shared with another developer who has different screen dimensions. If the stack is resized this will change the loc(ation), width, height and rect(angle) of the stack. Or if the stack location is simply moved on screen, the loc and rect will have changed but not the width or height.

However, since stacks do not save their rect (ie, they do not have top, left, bottom, right parameters), they are all restored to the center of the screen unless a loc(ation) parameter is set. This is the first mechanism to eliminate false positives from SmartCrumbs, however, this is done automatically, so you don't have to worry about it.

This must be kept in mind when using the mechanism of ignoring properties which is explained next.

How to ignore specific properties to eliminate false positives?

If an object is created that has a property whose value is modified by code, it may not need to update every time the crumbs are generated. SmartCrumbs can be set to ignore a given property and even ignore all changes to an object's properties.

Note: The only property that SmartCrumbs will never ignore is the script of the objects.

SmartCrumbs can ignore the changes of a specific property or properties of an object or the changes of all the properties of an object.

When ignoring the changes to one or more properties, SmartCrumbs saves the value of this property only once. Then the same value is kept regardless of what has changed in the object. If the value is removed from the JSON, SmartCrumbs will save it again with the current value the next time it is exported or updated.

How to tell SmartCrumbs to ignore property changes?

To tell SmartCrumbs to ignore changes to an object's property value, use the scIgnoreProperty command. When the object is restored it will always have the same value in the ignored properties.

The scIgnoreProperty command

This command expects three parameters, (1) the long id of the object to ignore the changes of, (2) the name of the property to be ignored and (3) an optional parameter which is used to tell SmartCrumbs to ignore this property on all objects of this type (unless the type is a stack) on the stack that owns the object passed in the first parameter or to tell it that all the children (applicable only to stacks, cards and groups) of the control passed in the first parameter will ignore the changes in the property if they have it.

Example:

The example shown below is a stack, "Example 1", of variable dimensions within which is a group named “header” and another named “menu”. Inside the header group are several objects accommodated by code. The menu group contains the application menu which will alway have the left parameter set to zero.

example of ignore property

When the stack size is changed, all objects in the header group change their rectangle including the group itself. So, many false positives will be produced every time the dimensions of the stack are modified.

To avoid these false positives, the library is told to ignore changes to the rectangle property of the stacks dynamically wrapped objects.

As SmartCrumbs does not save the stack rectangle, it only needs to ignore the width and height properties of the stack.

scIgnoreProperty the long id of stack "Example 1", "width"
scIgnoreProperty the long id of stack "Example 1", "height"

However, a number of false positives remain since the group "header" adapts to the width of the stack each time it changes, its own child controls rearrange inside of it adding to the number of changes recorded.

These false positives are eliminated in one of two ways. The first is to go object by object telling each to ignore the rectangle property or, simply tell it to ignore the rectangle property in the group "header" and inherit this to its children, like this:

scIgnoreProperty the long id of group "header", "rect", "children"

This way we will not have to remember what dimensions the stack had the first time the crumbs were created. :)

How to ignore changes to a custom property?

SmartCrumbs can also use the scIgnoreProperty command to ignore custom properties.

In LiveCode you can create simple custom properties or sets of these properties. To ignore just one of these properties, pass the name of the property to be ignored in the same way as a normal property in scIgnoreProperty. For a button with only one custom property called cProp, do as follows:

example of ignore custom property

scIgnoreProperty the long id of button 1, "cProp"

Now, changes to the cProp property will be ignored.

To ignore changes of the entire set of custom properties, use something similar to:

scIgnoreProperty the long id of button 1, "customKeys"

Note: If a custom property has the same name as a set of custom properties (considered bad practice), SmartCrumbs will ignore changes to only the first individual custom property, not the overall property set.

Ignore a custom property inside a custom property set.

To ignore just one property in one of many custom property sets, pass the name of the property set followed by a comma and then the name of that property that to be ignored. For example, a property set with the name cUpdate has a property called cURL inside which is to be ignored.

example of ignore custom property set

Do as follows:

scIgnoreProperty the long id of button 1, "cUpdate,cURL"

Note: Do not insert a space between the comma and either name. Use only the names with commas to separate them.

How to ignore the changes to all properties of an object?

To ignore the changes to all properties of an object, use the scIgnoreAllProperties command, which expects two parameters, (1) the long id of the object to which all property changes will be ignored and optionally (2) whether it applies to all objects of this type or to the children of the object passed in the first parameter..

scIgnoreAllProperties the long id of button 1

This will now ignore changes in the values of all properties of the first button in the stack.

How to stop Ignoring changes to an objects property?

When it is necessary to stop ignoring the changes to an object (temporarily or permanently), SmartCrumbs needs to be told to stop ignoring those changes. To do this, use the scStopIgnoreProperty or scIgnoreAllProperties command. These commands expect the same parameters as the scIgnoreProperty and the scIgnoreAllProperties commands but stop ignoring rather than begin.

How to know which properties are being ignored?

To find out which properties SmartCrumbs is ignoring on an object, use the scIgnoreProperties() function. It expects a single parameter, the long id of the object. If the object is ignored for all property changes, this function returns true. If no property is ignored, it returns empty, otherwise it returns a list with all the properties whose changes are ignored.

What is the SmartCrumbs Plugin system?

The plugins for SmartCrumbs are not referring to LiveCode IDE plugins. It refers to some plugins or library resources that the SmartCrumbs library uses to solve problems that may occur with some custom properties and some widgets.

It is not possible to foresee every possible problem that may arise with the library and so the plugins for the SmartCrumbs library allow us to create patches easily for those widgets, or for some other object which, under certain conditions, produces unwanted effects. It therefore allows us to extend the functionality of the library without having to modify the library itself. Also, a plugin needed in one project won't necessarily be needed in another so can be eliminated when building a standalone.

If, for example, a binary is saved inside a custom property, this will cause SmartCrumbs to attempt to convert that property to JSON and fail. So, a plugin is needed that converts that property to Base64 on save and on restore converts it back to its original value.

How to get the list of plugins in the library?

To find out which plugin the SmartCrumbs library has, use the scListPlugins() function. This will return an array where each key is the name of the plugin and inside has all the information about it.

One parameter can be passed to the scListPlugins() function at a time, so that it returns the plugins that meet a certain condition. The possible values of this parameter are:

If a parameter is not passed or the value passed is not one of the previous ones, the function returns all the plugins that are installed.

How to create a new plugin for SmartCrumbs?

The SmartCrumbs library offers the possibility of creating a plugin to solve certain errors that may arise or eliminate some properties not wanted to be saved. But these plugins must have a certain structure in order for the library to be able to use them.

To create a plugin for SmartCrumbs, the scCreatePlugin command must be used. The parameters it expects are (1) the name of the new plugin and (2) the path to the folder where it will be saved.

The scCreatePlugin command creates a plugin template for the SmartCrumbs library. The structure of its code will be explained later.

How to install and uninstall a plugin for SmartCrumbs?

To install a plugin to the SmartCrumbs library, use the scInstallPlugin command. It expects as its only parameter the path to the plugin file. This command copies the plugin file to the "My LiveCode/SmartCrumbsVCW" folder.

To get the path of the "My LiveCode/SmartCrumbsVCW" folder, copy the following into the message box:

put specialFolderPath("documents") & "/My LiveCode"

After installing the plugin it needs to be enabled in order to use it. To enable a plugin, use the scEnabledPlugin command and to disable it, the scDisabledPlugin command. These commands expect as their only parameter the name of the plugin that is to be enabled or disabled.

If there is a plugin that is no longer needed, use the scUninstallPlugin command to uninstall it. This command expects as its only parameter the name of the plugin to be uninstalled.

What is the structure and operation of the SmartCrumbs plugins?

SmartCrumbs plugins are script-only stacks, in which the library fires certain events. These events are what allow us to interact with the properties before they are saved and after the objects are restored.

The events that SmartCrumbs sends to those script-only stacks that it uses as a plugin are:

end startupPlugin - **restoreProperties**: This message is sent to the plugin when the library finishes restoring an object. This consists of two parameters, the first is the long id of the restored object. The second is the array with the properties that were set to the restored object. on restoreProperties pLongId, pProperties

end restoreProperties - **savingProperties**: This message is sent to the plugin when the library calls it. As the only parameter, it brings an array with the plugin configuration. on savingProperties pLongId, @pProperties

end savingProperties - **restoreScript**: This message is sent to the plugin when the library finishes restoring an object. This consists of two parameters, the first is the long id of the restored object. The second is the script that will be set on the restored object. on restoreScript pLongId, @pScript

end restoreScript
```

Warning

As you might notice, in all but restoreProperties we use an @ to indicate that the second parameter is a pointer. This means that the changes we make to this parameter will be sent to the SmartCrumbs library. Be careful with this, as this helps us extend the functionality of the library. It can also cause it to stop working properly.