diff --git a/README.txt b/README.txt
index e24eb42..a16b191 100644
--- a/README.txt
+++ b/README.txt
@@ -1,7 +1,7 @@
 
 Description
 -----------
-This module defines the "money" CCK field. It uses the Currency API, which is
+This module defines the "money" field. It uses the Currency API, which is
 included in the Currency module, to get a list of valid currencies.
 
 Only amounts with 2 decimals can be used. Any decimal separator and any digit
@@ -12,8 +12,8 @@ any point, only integers are stored in the database.
 
 Dependencies
 ------------
-* CCK (http://drupal.org/project/cck)
 * Currency API (http://drupal.org/project/currency)
+* Format Number (http://drupal.org/project/format_number)
 
 
 Installation
@@ -23,18 +23,3 @@ Installation
 
 2) Enable the module.
 
-
-Sponsor
--------
-Etienne Leers of http://creditcalc.biz.
-
-
-Author
-------
-Wim Leers
-
-* mail: work@wimleers.com
-* website: http://wimleers.com/work
-
-The author can be contacted for paid customizations of this module as well as
-Drupal consulting, development and installation.
diff --git a/money.css b/money.css
index eaa715e..b166892 100644
--- a/money.css
+++ b/money.css
@@ -1,5 +1,10 @@
 
-/* Put the description of the "amount" form item below the form items. */
-div.money-field-form-items div.description {
-	display: block;
+/* Checkboxes for currency selection. */
+.money-field-currency-checkboxes .form-checkboxes .form-item {
+  display: block;
+  float: left;
+  width: 300px;
+}
+.money-field-currency-checkboxes .description {
+  clear: both;
 }
diff --git a/money.info b/money.info
index 6a654e2..31bf6fe 100644
--- a/money.info
+++ b/money.info
@@ -1,4 +1,7 @@
-name = Money
-description = Allows the user to define a currency and an amount in custom content types.
-dependencies = content currency_api
-package = CCK
+name = Money Field
+description = Defines a field with an amount and a currency.
+dependencies[] = currency_api
+dependencies[] = format_number
+dependencies[] = formatted_number
+package = Fields
+core = 7.x
diff --git a/money.module b/money.module
index 9db55d7..e2a7286 100644
--- a/money.module
+++ b/money.module
@@ -2,385 +2,451 @@
 
 /**
  * @file
- * This module defines the "money" CCK field. It uses the Currency API, which
- * is included in the Currency module, to get a list of valid currencies.
- * 
- * Only amounts with 2 decimals can be used. Any decimal separator and any
- * digit group separator can be used, but it defaults to the comma and the dot
- * respectively, which is according to ISO 31-0. The separators can be changed
- * at any point, only integers are stored in the database.
+ * This module defines the Money field.
  */
 
-//----------------------------------------------------------------------------
-// CCK hooks.
-
 /**
- * Implementation of hook_field_info().
+ * Implements hook_field_info().
  */
 function money_field_info() {
-  return array('money' => array('label' => t('Money')));
+  return array(
+    'money' => array(
+      'label' => t('Money'),
+      'description' => t('This field stores and renders an amount with its currency.'),
+      'settings' => array('min' => '', 'max' => '', 'precision' => 10, 'scale' => 2),
+      'instance_settings' => array('min' => '', 'max' => ''),
+      'default_widget' => 'money_widget',
+      'default_formatter' => 'money_default',
+    ),
+  );
 }
 
 /**
- * Implementation of hook_field_settings().
+ * Implements hook_field_settings_form().
  */
-function money_field_settings($op, $field) {
-  switch ($op) {
-    case 'form':
-      $form = array();
-      $form['currency_list'] = array(
-        '#value' => theme('money_field_settings_currency_list', currency_api_get_list()),
-      );
-      $form['allowed_currencies'] = array(
-        '#type' => 'textarea',
-        '#rows' => 5,
-        '#title' => t('Currencies'),
-        '#description' => t('Enter the 3-letter ISO codes for the currencies that you want to allow, separated by commas. Leave empty to allow all currencies.'),
-        '#default_value' => (isset($field['allowed_currencies'])) ? $field['allowed_currencies'] : '',
-      );
-      return $form;
+function money_field_settings_form($field, $instance, $has_data) {
+  $settings = $field['settings'];
+  $form = array();
+
+  $form['precision'] = array(
+    '#type' => 'select',
+    '#title' => t('Precision'),
+    '#options' => drupal_map_assoc(range(10, 32)),
+    '#default_value' => $settings['precision'],
+    '#description' => t('The total number of digits to store in the database, including those to the right of the decimal.'),
+    '#disabled' => $has_data,
+  );
+  $form['scale'] = array(
+    '#type' => 'select',
+    '#title' => t('Scale'),
+    '#options' => drupal_map_assoc(range(0, 10)),
+    '#default_value' => $settings['scale'],
+    '#description' => t('The number of digits to the right of the decimal.'),
+    '#disabled' => $has_data,
+  );
 
-    case 'validate':
-      $valid_currencies = array_keys(currency_api_get_list());
-      $allowed_currencies = _money_parse_currencies($field['allowed_currencies']);
-      foreach ($allowed_currencies as $currency) {
-        if (!in_array($currency, $valid_currencies)) {
-          form_set_error('allowed_currencies', t('The currency %currency is not a valid currency.', array('%currency' => $currency)));
-        }
-      }
-      break;
+  return $form;
+}
 
-    case 'save':
-      return array('allowed_currencies');
+/**
+ * Implements hook_field_info_alter().
+ */
+function money_field_info_alter(&$info) {
+  // Add min/max settings to decimal field types.
+  if (isset($info['money'])) {
+    $precision = $info['money']['settings']['precision'];
+    $scale = $info['money']['settings']['scale'];
+    $min = (float)('-' . str_repeat('9', $precision - $scale) . '.' . str_repeat('9', $scale));
+    $max = (float)(str_repeat('9', $precision - $scale) . '.' . str_repeat('9', $scale));
+    $info['money']['settings']['min'] = $min;
+    $info['money']['settings']['max'] = $max;
+  }
+}
 
-    case 'database columns':
-      $columns['amount'] = array(
-        'type' => 'int',
-        'length' => 13,
-        'not null' => TRUE,
-        'default' => 0,
-        'unsigned' => FALSE,
-      );
-      $columns['currency'] = array('type' => 'varchar', 'length' => 3);
-      return $columns;
+/**
+ * Implements hook_field_instance_settings_form().
+ */
+function money_field_instance_settings_form($field, $instance) {
+  $settings = $instance['settings'];
+
+  $form['min'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Minimum'),
+    '#default_value' => $settings['min'],
+    '#description' => t('The minimum value that should be allowed in this field. Leave blank for no minimum.'),
+    '#element_validate' => array('_element_validate_limit'),
+    '#weight' => -1,
+  );
+  $form['max'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Maximum'),
+    '#default_value' => $settings['max'],
+    '#description' => t('The maximum value that should be allowed in this field. Leave blank for no maximum.'),
+    '#element_validate' => array('_element_validate_limit'),
+    '#weight' => -1,
+  );
 
-    case 'filters':
-      return array(
-        'default' => array(
-          'name' => t('Default'),
-          'operator' => 'views_handler_operator_gtlt',
-        ),
-        'currency_order' => array(
-          'name' => t('Order by currency'),
-          'operator' => 'views_handler_operator_gtlt',
-        ),
+  return $form;
+}
+
+/**
+ * Implements hook_field_validate().
+ *
+ * Possible error codes:
+ * - 'money_min': The value is less than the allowed minimum value.
+ * - 'money_max': The value is greater than the allowed maximum value.
+ * - 'money_currency': Currency is missing.
+ * - 'money_amount': Amount is missing.
+ */
+function money_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {
+  foreach ($items as $delta => $item) {
+    if ($item['amount'] != '') {
+      $min = is_numeric($instance['settings']['min']) ? $instance['settings']['min'] : $field['settings']['min'];
+      if (is_numeric($min) && $item['amount'] < $min) {
+        $errors[$field['field_name']][$langcode][$delta][] = array(
+          'error' => 'money_min',
+          'message' => t('%name: the value may be no less than %min.', array('%name' => $instance['label'], '%min' => $min)),
+        );
+      }
+      $max = is_numeric($instance['settings']['max']) ? $instance['settings']['max'] : $field['settings']['max'];
+      if (is_numeric($max) && $item['amount'] > $max) {
+        $errors[$field['field_name']][$langcode][$delta][] = array(
+          'error' => 'money_max',
+          'message' => t('%name: the value may be no greater than %max.', array('%name' => $instance['label'], '%max' => $max)),
+        );
+      }
+      if (empty($item['currency'])) {
+        $errors[$field['field_name']][$langcode][$delta][] = array(
+          'error' => 'money_currency',
+          'message' => t('%name: currency is required when an amount is specified.', array('%name' => $instance['label'])),
+        );
+      }
+    }
+    if (!is_numeric($item['amount']) && $item['currency']) {
+      $errors[$field['field_name']][$langcode][$delta][] = array(
+        'error' => 'money_amount',
+        'message' => t('%name: a valid amount is required when a currency is specified.', array('%name' => $instance['label'])),
       );
+    }
   }
 }
 
 /**
- * Implementation of hook_field().
+ * Implements hook_field_is_empty().
  */
-function money_field($op, &$node, $field, &$items, $teaser, $page) {
-  switch ($op) {
-    case 'validate':
-      $allowed_currencies = _money_parse_currencies($field['allowed_currencies']);
-
-      if (is_array($items)) {
-        foreach ($items as $delta => $item) {
-          // Validate the currency.
-          if (!in_array($item['currency'], $allowed_currencies)) {
-            form_set_error($field['field_name'] .']['. $delta .'][currency', t('The currency %currency is not allowed.', array('%currency' => t($item['currency']))));
-          }
-          // Validate the amount.
-          if (!is_numeric($item['amount'])) {
-            form_set_error($field['field_name'] .']['. $delta .'][amount', t('You entered an invalid amount.'));
-          }
-        }
-      }
-      break;
+function money_field_is_empty($item, $field) {
+  if (!is_numeric($item['amount']) && empty($item['currency'])) {
+    return TRUE;
   }
+  return FALSE;
 }
 
 /**
- * Implementation of hook_field_formatter_info().
+ * Implements hook_field_formatter_info().
  */
 function money_field_formatter_info() {
   return array(
-    'default' => array(
+    'money_default' => array(
       'label' => t('Default'),
       'field types' => array('money'),
     ),
+    'money_unformatted' => array(
+      'label' => t('Unformatted'),
+      'field types' => array('money'),
+    ),
   );
 }
 
 /**
- * Implementation of hook_field_formatter().
+ * Implements hook_field_formatter_view().
  */
-function money_field_formatter($field, $item, $formatter, $node) {
-  if (empty($item['amount'])) {
-    return '';
-  }
-  else {
-    $decimal_separator = _money_get_decimal_separator($field['widget']['decimal_separator']);
-    $digit_group_separator = _money_get_digit_group_separator($field['widget']['digit_group_separator']);
-    return check_plain(number_format($item['amount']/100, 2, $decimal_separator, $digit_group_separator));
+function money_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
+  $element = array();
+
+  foreach ($items as $delta => $item) {
+    $amount = $display['type'] == 'money_default' ? format_number($item['amount'], $field['settings']['scale']) : $item['amount'];
+    $currency = $item['currency'];
+    $output = '';
+    foreach (explode('|', $instance['widget']['settings']['currency_display_mode']) as $option) {
+      switch ($option) {
+      case 'a':
+        // The amount.
+        $output .= $amount;
+        break;
+      case 's':
+        // Currency symbol.
+        $currency_symbols = currency_api_get_symbols();
+        if (isset($currency_symbols[$currency])) {
+          $output .= $currency_symbols[$currency];
+          break;
+        }
+        // Fall back to currency code.
+      case 'c':
+        // Currency code.
+        $output .= $currency;
+        break;
+      case '+':
+        // Separator.
+        $output .= $display['type'] == 'money_default' ? "\xC2\xA0" : ' ';
+        break;
+      }
+    }
+
+    $element[$delta] = array('#markup' => $output);
   }
+
+  return $element;
 }
 
 /**
- * Implementation of hook_widget_info().
+ * Implements hook_field_widget_info().
  */
-function money_widget_info() {
+function money_field_widget_info() {
   return array(
-    'money_default' => array(
-      'label' => 'Select list for the currency, textfield for the amount',
+    'money_widget' => array(
+      'label' => t('Amount and currency'),
       'field types' => array('money'),
+      'settings' => array(
+        'currency_select_mode' => 'name',
+        'currency_display_mode' => 'a|+|c',
+        'decimals_display_mode' => 'field',
+        'currencies' => array(
+          'allowed_currencies' => array()
+        ),
+      ),
     ),
   );
 }
 
 /**
- * Implementation of hook_widget_settings().
+ * Implements hook_field_widget_form().
  */
-function money_widget_settings($op, $widget) {
-  switch ($op) {
-    case 'form':
-      $form = array();
-      $form['decimal_separator'] = array(
-        '#type' => 'textfield',
-        '#title' => t('Decimal separator'),
-        '#default_value' => _money_get_decimal_separator($widget['decimal_separator']),
-        '#size' => 5,
-        '#maxlength' => 255,
-        '#description' => t(
-          'Three decimal separators are used across the planet: the dot
-          (English-speaking countries), the comma (Europe) and the momayyez
-          (Arab world and Iran). ISO 31-0 specifies both the dot and the comma
-          as valid, but prefers the comma, this is also the default.'
-        ),
-      );
-      $form['digit_group_separator'] = array(
-        '#type' => 'textfield',
-        '#title' => t('Digit group separator'),
-        '#default_value' => _money_get_digit_group_separator($widget['digit_group_separator']),
-        '#size' => 5,
-        '#maxlength' => 255,
-        '#description' => t(
-          'Three digit group separators are used across the planet: the comma
-          (English-speaking countries), the dot (Europe) and the space. ISO
-          31-0 specifies only the space as valid, this is also the default.'
+function money_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
+  $value = isset($items[$delta]['amount']) ? $items[$delta]['amount'] : '';
+  // Substitute the decimal separator.
+  $value = strtr($value, '.', format_number_get_options('decimal_point'));
+  $element += array(
+    '#type' => 'fieldset',
+    '#tree' => TRUE,
+    '#attributes' => array('class' => array('container-inline')),
+  );
+  $element['amount'] = array(
+    '#type' => 'textfield',
+    '#default_value' => $value,
+    // Allow a slightly larger size than the field length to allow for some
+    // configurations where all characters won't fit in input field.
+    '#size' => $field['settings']['precision'] + 4,
+    // Allow two extra characters for signed values and decimal separator.
+    '#maxlength' => $field['settings']['precision'] + 2,
+    '#attributes' => array('class' => array('formatted-number'), 'decimals' => $field['settings']['scale']),
+    '#attached' => array(
+      'css' => array(
+        drupal_get_path('module', 'format_number') . '/format_number.css',
+        drupal_get_path('module', 'formatted_number') . '/formatted_number.css',
+      ),
+      'js' => array(
+        drupal_get_path('module', 'format_number') . '/format_number.js',
+        drupal_get_path('module', 'formatted_number') . '/formatted_number.js',
+        array(
+          'data' => array('format_number' => format_number_get_options()),
+          'type' => 'setting',
         ),
-      );     
-      return $form;
-    case 'save':
-      return array('decimal_separator', 'digit_group_separator');
-  }
+      ),
+    ),
+  );
+  $element['currency'] = array(
+    '#type' => 'select',
+    '#default_value' => (isset($items[$delta]['currency']) ? $items[$delta]['currency'] : array()),
+    '#options' => money_get_widget_currencies($instance, $element),
+  );
+
+  $element['#element_validate'][] = 'money_field_widget_validate';
+  return $element;
 }
 
 /**
- * Implementation of hook_widget().
+ * Build currency options for the given field/widget.
  */
-function money_widget($op, &$node, $field, &$items) {
-  if ($field['widget']['type'] == 'money_default') {
-    switch ($op) {
-      case 'prepare form values':
-        $decimal_separator = _money_get_decimal_separator($field['widget']['decimal_separator']);
-        $digit_group_separator = _money_get_digit_group_separator($field['widget']['digit_group_separator']);
-      
-        if (!count($items)) {
-          $items[0] = array();
-        }
-        else {
-          foreach ($items as $delta => $item) {
-            $items[$delta]['amount'] = check_plain(number_format($item['amount']/100, 2, $decimal_separator, $digit_group_separator));
-          }
-        }
-        break;
-
-      case 'form':
-        drupal_add_css(drupal_get_path('module', 'money') .'/money.css');
-
-        $decimal_separator = _money_get_decimal_separator($field['widget']['decimal_separator']);
-        $allowed_currencies = _money_parse_currencies($field['allowed_currencies']);
-
-        // Variables to be used in the "currency" form item.
-        $currency_options = array_combine($allowed_currencies, $allowed_currencies);
-
-        // Variables to be used in the "amount" form item.
-        if (isset($field['widget']['default_value'][0]['amount'])) {
-          $amount_default = check_plain(number_format($field['widget']['default_value'][0]['amount']/100, 2, $decimal_separator, $digit_group_separator));
-        }
-        else {
-          $amount_default = check_plain(number_format("0{$decimal_separator}00"/100, 2, $decimal_separator, $digit_group_separator));
-        }
-        $amount_description = t(
-          'Use "@decimal_separator" as the decimal separator and (optionally)
-          "@digit_group_separator" as the digit group separator. You can
-          only enter two decimals.',
-          array(
-            '@decimal_separator' => $field['widget']['decimal_separator'],
-            '@digit_group_separator' => $field['widget']['digit_group_separator'],
-          )
-        );
-
-        // If this field is configured as a multiple value field, make sure
-        // that there are at least 3 form items.
-        while ($field['multiple'] && count($items) < 3) {
-          $items[] = array();
-        }
-
-        // Create the prefix in which we'll store first the label, then a
-        // container div in which we'll put the actual form elements.
-        $prefix = '<div class="form-item">';
-        $prefix .= '<label>'. t($field['widget']['label']);
-        if (!empty($field['required'])) {
-          $prefix .= '<span class="form-required" title="'. t('This field is required.') .'">*</span>';
-        }
-        $prefix .= '</label>';
-        
-        // Actual form creation begins here.
-        $form = array();
-        $form[$field['field_name']]['#tree'] = TRUE;
-        $form[$field['field_name']]['#prefix'] = $prefix;
-        $form[$field['field_name']]['#type'] = ($field['multiple']) ? 'fieldset' : 'markup';
-        $form[$field['field_name']]['#suffix'] = '</div>';
-
-        foreach ($items as $delta => $item) {      
-          // These are the actual form items for each money field.
-          $form[$field['field_name']][$delta]['#tree'] = TRUE;
-          $form[$field['field_name']][$delta]['currency'] = array(
-            '#type' => 'select',
-            '#options' => $currency_options,
-            '#default_value' => isset($item['currency']) ? $item['currency'] : $field['widget']['default_value'][0]['currency'],
-            '#attributes' => array('class' => 'money-field money-field-currency'),
-            '#prefix' => '<div class="container-inline money-field-form-items">',
-          );
-          $form[$field['field_name']][$delta]['amount'] = array(
-            '#type' => 'textfield',
-            '#size' => 20,
-            '#maxlength' => 25,
-            '#default_value' => isset($item['amount']) ? $item['amount'] : $amount_default,
-            '#attributes' => array('class' => 'money-field money-field-amount'),
-            '#description' => ($delta == end(array_keys($items))) ? $amount_description : NULL, 
-            '#suffix' => '</div>',
-          );
-        }
-
-        return $form;
-      
-      case 'validate':
-        // Generate the regular expression to validate the entered amounts.
-        $decimal_separator = preg_quote(_money_get_decimal_separator($field['widget']['decimal_separator']));
-        $digit_group_separator = preg_quote(_money_get_digit_group_separator($field['widget']['digit_group_separator']));
-        $regexp = "/^-?(((\d{1,3}". $digit_group_separator .")?(\d{3}". $digit_group_separator .")*(\d{3}){1})|\d+)(". $decimal_separator ."\d{1,2})?$/";
-
-        // Make sure the amount is entered using the correct format.
-        foreach ($items as $delta => $item) {
-          if (!empty($item['amount']) && !preg_match($regexp, $item['amount'])) {
-            form_set_error($field['field_name'] .']['. $delta .'][amount', t('The amount is formatted invalidly.')); 
-          }
-        }
-        break;
-
-      case 'process form values':
-        $decimal_separator = _money_get_decimal_separator($field['widget']['decimal_separator']);
-        $digit_group_separator = _money_get_digit_group_separator($field['widget']['digit_group_separator']);
-
-        foreach ($items as $delta => $item) {
-          if (empty($item['amount'])) {
-            unset($items[$delta]['amount']);
-          }
-          else {
-            // Convert the entered amount to be compatible with PHP's number
-            // notation: a dot as a decimal separator, nothing as a digit
-            // group separator.
-            $converted_amount = str_replace(array($decimal_separator, $digit_group_separator), array('.', ''),  $item['amount']);
+function money_get_widget_currencies($instance, $element) {
+  $widget = $instance['widget'];
+  $settings = $widget['settings'];
+  // Currently implemented modes: code, name. See money_field_widget_settings_form().
+  $mode = $settings['currency_select_mode'];
+
+  // Prepare the array of allowed currencies.
+  if (isset($settings['currencies']['allowed_currencies']) && is_array($settings['currencies']['allowed_currencies'])) {
+    // Obtain the list of allowed currencies. Note that this array is in the form of 'code' => boolean.
+    $allowed_currencies = array_filter($settings['currencies']['allowed_currencies']);
+  }
+  else {
+    // Initialize array when the list has not been already set in field settings.
+    $allowed_currencies = array();
+  }
 
-            // Now convert the amount to make it storable as an integer.
-            // We are always working with a maximum of 2 decimals, this means
-            // that one unit in the database corresponds to 1/100th of a unit
-            // in reality (i.e. in forms and on display).
-            $items[$delta]['amount'] = $converted_amount * 100;
-          }
-        }
-        break;
+  // When no currency has been specified in widget settings we allow them all.
+  if (empty($allowed_currencies)) {
+    // Note that this array is built in the form of 'code' => 'name'.
+    $allowed_currencies = currency_api_get_list();
+  }
+  else {
+    // One or more currencies have been specified in widget settings.
+    if ($mode == 'name') {
+      // Build the array in the form of 'code' => 'name' extracting the
+      // allowed currencies from the array returned from currency_api.
+      $allowed_currencies = array_intersect_key(currency_api_get_list(), $allowed_currencies);
     }
   }
-}
 
+  // If the requested mode is 'code', then we need to transform the array
+  // so that item keys are also used for values.
+  if ($mode == 'code') {
+    $allowed_currencies = array_keys($allowed_currencies);
+    $allowed_currencies = array_combine($allowed_currencies, $allowed_currencies);
+  }
 
-//----------------------------------------------------------------------------
-// Private functions.
+  // When field is not required, an additional empty currency is pushed on top of the resulting list.
+  if (!$element['#required']) {
+    $allowed_currencies = array('' => ($mode == 'code' ? '---' : t('-- Select currency --'))) + $allowed_currencies;
+  }
 
-/**
- * Parse currency codes from a comma-separated list.
- *
- * @param $currencies_string
- *   A string containing a list of currency codes, separated by commas.
- * @return
- *   An array of currency code.
- */
-function _money_parse_currencies($currencies_string) {
-  return explode(',', str_replace(' ', '', trim($currencies_string)));
+  return $allowed_currencies;
 }
 
 /**
- * Get the decimal separator from a variable, use the default if the variable
- * is empty.
- *
- * @param $decimal_separator
- *   A variable that possibly contains a decimal separator.
- * @return
- *   A decimal separator, either the variable or the default (a comma).
+ * Implements hook_field_widget_settings_form().
  */
-function _money_get_decimal_separator($decimal_separator = NULL) {
-  return (!empty($decimal_separator)) ? $decimal_separator : ',';
+function money_field_widget_settings_form($field, $instance) {
+  $widget = $instance['widget'];
+  $settings = $widget['settings'];
+
+  $options = array('code' => t('Currency code'), 'name' => t('Currency name'));
+  $form['currency_select_mode'] = array(
+    '#type' => 'radios',
+    '#title' => t('Currency selection mode'),
+    '#options' => $options,
+    '#default_value' => $settings['currency_select_mode'],
+    '#required' => TRUE,
+    '#description' => t('Choose the format of the label that will be displayed for options of the currency select list.'),
+  );
+  $options = money_get_display_modes();
+  $form['currency_display_mode'] = array(
+    '#type' => 'select',
+    '#title' => t('Currency display mode'),
+    '#options' => $options,
+    '#default_value' => $settings['currency_display_mode'],
+    '#required' => TRUE,
+    '#description' => t('Choose the format that will be used to display this money field when a node is rendered.'),
+  );
+  if (function_exists('currency_api_get_currencies')) {
+    $options = array('field' => t('Field precision'), 'currency' => t('Currency precision'));
+    $form['decimals_display_mode'] = array(
+      '#type' => 'radios',
+      '#title' => t('Decimals display mode'),
+      '#options' => $options,
+      '#default_value' => $settings['decimals_display_mode'],
+      '#required' => TRUE,
+      '#description' => t('Choose the method to select the number of decimals used to display the field. The standard precision for each currency is displayed in the <em>Available currencies</em> list.'),
+    );
+    $currency_options = array();
+    foreach (currency_api_get_currencies() as $code => $currency) {
+      $currency_options[$code] = $currency['name'] . ' [' . $currency['decimals'] . ']';
+    }
+  }
+  else {
+    $currency_options = currency_api_get_list();
+  }
+  $form['currencies'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Available currencies'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+    '#description' => t('Choose the currencies that you want to enable for this field. Do not select any currency to enable them all.'),
+  );
+  if (function_exists('currency_api_get_currencies')) {
+    $form['currencies']['#description'] .= ' ' . t('The number between square brakets indicates the standard precision for each currency.');
+  }
+  if (isset($settings['currencies']['allowed_currencies']) && is_array($settings['currencies']['allowed_currencies'])) {
+    // Get filtered array.
+    $allowed_currencies = array_filter($settings['currencies']['allowed_currencies']);
+    // If not empty, create array for the form element values.
+    if (!empty($allowed_currencies)) {
+      $allowed_currencies = array_keys($allowed_currencies);
+      $allowed_currencies = array_combine($allowed_currencies, $allowed_currencies);
+    }
+  }
+  else {
+    $allowed_currencies = array();
+  }
+  $form['currencies']['allowed_currencies'] = array(
+    '#type' => 'checkboxes',
+    '#options' => $currency_options,
+    '#default_value' => $allowed_currencies,
+    '#checkall' => TRUE,
+    '#prefix' => '<div class="money-field-currency-checkboxes">',
+    '#suffix' => '</div>',
+    '#attached' => array(
+      'css' => array(drupal_get_path('module', 'money') . '/money.css')
+    ),
+  );
+  return $form;
 }
 
 /**
- * Get the digit group separator from a variable, use the default if the
- * variable is empty.
- *
- * @param $digit_group_separator
- *   A variable that possibly contains a digit group separator.
- * @return
- *   A digit group separator, either the variable or the default (a space).
+ * Obtain display modes for money fields.
  */
-function _money_get_digit_group_separator($digit_group_separator = NULL) {
-  return (!empty($digit_group_separator)) ? $digit_group_separator : ' ';
+function money_get_display_modes() {
+  return array(
+    's|a' => t('Symbol + Amount'),
+    's|+|a' => t('Symbol + Space + Amount'),
+    'a|s' => t('Amount + Symbol'),
+    'a|+|s' => t('Amount + Space + Symbol'),
+    's|a|+|c' => t('Symbol + Amount + Space + Currency Code'),
+    's|+|a|+|c' => t('Symbol + Space + Amount + Space + Currency Code'),
+    'a|+|c' => t('Amount + Space + Currency Code'),
+    'c|+|a' => t('Currency Code + Space + Amount'),
+    'c|+|a|s' => t('Currency Code + Space + Amount + Symbol'),
+    'c|+|a|+|s' => t('Currency Code + Space + Amount + Space + Symbol'),
+  );
 }
 
-
-//----------------------------------------------------------------------------
-// Theming functions.
-
-/**
- * @ingroup themeable
- * @{
- */
-
 /**
- * Format the list of currencies that is displayed in the money field settings
- * form.
- *
- * @param $currencies
- *   An array of currencies, where the keys are the currency codes and the
- *   values are the full names, with bracketed currency codes appended. (An
- *   array returned by currency_api_get_list()).
- * @return
- *   A rendered list of currencies.
+ * FAPI validation of an individual number element.
  */
-function theme_money_field_settings_currency_list($currencies) {
-  $output = '';
-
-  $output .= '<div id="money-field-settings-currency-list">';
-  $output .= theme_item_list(array_values($currencies), t('Available currencies'));
-  $output .= '</div>';
-
-  return $output;
+function money_field_widget_validate($element, &$form_state) {
+  $instance = field_widget_instance($element, $form_state);
+  $value = $element['amount']['#value'];
+  $decimal_separator = format_number_get_options('decimal_point');
+
+  // Reject invalid characters.
+  if (!empty($value)) {
+    $regexp = '@([^-0-9\\' . $decimal_separator . '])|(.-)@';
+    $message = t('Only numbers and the decimal separator (@separator) allowed in %field.', array('%field' => $instance['label'], '@separator' => $decimal_separator));
+    if ($value != preg_replace($regexp, '', $value)) {
+      form_error($element, $message);
+    }
+    else {
+      // Verify that only one decimal separator exists in the field.
+      if (substr_count($value, $decimal_separator) > 1) {
+        $message = t('%field: There should only be one decimal separator (@separator).',
+          array(
+            '%field' => t($instance['label']),
+            '@separator' => $decimal_separator,
+          )
+        );
+        form_error($element, $message);
+      }
+      else {
+        // Substitute the decimal separator; things should be fine.
+        $value = strtr($value, $decimal_separator, '.');
+      }
+      form_set_value($element['amount'], $value, $form_state);
+    }
+  }
 }
 
 /**
- * @} End of "ingroup themeable".
+ * Implements hook_field_widget_error().
  */
+function money_field_widget_error($element, $error, $form, &$form_state) {
+  form_error($element, $error['message']);
+}
