? location-718928-9.patch
Index: location.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/location/location.inc,v
retrieving revision 1.95.2.7
diff -u -p -r1.95.2.7 location.inc
--- location.inc	13 Jun 2010 09:27:10 -0000	1.95.2.7
+++ location.inc	15 Jun 2010 14:59:22 -0000
@@ -225,14 +225,7 @@ function _location_convert_distance_to_m
     return NULL;
   }
 
-  // Force an integer version of distance, just in case anyone wants to add a caching mechanism
-  // for postal code proximity searches.
-
-  if (is_float($distance)) {
-    $distance = intval(ceil($distance));
-  }
-
-  if ($distance < 1) {
+  if ($distance == 0) {
     return NULL;
   }
 
@@ -241,8 +234,6 @@ function _location_convert_distance_to_m
   }
 
   // Convert distance to meters
-  //$distance_float = floatval($distance) * (($distance_unit == 'km') ? 1000.0 : 1609.347);
-  //return round($distance_float, 2);
   $retval = round(floatval($distance) * (($distance_unit == 'km') ? 1000.0 : 1609.347), 2);
   return $retval;
 }
Index: location.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/location/location.install,v
retrieving revision 1.41.2.1
diff -u -p -r1.41.2.1 location.install
--- location.install	25 Feb 2009 18:49:30 -0000	1.41.2.1
+++ location.install	15 Jun 2010 14:59:23 -0000
@@ -1121,3 +1121,55 @@ function location_update_6308() {
   }
   return $ret;
 }
+
+/**
+ * There has been a change in the options for the views distance/proximity filter.
+ * The old options need to be migrated into the new.
+ */
+function location_update_6309() {
+  $ret = array();
+  $updated_views = array();
+
+  $views = views_get_all_views();
+  foreach ($views as $view) {
+    $modified = FALSE;
+    // For each view check all filters of all displays for the distance field filter.
+    foreach ($view->display as $did => $display) {
+      if (isset($display->display_options['filters']) && !empty($display->display_options['filters'])) {
+        foreach ($display->display_options['filters'] as $fid => $filter) {
+          if ($filter['table'] == 'location' && $filter['field'] == 'distance') {
+            // Set the origin (new option) from the type (old option).
+            $origin = '';
+            switch ($filter['type']) {
+              case 'latlon':
+                $origin = 'static';
+                break;
+              case 'postal':
+              case 'postal_default':
+              case 'latlon_gmap':
+                $origin = $filter['type'];
+                break;
+            }
+            if ($origin) {
+              $view->display[$did]->display_options['filters'][$fid]['origin'] = $origin;
+              unset($view->display[$did]->display_options['filters'][$fid]['type']);
+              $modified = TRUE;
+            }
+          }
+        }
+      }
+    }
+    // Save the view if we changed any options on any diplays.
+    // Views caches are cleared during save so we don't have to do it.
+    if ($modified) {
+      $updated_views[] = $view->name;
+      $view->save();
+    }
+  }
+
+  if (!empty($updated_views)) {
+    drupal_set_message(t("Note: The 'Form type' option for the 'Location: Distance / Proximity' filter in views has been changed and is now the 'Origin' option.  The following views were using that setting and have been updated to use the new setting: %views.", array('%views' => implode(', ', $updated_views))));
+  }
+
+  return $ret;
+}
Index: handlers/location_handler_field_location_distance.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/location/handlers/location_handler_field_location_distance.inc,v
retrieving revision 1.2.2.2
diff -u -p -r1.2.2.2 location_handler_field_location_distance.inc
--- handlers/location_handler_field_location_distance.inc	28 Mar 2010 22:13:53 -0000	1.2.2.2
+++ handlers/location_handler_field_location_distance.inc	15 Jun 2010 14:59:23 -0000
@@ -14,6 +14,11 @@ class location_handler_field_location_di
     $options['units'] = array('default' => 'km');
     $options['latitude'] = array('default' => '');
     $options['longitude'] = array('default' => '');
+    $options['postal_code'] = array('default' => '');
+    $options['country'] = array('default' => '');
+    $options['php_code'] = array('default' => '');
+    $options['nid_arg'] = array('default' => '');
+    $options['uid_arg'] = array('default' => '');
     return $options;
   }
 
@@ -29,30 +34,100 @@ class location_handler_field_location_di
         'km' => t('Kilometers'),
         'mi' => t('Miles'),
       ),
