You can set up a 'next callback' in $form_info, but not a 'previous callback'. The 'next callback' is called in both cases. The default function name is FORM_ID_next(&$form_state).
In here you control where to go next. The simplest solution is either you want to go to the next, or to the previous. Like this:
<?php
function mywizard_next(&$form_state) {
if (isset($form_state['triggering_element']['#next'])) {
$form_state['my_storage']['step'] = $form_state['triggering_element']['#next'];
}
// set the values built by this page
$form_state['my_storage'][$form_state['step']] = $form_state['values'];
// Update the cache with any changes.
mywizard_cache_set('form_values', $form_state['my_storage']);
}
?>
Why do it like this? Because, being one for elegance and aesthetics, I do not want the current step to appear in the URL (mywizard/step1). I want just the one URL (mywizard). Also notice the use of "triggering_element" which is an addition for Drupal 7, and is identical to "clicked_button". However "clicked_button" will be discontinued in Drupal 8.
The Chaos Tools form wizard needs you to build the $form_info array and call its function 'ctools_wizard_multistep_form' each time the page is to be rebuilt. So, in my function that gets called for the page I do some interesting things.
<?php
function mywizard_wizard() {
ctools_include('wizard');
// Fetch the form info array
$form_info = mywizard_form_info();
// Fetch the current form values from the cache...
$form_values = mywizard_cache_get('form_values');
if (empty($form_values)) {
// ...but if they aren't there, build the initial values
$form_values = array(
// set the first step
'step' => array_shift(array_keys($form_info['order'])),
// and my default start values (if any)
'mywizard_values' => array(),
);
mywizard_cache_set('form_values', $form_values);
}
// Build $form_state array
$form_state = array(
'my_storage' => $form_values,
);
// And build the current step
return ctools_wizard_multistep_form($form_info, $form_values['step'], $form_state);
}
?>
So, if you can follow this, when we enter the function we build the $form_info array which describes the whole multi-page form. If this was a complicated process (it could be) you could also cache the $form_info array so it only needs to be done once.
Then we either fetch the cached form data, which contains the next step, or build a new one if we don't have one (in other words this is the first time through).
We save this information in $form_state and then call the ctools multi-step form builder which finds the right form and builds it.
When "next" is clicked (or "back") we save the current step and cache it. To be picked up when the page is built next time.
Voila.
Edited later to remove some unnecessarily complex code. This should be viewed in conjunction with the Chaos Tools Advanced Help.