When creating a Chaos Tools content type plugin, what is a subtype and how are they used? I'm perfectly fine with creating Chaos Tools content types for use in Panels, but I've always used single =&gt; TRUE.

I've looked through the docs, but still can't quite seem to grok how to utilise subtypes.  None of the examples shipped with Chaos Tools seem to use them (they also all implement single =&gt; TRUE, and a quick grep of the Chaos Tools directory only shows the Custom Content type using subtypes, and it only appears to use it for including the export functionality (unless I'm reading it wrong - which is likely).
It's a good question, and some further information can be found in the ctools help file context-content.html


  The CTools pluggable content system provides various pieces of content
  as discrete bits of data that can be added to other applications, such
  as Panels or Dashboard via the UI. Whatever the content is added to
  stores the configuration for that individual piece of content, and
  provides this to the content.
  
  Each content type plugin will be contained in a .inc file, with
  subsidiary files, if necessary, in or near the same directory. Each
  content type consists of some information and one or more subtypes,
  which all use the same renderer. Subtypes are considered to be
  instances of the type. For example, the 'views' content type would
  have each view in the system as a subtype. Many content types will
  have exactly one subtype.


Also, in ctools_content_get_subtypes


  Get all of the individual subtypes provided by a given content type.
  This would be all of the blocks for the block type, or all of the
  views for the view type.


So in a sense, the subtypes are individual instances of that type, such as individual Views or Blocks, or custom content instances, that use the same rendering functions as the parent content type.

For example, you can see in ctools_content_process

// If a subtype plugin exists, try to use it. Otherwise assume single.
if (function_exists($function_base . 'content_types')) {
  $plugin['content types'] = $function_base . 'content_types';
}


In the example of the block content type, this function returns the content types, or sub types for that plugin.

/**
 * Return all block content types available.
 *
 * Modules wanting to make special adjustments the way that CTools handles their blocks
 * can implement an extension to the hook_block() family, where the function name is
 * of the form "$module . '_ctools_block_info'".
 */
function ctools_block_content_type_content_types() {
  $types = array();
  foreach (module_implements('block_info') as $module) {
    $module_blocks = module_invoke($module, 'block_info');
    if ($module_blocks) {
      foreach ($module_blocks as $delta =&gt; $block) {
        $info = _ctools_block_content_type_content_type($module, $delta, $block);
        // this check means modules can remove their blocks; particularly useful
        // if they offer the block some other way (like we do for views)
        if ($info) {
          $types["$module-$delta"] = $info;
        }
      }
    }
  }
  return $types;
}


Lastly one more example for good measure from the views_content content type plugin

/**
 * Return all content types available.
 */
function views_content_views_content_type_content_types($plugin) {
  $types = array();
  // It can be fairly intensive to calculate this, so let's cache this in the
  // cache_views table. The nice thing there is that if views ever change, that
  // table will always be cleared. Except for the occasional default view, so
  // we must use the Views caching functions in order to respect Views caching
  // settings.
  views_include('cache');
  $data = views_cache_get('views_content_all', TRUE);
  if (!empty($data-&gt;data)) {
    $types = $data-&gt;data;
  }

  if (empty($types)) {
    $views = views_get_all_views();

    foreach ($views as $view) {
      if (empty($view-&gt;disabled)) {
        $types[$view-&gt;name] = _views_content_views_content_type($view);
      }
    }

    views_cache_set('views_content_all', $types, TRUE);
  }

  return $types;
}


In this case _views_content_views_content_type returns

function _views_content_views_content_type($view) {
  $title = $view-&gt;get_human_name();

  $icon = 'icon_views_page_legacy.png';

  return array(
    'view' =&gt; $view-&gt;name, 
    'title' =&gt; $title, 
    'icon' =&gt; $icon, 
    'description' =&gt; filter_xss_admin($view-&gt;description), 
    'category' =&gt; t('Views'),
  );

}