-      '#description' => t('FIXME'),
       '#default_value' => $this->options['units'],
     );
     $form['origin'] = array(
-      '#type' => 'radios',
+      '#type' => 'select',
       '#title' => t('Origin'),
       '#options' => array(
-        'user' => t("User's location (blank if unset)"),
-        'hybrid' => t("User's location (fall back to static if unset)"),
-        'static' => t("Static location"),
+        'user' => t("User's Latitude / Longitude (blank if unset)"),
+        'hybrid' => t("User's Latitude / Longitude (fall back to static if unset)"),
+        'static' => t('Static  Latitude / Longitude'),
         'tied' => t("Use Distance / Proximity filter"),
+        'postal' => t('Postal Code / Country'),
+        'postal_default' => t('Postal Code (assume default country)'),
+        'php' => t('Use PHP code to determine latitude/longitude'),
+        'nid_arg' => t("Node's Latitude / Longitude from views nid argument"),
+        'uid_arg' => t("User's Latitude / Longitude from views nid argument"),
       ),
-      '#description' => t('FIXME'),
+      '#description' => t("This will be the way the latitude/longitude of origin is determined. When using the user's latitude / longitude, if a user has multiple locations the first will be used."),
       '#default_value' => $this->options['origin'],
     );
+
     $form['latitude'] = array(
       '#type' => 'textfield',
       '#title' => t('Latitude'),
       '#default_value' => $this->options['latitude'],
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('edit-options-origin' => array('hybrid', 'static')),
     );
     $form['longitude'] = array(
       '#type' => 'textfield',
       '#title' => t('Longitude'),
       '#default_value' => $this->options['longitude'],
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('edit-options-origin' => array('hybrid', 'static')),
+    );
+
+    $form['postal_code'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Postal code'),
+      '#default_value' => $this->options['postal_code'],
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('edit-options-origin' => array('postal', 'postal_default')),
+    );
+    $form['country'] = array(
+      '#type' => 'select',
+      '#title' => t('Country'),
+      '#options' => array('' => '') + location_get_iso3166_list(),
+      '#default_value' => $this->options['country'],
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('edit-options-origin' => array('postal')),
+    );
+
+    $form['php_code'] = array(
+      '#type' => 'textarea',
+      '#title' => t('PHP code for latitude, longitude'),
+      '#default_value' => $this->options['php_code'],
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('edit-options-origin' => array('php')),
+      '#description' => t("Enter PHP code that returns a latitude/longitude.  Do not use &lt;?php ?&gt;. You must return only an array with float values set for the 'latitude' and 'longitude' keys."),
+    );
+
+    // Get the arguments for this view so we can use nid, uid or Global: Null arguments.
+    $uid_argument_options = array();
+    $nid_argument_options = array();
+    $arguments = $this->view->display_handler->get_handlers('argument');
+    foreach ($arguments as $id => $handler) {
+      if ($handler->field == 'null') {
+        $uid_argument_options[$id] = $handler->ui_name();
+        $nid_argument_options[$id] = $handler->ui_name();
+      }
+      else if ($handler->field == 'nid') {
+        $nid_argument_options[$id] = $handler->ui_name();
+      }
+      else if ($handler->field == 'uid') {
+        $uid_argument_options[$id] = $handler->ui_name();
+      }
+    }
+
+    $form['value']['nid_arg'] = array(
+      '#type' => 'select',
+      '#title' => t('Node ID argument to use'),
+      '#options' => $nid_argument_options,
+      '#default_value' => $this->value['nid_arg'],
+      '#description' => empty($nid_argument_options) ? t("Select which of the view's arguments to use as the node ID.  The latitude / longitude of the first location of that node will be used as the origin. Use the 'Global: Null' argument if you don't want to also restrict results to that node ID. You must have added arguments to the view to use this option.") : t("Select which of the view's arguments to use as the node ID.  The latitude / longitude of the first location of that node will be used as the origin. Use the 'Global: Null' argument if you don't want to also restrict results to that node ID."),
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('edit-options-origin' => array('nid_arg')),
+    );
+    $form['value']['uid_arg'] = array(
+      '#type' => 'select',
+      '#title' => t('User ID argument to use'),
+      '#options' => $uid_argument_options,
+      '#default_value' => $this->value['uid_arg'],
+      '#description' => empty($uid_argument_options) ? t("Select which of the view's arguments to use as the user ID.  The latitude / longitude of the first location of that user will be used as the origin. Use the 'Global: Null' argument if you don't want to also restrict results to that user ID. You must have added arguments to the view to use this option.") : t("Select which of the view's arguments to use as the user ID.  The latitude / longitude of the first location of that user will be used as the origin. Use the 'Global: Null' argument if you don't want to also restrict results to that user ID."),
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('edit-options-origin' => array('uid_arg')),
     );
   }
 
@@ -99,7 +174,7 @@ class location_handler_field_location_di
    * @return array with keys 'latitude' and 'longitude'; null if none was configured
    */
   function _get_reference_location() {
-    if ($this->options['origin'] == 'hybrid' || $this->options['origin'] == 'user') {
+    if ($this->options['origin'] == 'user' || $this->options['origin'] == 'hybrid') {
       global $user;
       if (!isset($user->locations)) {
         $user = user_load($user->uid);
@@ -109,13 +184,11 @@ class location_handler_field_location_di
         $longitude = (float)$user->locations[0]['longitude'];
       }
     }
-
-    if ($this->options['origin'] == 'static') {
+    else if ($this->options['origin'] == 'static') {
       $latitude = (float)$this->options['latitude'];
       $longitude = (float)$this->options['longitude'];
     }
-
-    if ($this->options['origin'] == 'tied') {
+    else if ($this->options['origin'] == 'tied') {
       if (!empty($this->view->filter)) {
         foreach ($this->view->filter as $k => $v) {
           if ($v->table == 'location' && $v->field == 'distance' && $v->options['relationship'] == $this->options['relationship']) {
@@ -127,6 +200,49 @@ class location_handler_field_location_di
         }
       }
     }
+    else if ($this->options['origin'] == 'postal' || $this->options['origin'] == 'postal_default') {
+      // Force default for country.
+      if ($this->options['origin'] == 'postal_default') {
+        $this->options['country'] = variable_get('location_default_country', 'us');
+      }
+
+      // Zip code lookup.
+      if (!empty($this->options['postal_code']) && !empty($this->options['country'])) {
+        $coord = location_latlon_rough($this->value);
+        if ($coord) {
+          $latitude = $coord['lat'];
+          $longitude = $coord['lon'];
+        }
+      }
+    }
+    else if ($this->options['origin'] == 'php') {
+      ob_start();
+      $result = eval($this->options['php_code']);
+      ob_end_clean();
+
+      if ($result && $result['latitude'] && $result['longitude']) {
+        $latitude = $result['latitude'];
+        $longitude = $result['longitude'];
+      }
+    }
+    else if ($this->options['origin'] == 'nid_arg') {
+      if ($nodehandler = $this->view->display_handler->get_handler('argument', $this->value['nid_arg'])) {
+        $nid = $nodehandler->get_value();
+        if ($nid && is_numeric($nid) && $tempnode = node_load($nid)) {
+          $this->value['longitude'] = $tempnode->location['longitude'];
+          $this->value['latitude'] = $tempnode->location['latitude'];
+        }
+      }
+    }
+    else if ($this->options['origin'] == 'uid_arg') {
+      if ($userhandler = $this->view->display_handler->get_handler('argument', $this->value['uid_arg'])) {
+        $uid = $userhandler->get_value();
+        if ($uid && is_numeric($uid) && $tempuser = user_load(array('uid' => $uid))) {
+          $this->value['longitude'] = $tempuser->location['longitude'];
+          $this->value['latitude'] = $tempuser->location['latitude'];
+        }
+      }
+    }
 
     if (isset($latitude) && isset($longitude)) {
       return array('latitude' => $latitude, 'longitude' => $longitude);
Index: handlers/location_handler_sort_location_distance.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/location/handlers/location_handler_sort_location_distance.inc,v
retrieving revision 1.1
diff -u -p -r1.1 location_handler_sort_location_distance.inc
--- handlers/location_handler_sort_location_distance.inc	3 Dec 2008 22:51:23 -0000	1.1
+++ handlers/location_handler_sort_location_distance.inc	15 Jun 2010 14:59:23 -0000
@@ -11,9 +11,13 @@ class location_handler_sort_location_dis
   function option_definition() {
     $options = parent::option_definition();
     $options['origin'] = array('default' => 'user');
-    $options['units'] = array('default' => 'km');
     $options['latitude'] = array('default' => '');
     $options['longitude'] = array('default' => '');
+    $options['postal_code'] = array('default' => '');
+    $options['country'] = array('default' => '');
+    $options['php_code'] = array('default' => '');
+    $options['nid_arg'] = array('default' => '');
+    $options['uid_arg'] = array('default' => '');
     return $options;
   }
 
@@ -22,37 +26,97 @@ class location_handler_sort_location_dis
   }
 
   function extra_options_form(&$form, &$form_state) {
-    $form['units'] = array(
-      '#type' => 'radios',
-      '#title' => t('Units'),
-      '#options' => array(
-        'km' => t('Kilometers'),
-        'mi' => t('Miles'),
-      ),
-      '#description' => t('FIXME'),
-      '#default_value' => $this->options['units'],
-    );
     $form['origin'] = array(
-      '#type' => 'radios',
+      '#type' => 'select',
       '#title' => t('Origin'),
       '#options' => array(
-        'user' => t("User's location (blank if unset)"),
-        'hybrid' => t("User's location (fall back to static if unset)"),
-        'static' => t("Static location"),
+        'user' => t("User's Latitude / Longitude (blank if unset)"),
+        'hybrid' => t("User's Latitude / Longitude (fall back to static if unset)"),
+        'static' => t('Static  Latitude / Longitude'),
         'tied' => t("Use Distance / Proximity filter"),
+        'postal' => t('Postal Code / Country'),
+        'postal_default' => t('Postal Code (assume default country)'),
+        'php' => t('Use PHP code to determine latitude/longitude'),
+        'nid_arg' => t("Node's Latitude / Longitude from views nid argument"),
+        'uid_arg' => t("User's Latitude / Longitude from views nid argument"),
       ),
-      '#description' => t('FIXME'),
+      '#description' => t("This will be the way the latitude/longitude of origin is determined. When using the user's latitude / longitude, if a user has multiple locations the first will be used."),
       '#default_value' => $this->options['origin'],
     );
     $form['latitude'] = array(
       '#type' => 'textfield',
       '#title' => t('Latitude'),
       '#default_value' => $this->options['latitude'],
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('edit-options-origin' => array('hybrid', 'static')),
     );
     $form['longitude'] = array(
       '#type' => 'textfield',
       '#title' => t('Longitude'),
       '#default_value' => $this->options['longitude'],
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('edit-options-origin' => array('hybrid', 'static')),
+    );
+
+    $form['postal_code'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Postal code'),
+      '#default_value' => $this->options['postal_code'],
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('edit-options-origin' => array('postal', 'postal_default')),
+    );
+    $form['country'] = array(
+      '#type' => 'select',
+      '#title' => t('Country'),
+      '#options' => array('' => '') + location_get_iso3166_list(),
+      '#default_value' => $this->options['country'],
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('edit-options-origin' => array('postal')),
+    );
+
+    $form['php_code'] = array(
+      '#type' => 'textarea',
+      '#title' => t('PHP code for latitude, longitude'),
+      '#default_value' => $this->options['php_code'],
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('edit-options-origin' => array('php')),
+      '#description' => t("Enter PHP code that returns a latitude/longitude.  Do not use &lt;?php ?&gt;. You must return only an array with float values set for the 'latitude' and 'longitude' keys."),
+    );
+
+    // Get the arguments for this view so we can use nid, uid or Global: Null arguments.
+    $uid_argument_options = array();
+    $nid_argument_options = array();
+    $arguments = $this->view->display_handler->get_handlers('argument');
+    foreach ($arguments as $id => $handler) {
+      if ($handler->field == 'null') {
+        $uid_argument_options[$id] = $handler->ui_name();
+        $nid_argument_options[$id] = $handler->ui_name();
+      }
+      else if ($handler->field == 'nid') {
+        $nid_argument_options[$id] = $handler->ui_name();
+      }
+      else if ($handler->field == 'uid') {
+        $uid_argument_options[$id] = $handler->ui_name();
+      }
+    }
+
+    $form['value']['nid_arg'] = array(
+      '#type' => 'select',
+      '#title' => t('Node ID argument to use'),
+      '#options' => $nid_argument_options,
+      '#default_value' => $this->value['nid_arg'],
+      '#description' => empty($nid_argument_options) ? t("Select which of the view's arguments to use as the node ID.  The latitude / longitude of the first location of that node will be used as the origin. Use the 'Global: Null' argument if you don't want to also restrict results to that node ID. You must have added arguments to the view to use this option.") : t("Select which of the view's arguments to use as the node ID.  The latitude / longitude of the first location of that node will be used as the origin. Use the 'Global: Null' argument if you don't want to also restrict results to that node ID."),
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('edit-options-origin' => array('nid_arg')),
+    );
+    $form['value']['uid_arg'] = array(
+      '#type' => 'select',
+      '#title' => t('User ID argument to use'),
+      '#options' => $uid_argument_options,
+      '#default_value' => $this->value['uid_arg'],
+      '#description' => empty($uid_argument_options) ? t("Select which of the view's arguments to use as the user ID.  The latitude / longitude of the first location of that user will be used as the origin. Use the 'Global: Null' argument if you don't want to also restrict results to that user ID. You must have added arguments to the view to use this option.") : t("Select which of the view's arguments to use as the user ID.  The latitude / longitude of the first location of that user will be used as the origin. Use the 'Global: Null' argument if you don't want to also restrict results to that user ID."),
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('edit-options-origin' => array('uid_arg')),
     );
   }
 
@@ -60,7 +124,7 @@ class location_handler_sort_location_dis
     $latitude = 0;
     $longitude = 0;
 
-    if ($this->options['origin'] == 'hybrid' || $this->options['origin'] == 'user') {
+    if ($this->options['origin'] == 'user' || $this->options['origin'] == 'hybrid') {
       global $user;
       if (!isset($user->locations)) {
         $user = user_load($user->uid);
@@ -70,13 +134,11 @@ class location_handler_sort_location_dis
         $longitude = (float)$user->locations[0]['longitude'];
       }
     }
-
-    if ($this->options['origin'] == 'static') {
+    else if ($this->options['origin'] == 'static') {
       $latitude = (float)$this->options['latitude'];
       $longitude = (float)$this->options['longitude'];
     }
-
-    if ($this->options['origin'] == 'tied') {
+    else if ($this->options['origin'] == 'tied') {
       if (!empty($this->view->filter)) {
         foreach ($this->view->filter as $k => $v) {
           if ($v->table == 'location' && $v->field == 'distance' && $v->options['relationship'] == $this->options['relationship']) {
@@ -88,6 +150,49 @@ class location_handler_sort_location_dis
         }
       }
     }
+    else if ($this->options['origin'] == 'postal' || $this->options['origin'] == 'postal_default') {
+      // Force default for country.
+      if ($this->options['origin'] == 'postal_default') {
+        $this->options['country'] = variable_get('location_default_country', 'us');
+      }
+
+      // Zip code lookup.
+      if (!empty($this->options['postal_code']) && !empty($this->options['country'])) {
+        $coord = location_latlon_rough($this->value);
+        if ($coord) {
+          $latitude = $coord['lat'];
+          $longitude = $coord['lon'];
+        }
+      }
+    }
+    else if ($this->options['origin'] == 'php') {
+      ob_start();
+      $result = eval($this->options['php_code']);
+      ob_end_clean();
+
+      if ($result && $result['latitude'] && $result['longitude']) {
+        $latitude = $result['latitude'];
+        $longitude = $result['longitude'];
+      }
+    }
+    else if ($this->options['origin'] == 'nid_arg') {
+      if ($nodehandler = $this->view->display_handler->get_handler('argument', $this->value['nid_arg'])) {
+        $nid = $nodehandler->get_value();
+        if ($nid && is_numeric($nid) && $tempnode = node_load($nid)) {
+          $this->value['longitude'] = $tempnode->location['longitude'];
+          $this->value['latitude'] = $tempnode->location['latitude'];
+        }
+      }
+    }
+    else if ($this->options['origin'] == 'uid_arg') {
+      if ($userhandler = $this->view->display_handler->get_handler('argument', $this->value['uid_arg'])) {
+        $uid = $userhandler->get_value();
+        if ($uid && is_numeric($uid) && $tempuser = user_load(array('uid' => $uid))) {
+          $this->value['longitude'] = $tempuser->location['longitude'];
+          $this->value['latitude'] = $tempuser->location['latitude'];
+        }
+      }
+    }
 
     $this->ensure_my_table();
 
@@ -99,7 +204,6 @@ class location_handler_sort_location_dis
     foreach ($this->view->field as $k => $v) {
       if ($v->table == 'location' && $v->field == 'distance' && $v->options['relationship'] == $this->options['relationship']) {
         if ($v->options['origin'] == $this->options['origin']
-            && $v->options['units'] == $this->options['units']
             && $v->options['latitude'] == $this->options['latitude']
             && $v->options['longitude'] == $this->options['longitude']) {
           // We have a match! Sync aliases to make it easier on the database.
Index: handlers/location_views_handler_filter_proximity.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/location/handlers/location_views_handler_filter_proximity.inc,v
retrieving revision 1.3.2.2
diff -u -p -r1.3.2.2 location_views_handler_filter_proximity.inc
--- handlers/location_views_handler_filter_proximity.inc	20 Apr 2010 00:48:16 -0000	1.3.2.2
+++ handlers/location_views_handler_filter_proximity.inc	15 Jun 2010 14:59:23 -0000
@@ -11,18 +11,21 @@ class location_views_handler_filter_prox
 
   function option_definition() {
     $options = parent::option_definition();
-    $options['type'] = array('default' => 'latlon');
-
     $options['operator'] = array('default' => 'mbr');
 
     $options['identifier'] = array('default' => 'dist');
 
+    $options['origin'] = array('default' => 'user');
+
     $options['value'] = array(
       'default' => array(
         'latitude' => '',
         'longitude' => '',
         'postal_code' => '',
         'country' => '',
+        'php_code' => '',
+        'nid_arg' => '',
+        'uid_arg' => '',
         'search_distance' => 100,
         'search_units' => 'mile',
       ),
@@ -48,24 +51,6 @@ class location_views_handler_filter_prox
 
   function expose_form_left(&$form, &$form_state) {
     parent::expose_form_left($form, $form_state);
-    $form['expose']['type'] = array(
-      '#parents' => array('options', 'type'),
-      '#type' => 'select',
-      '#title' => t('Form mode'), // @@@ Less stupid title?
-      '#options' => array(
-        'latlon' => t('Latitude / Longitude input'),
-        'postal' => t('Postal Code / Country'),
-        'postal_default' => t('Postal Code (assume default country)'),
-      ),
-      //'#id' => 'edit-options-type',
-      '#description' => t('FIXME'),
-      '#default_value' => $this->options['type'],
-    );
-
-    if (module_exists('gmap')) {
-      $form['expose']['type']['#options']['latlon_gmap'] = t('Latitude / Longitude input (use map)');
-    }
-
     // @@@ Todo: autohide this.
     $form['expose']['gmap_macro'] = array(
       '#parents' => array('options', 'gmap_macro'),
@@ -75,10 +60,34 @@ class location_views_handler_filter_prox
       '#default_value' => $this->options['gmap_macro'],
     );
 
+    $form['expose']['user_location_choose'] = array(
+      '#type' => 'checkbox',
+      '#title' => t("Allow choice of user location"),
+      '#default_value' => $this->options['expose']['user_location_choose'],
+      '#description' => t("If checked and using a user location origin, the user will be able to choose which of their locations to use.  Otherwise their first location will be used."),
+    );
   }
 
   function value_form(&$form, &$form_state) {
-    $val = $this->value;
+    $form['origin'] = array(
+      '#type' => 'select',
+      '#title' => t('Origin'),
+      '#options' => array(
+        'user' => t("User's Latitude / Longitude (blank if unset)"),
+        'hybrid' => t("User's Latitude / Longitude (fall back to static if unset)"),
+        'static' => t('Static  Latitude / Longitude'),
+        'postal' => t('Postal Code / Country'),
+        'postal_default' => t('Postal Code (assume default country)'),
+        'php' => t('Use PHP code to determine latitude/longitude'),
+        'nid_arg' => t("Node's Latitude / Longitude from views nid argument"),
+        'uid_arg' => t("User's Latitude / Longitude from views nid argument"),
+      ),
+      '#description' => t('This will be the way the latitude/longitude of origin is determined.  If this filter is exposed, this will determine the default values. NOTE: The PHP code, nid argument and uid argument options will not be available when the filter is exposed and the use of map is only available when the filter is exposed.'),
+      '#default_value' => $this->options['origin'] ? $this->options['origin'] : 'user',
+    );
+    if (module_exists('gmap')) {
+      $form['origin']['#options']['latlon_gmap'] = t('Latitude / Longitude input (use map)');
+    }
 
     // [11:44] <merlinofchaos> If you load the page from scratch, $input for your identifier will be empty.
     // [11:44] <merlinofchaos> So what's going on here is that for $_GET forms, there's no form id, no op button or anything, so they always appear to submit.
@@ -108,14 +117,16 @@ class location_views_handler_filter_prox
       '#title' => t('Latitude'),
       '#default_value' => $this->value['latitude'],
       '#process' => array('views_process_dependency'),
-      '#dependency' => array('edit-options-type' => array('latlon', 'latlon_gmap')),
+      '#dependency' => array('edit-options-origin' => array('hybrid', 'static', 'latlon_gmap')),
+      '#weight' => 1,
     );
     $form['value']['longitude'] = array(
       '#type' => 'textfield',
       '#title' => t('Longitude'),
       '#default_value' => $this->value['longitude'],
       '#process' => array('views_process_dependency'),
-      '#dependency' => array('edit-options-type' => array('latlon', 'latlon_gmap')),
+      '#dependency' => array('edit-options-origin' => array('hybrid', 'static', 'latlon_gmap')),
+      '#weight' => 2,
     );
 
     $form['value']['postal_code'] = array(
@@ -123,22 +134,73 @@ class location_views_handler_filter_prox
       '#title' => t('Postal code'),
       '#default_value' => $this->value['postal_code'],
       '#process' => array('views_process_dependency'),
-      '#dependency' => array('edit-options-type' => array('postal', 'postal_default')),
+      '#dependency' => array('edit-options-origin' => array('postal', 'postal_default')),
+      '#weight' => 3,
     );
-
     $form['value']['country'] = array(
       '#type' => 'select',
       '#title' => t('Country'),
       '#options' => array('' => '') + location_get_iso3166_list(),
       '#default_value' => $this->value['country'],
       '#process' => array('views_process_dependency'),
-      '#dependency' => array('edit-options-type' => array('postal')),
+      '#dependency' => array('edit-options-origin' => array('postal')),
+      '#weight' => 4,
+    );
+
+    $form['value']['php_code'] = array(
+      '#type' => 'textarea',
+      '#title' => t('PHP code for latitude, longitude'),
+      '#default_value' => $this->options['php_code'],
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('edit-options-origin' => array('php')),
+      '#description' => t("Enter PHP code that returns a latitude/longitude.  Do not use &lt;?php ?&gt;. You must return only an array with float values set for the 'latitude' and 'longitude' keys."),
+      '#default_value' => $this->value['php_code'],
+      '#weight' => 5,
+    );
+
+    // Get the arguments for this view so we can use nid, uid or Global: Null arguments.
+    $uid_argument_options = array();
+    $nid_argument_options = array();
+    $arguments = $this->view->display_handler->get_handlers('argument');
+    foreach ($arguments as $id => $handler) {
+      if ($handler->field == 'null') {
+        $uid_argument_options[$id] = $handler->ui_name();
+        $nid_argument_options[$id] = $handler->ui_name();
+      }
+      else if ($handler->field == 'nid') {
+        $nid_argument_options[$id] = $handler->ui_name();
+      }
+      else if ($handler->field == 'uid') {
+        $uid_argument_options[$id] = $handler->ui_name();
+      }
+    }
+
+    $form['value']['nid_arg'] = array(
+      '#type' => 'select',
+      '#title' => t('Node ID argument to use'),
+      '#options' => $nid_argument_options,
+      '#default_value' => $this->value['nid_arg'],
+      '#description' => empty($nid_argument_options) ? t("Select which of the view's arguments to use as the node ID.  The latitude / longitude of the first location of that node will be used as the origin. Use the 'Global: Null' argument if you don't want to also restrict results to that node ID. You must have added arguments to the view to use this option.") : t("Select which of the view's arguments to use as the node ID.  The latitude / longitude of the first location of that node will be used as the origin. Use the 'Global: Null' argument if you don't want to also restrict results to that node ID."),
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('edit-options-origin' => array('nid_arg')),
+      '#weight' => 6,
+    );
+    $form['value']['uid_arg'] = array(
+      '#type' => 'select',
+      '#title' => t('User ID argument to use'),
+      '#options' => $uid_argument_options,
+      '#default_value' => $this->value['uid_arg'],
+      '#description' => empty($uid_argument_options) ? t("Select which of the view's arguments to use as the user ID.  The latitude / longitude of the first location of that user will be used as the origin. Use the 'Global: Null' argument if you don't want to also restrict results to that user ID. You must have added arguments to the view to use this option.") : t("Select which of the view's arguments to use as the user ID.  The latitude / longitude of the first location of that user will be used as the origin. Use the 'Global: Null' argument if you don't want to also restrict results to that user ID."),
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('edit-options-origin' => array('uid_arg')),
+      '#weight' => 7,
     );
 
     $form['value']['search_distance'] = array(
       '#type' => 'textfield',
       '#title' => t('Distance'),
       '#default_value' => $this->value['search_distance'],
+      '#weight' => 8,
     );
 
     $form['value']['search_units'] = array(
@@ -148,48 +210,108 @@ class location_views_handler_filter_prox
         'km' => t('Kilometers'),
       ),
       '#default_value' => $this->value['search_units'],
+      '#weight' => 9,
     );
   }
 
   function exposed_form(&$form, &$form_state) {
     parent::exposed_form($form, $form_state);
     $key = $this->options['expose']['identifier'];
-    $type = $this->options['type'];
+    $origin = $this->options['origin'];
+    if ($origin == 'latlon_gmap' && module_exists('gmap')) {
+      // Bad things happen if we try to show a gmap in the views live preview.
+      if ($form_state['post']['live_preview']) {
+        $form[$key]['proximity_map'] = array(
+          '#value' => t('Gmap location selection is not available during live preview.'),
+          '#weight' => 0,
+        );
+      }
+      else {
+        $form[$key]['proximity_map'] = array(
+          '#value' => gmap_set_location($this->options['gmap_macro'], $form[$key], array('latitude' => 'latitude', 'longitude' => 'longitude')),
+          '#weight' => 0,
+        );
+      }
+    }
+    else if (($origin == 'user' || $origin == 'hybrid') && $this->options['expose']['user_location_choose']) {
+      global $user;
+      $user_locations = isset($user->locations) ? $user->locations : location_load_locations($user->uid, 'uid');
+      $location_options = array();
+      if (!empty($user_locations)) {
+        foreach ($user_locations as $i => $location) {
+          if (isset($location['latitude']) && isset($location['longitude'])) {
+            if (!empty($location['name'])) {
+              $location_options[$i] = t(check_plain($location['name']));
+            }
+            else {
+              $location_options[$i] = t('Location #@num', array('@num' => $i + 1));
+            }
+          }
+        }
+      }
 
-    if ($type == 'latlon_gmap') {
-      if (module_exists('gmap')) {
-      $form[$key]['proximity_map'] = array(
-        '#type' => 'markup',
-        '#value' => gmap_set_location($this->options['gmap_macro'], $form[$key], array('latitude' => 'latitude', 'longitude' => 'longitude')),
+      $form[$key]['user_location_delta'] = array(
+        '#type' => 'select',
+        '#title' => t('Location'),
+        '#options' => $location_options,
+        '#description' => t('Select which of your locations to use.'),
+        '#weight' => 0,
       );
-      }
     }
 
     // Remove unneeded fields when exposing the form.
     // It's shorter than redefining value_form.
-    if ($type != 'latlon' && $type != 'latlon_gmap') {
+    if ($origin != 'static' && $origin != 'latlon_gmap') {
       unset($form[$key]['latitude']);
       unset($form[$key]['longitude']);
     }
-    if ($type != 'postal' && $type != 'postal_default') {
+    if ($origin != 'postal' && $origin != 'postal_default') {
       unset($form[$key]['postal_code']);
     }
-    if ($type != 'postal') {
+    if ($origin != 'postal') {
       unset($form[$key]['country']);
     }
+
+    // And we definitely do not want to expose the php code option when exposing the filter
+    unset($form[$key]['php_code']);
+    // The nid/uid arg options are not useful on an exposed form.
+    unset($form[$key]['nid_arg']);
+    unset($form[$key]['uid_arg']);
+
+    unset($form['origin']);
   }
 
   // Used from the distance field.
   function calculate_coords() {
-    if (!empty($this->value['latitude']) && !empty($this->value['longitude'])) {
-      // If there are already coordinates, there's no work for us.
-      return TRUE;
+    if ($this->options['origin'] == 'user' || $this->options['origin'] == 'hybrid') {
+      global $user;
+      $user_locations = isset($user->locations) ? $user->locations : location_load_locations($user->uid, 'uid');
+      $i = (isset($this->value['user_location_delta']) && !empty($this->value['user_location_delta'])) ? $this->value['user_location_delta'] : 0;
+      if (isset($user_locations[$i]['latitude']) || !empty($user_locations[$i]['latitude'])) {
+        $this->value['latitude'] = (float)$user_locations[$i]['latitude'];
+        $this->value['longitude'] = (float)$user_locations[$i]['longitude'];
+      }
+      elseif ($this->options['origin'] == 'user') {
+        return FALSE;
+      }
     }
-    // @@@ Switch to mock location object and rely on location more?
-
-    if ($this->options['type'] == 'postal' || $this->options['type'] == 'postal_default') {
+    else if ($this->options['origin'] == 'php') {
+      ob_start();
+      $result = eval($this->value['php_code']);
+      ob_end_clean();
+
+      if ($result && $result['latitude'] && $result['longitude']) {
+        $this->value['latitude'] = $result['latitude'];
+        $this->value['longitude'] = $result['longitude'];
+        return TRUE;
+      }
+      else {
+        return FALSE;
+      }
+    }
+    else if ($this->options['origin'] == 'postal' || $this->options['origin'] == 'postal_default') {
       // Force default for country.
-      if ($this->options['type'] == 'postal_default') {
+      if ($this->options['origin'] == 'postal_default') {
         $this->value['country'] = variable_get('location_default_country', 'us');
       }
 
@@ -209,6 +331,24 @@ class location_views_handler_filter_prox
         return false;
       }
     }
+    else if ($this->options['origin'] == 'nid_arg') {
+      if ($nodehandler = $this->view->display_handler->get_handler('argument', $this->value['nid_arg'])) {
+        $nid = $nodehandler->get_value();
+        if ($nid && is_numeric($nid) && $tempnode = node_load($nid)) {
+          $this->value['longitude'] = $tempnode->location['longitude'];
+          $this->value['latitude'] = $tempnode->location['latitude'];
+        }
+      }
+    }
+    else if ($this->options['origin'] == 'uid_arg') {
+      if ($userhandler = $this->view->display_handler->get_handler('argument', $this->value['uid_arg'])) {
+        $uid = $userhandler->get_value();
+        if ($uid && is_numeric($uid) && $tempuser = user_load(array('uid' => $uid))) {
+          $this->value['longitude'] = $tempuser->location['longitude'];
+          $this->value['latitude'] = $tempuser->location['latitude'];
+        }
+      }
+    }
     if (empty($this->value['latitude']) || empty($this->value['longitude'])) {
       return false;
     }
@@ -237,12 +377,18 @@ class location_views_handler_filter_prox
     $lon = $this->value['longitude'];
 
     $distance_meters = _location_convert_distance_to_meters($this->value['search_distance'], $this->value['search_units']);
-
     $latrange = earth_latitude_range($lon, $lat, $distance_meters);
     $lonrange = earth_longitude_range($lon, $lat, $distance_meters);
 
-    // Add MBR check (always.)
-    $this->query->add_where($this->options['group'], "$this->table_alias.latitude > %f AND $this->table_alias.latitude < %f AND $this->table_alias.longitude > %f AND $this->table_alias.longitude < %f", $latrange[0], $latrange[1], $lonrange[0], $lonrange[1]);
+    // Add MBR check (always).
+    // In case we go past the 180/-180 mark for longitude.
+    if ($lonrange[0] > $lonrange[1]) {
+      $where = "$this->table_alias.latitude > %f AND $this->table_alias.latitude < %f AND (($this->table_alias.longitude < 180 AND $this->table_alias.longitude > %f) OR ($this->table_alias.longitude < %f AND $this->table_alias.longitude > -180))";
+    }
+    else {
+      $where = "$this->table_alias.latitude > %f AND $this->table_alias.latitude < %f AND $this->table_alias.longitude > %f AND $this->table_alias.longitude < %f";
+    }
+    $this->query->add_where($this->options['group'], $where, $latrange[0], $latrange[1], $lonrange[0], $lonrange[1]);
 
     if ($this->operator == 'dist') {
       // Add radius check.
