--- custom_breadcrumbs-6.x-1.4/custom_breadcrumbs.install	2008-09-18 20:00:12.000000000 -0500
+++ ./custom_breadcrumbs.install	2008-10-25 17:47:29.000000000 -0500
@@ -112,6 +112,12 @@ function custom_breadcrumbs_update_2() {
   return $ret;
 }
 
+// Ensure this module's weight is larger than other modules, like views, so custom breadcrumbs page callback is used.
+function custom_breadcrumbs_update_3() {
+  $ret[] = update_sql("UPDATE {system} SET weight = 12 WHERE name = 'custom_breadcrumbs'");
+  return $ret;
+}
+
 // Add the menu flag
 function custom_breadcrumbs_update_6001() {
   $ret = array();
--- custom_breadcrumbs-6.x-1.4/custom_breadcrumbs.admin.inc	2008-09-18 19:57:24.000000000 -0500
+++ ./custom_breadcrumbs.admin.inc	2008-10-25 18:29:35.000000000 -0500
@@ -1,16 +1,29 @@
 <?php
 // $Id: custom_breadcrumbs.admin.inc,v 1.1.2.2 2008/09/19 00:57:24 eaton Exp $
 
+//  Constant tag for setting off views in the node select list. Text is arbitrary
+//  and can be customized to suit
+define("CB_VIEW_TAG", t('View::'));
+
 // Lists all current custom breadcrumbs, and provides a link to the edit page.
 function custom_breadcrumbs_page() {
   $breadcrumbs = _custom_breadcrumbs_load_all_breadcrumbs(TRUE);
-
-  $header = array(t('Node type'), '');
-
+  $head = 'Node type';
+  if (module_exists('views')) {
+    $head .= ' or View page';
+  }
+  $header = array(t($head), '');
   $rows = array();
   foreach ($breadcrumbs as $breadcrumb) {
     $row = array();
-    $row[] = $breadcrumb->node_type . (!empty($breadcrumb->visibility_php) ? t(' with PHP snippet') : '');
+    if ( ($breadcrumb->node_type == 'Specify Path') || ($breadcrumb->node_type == 'Views Page') ) {
+      $title_list = explode("\n", $breadcrumb->titles);
+      $name = $title_list[0];
+    }
+    else {
+      $name = $breadcrumb->node_type;
+    }
+    $row[] = $name . (!empty($breadcrumb->visibility_php) ? ' '. t('with PHP snippet') : '');
     $row[] =  l(t('edit'), 'admin/build/custom_breadcrumbs/edit/'. $breadcrumb->bid);
     $rows[] = $row;
   }
@@ -23,12 +36,17 @@ function custom_breadcrumbs_page() {
   return theme('table', $header, $rows);
 }
 
-
 // Displays an edit form for a custom breadcrumb record.
 function custom_breadcrumbs_form() {
   $bid = arg(4);
   if (isset($bid)) {
     $breadcrumb = _custom_breadcrumbs_load_breadcrumb($bid);
+    if ($breadcrumb->node_type == 'Views Page') {
+      // extract the view path and title for form display - less confusion for user
+      $breadcrumb->node_type = drupal_substr($breadcrumb->titles, 0, strpos($breadcrumb->titles, "\n"));
+      $breadcrumb->paths = strstr($breadcrumb->paths, "\n");  // omit first row
+      $breadcrumb->titles = strstr($breadcrumb->titles, "\n"); // omit first row
+    }
     $form['bid'] = array(
       '#type' => 'hidden',
       '#value' => $bid,
@@ -37,15 +55,40 @@ function custom_breadcrumbs_form() {
 
   $options = array();
   foreach (node_get_types('names') as $type => $name) {
-    $options[$type] = $name;
+    $options[$type] = t($name);
   }
-
+  $head = 'Node type';
+  $desc = 'The node type this custom breadcrumb trail will apply to.';
+  $viewoptions = array();
+
+  if (module_exists('views')) {
+    $head .= ' '.'or Views page';
+    $views =  views_get_all_views();
+    foreach ($views as $view) {
+      if (!$view->disabled) {
+        foreach ($view->display as $display) {
+          if ($display->display_plugin == 'page') {
+            $name = CB_VIEW_TAG . $display->display_options['path'];
+            $viewoptions[$name] = t($name);
+          }
+        }
+      }
+    }
+
+    if (count($viewoptions) > 0) {
+      $desc .= "Breadcrumbs can also be set up for views  To do this, select one of the listed (". CB_VIEW_TAG .") paths.";
+      $options = array_merge($options, $viewoptions);
+    }
+  }
+  $type = 'Specify Path';
+  $options[$type] = t($type);  // add a place holder to allow matching on a path instead of node type
+  $desc .= ' '.'To set up a breadcrumb for a node accessed through a specific path, select Specify Path and enter the path below.';
   $form['node_type'] = array(
     '#type' => 'select',
-    '#title' => t('Node type'),
+    '#title' => t($head),
     '#required' => TRUE,
     '#options' => $options,
-    '#description' => t('The node type this custom breadcrumb trail will apply to.'),
+    '#description' => t($desc),
     '#default_value' => $bid ? $breadcrumb->node_type : NULL,
   );
 
@@ -61,16 +104,16 @@ function custom_breadcrumbs_form() {
     '#type' => 'textarea',
     '#title' => t('Titles'),
     '#required' => TRUE,
-    '#description' => t('A list of titles for the breadcrumb links, one on each line.'),
-    '#default_value' => $bid ? $breadcrumb->titles : NULL
+    '#description' => t('A list of titles for the breadcrumb links, one on each line. If Specify Path is selected above , the first entry will be used for breadcrumb title on the custom breadcrumbs admin page.'),
+    '#default_value' => $bid ? $breadcrumb->titles : NULL,
   );
 
   $form['paths'] = array(
     '#type' => 'textarea',
     '#title' => t('Paths'),
     '#required' => TRUE,
-    '#description' => t('A list of Drupal paths for the breadcrumb links, one on each line.'),
-    '#default_value' => $bid ? $breadcrumb->paths : NULL
+    '#description' => t('A list of Drupal paths for the breadcrumb links, one on each line. If Specify Path is selected above, the first line should be the path that the breadcrumb is to be applied to.'),
+    '#default_value' => $bid ? $breadcrumb->paths : NULL,
   );
 
   $form['help'] = array(
@@ -91,15 +134,26 @@ function custom_breadcrumbs_form() {
     $form['help']['#collapsible'] = FALSE;
     $form['help']['#collapsed'] = FALSE;
   }
-  
+
+  $form['help2'] = array(
+    '#type' => 'fieldset',
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+    '#title' => t('Special identifiers'),
+    '#description' => t("The following special identifiers can be used to achieve a special behavior. Identifiers should be added to the paths area in the following format:  identifier|path. <br />Example: ". check_plain('<pathauto>') ."|[ogname-raw]"),
+  );
+
+  $form['help2']['tokens'] = array(
+    '#value' => theme('custom_breadcrumbs_help_identifiers'),
+  );
+
   $form['set_active_menu'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Override menu path'),
-    '#description' => t("If checked, the node's default location in the menu hierarchy will be forced to match the breadcrumb trail. This does not automatically add an entry for the node to existing menus, but will cause those menus to be displayed as 'active' when the node in question is viewed."),
-    '#return_value' => TRUE,
-    '#default_value' => $bid ? $breadcrumb->set_active_menu : FALSE
+     '#type' => 'checkbox',
+     '#title' => t('Override menu path'),
+     '#description' => t("If checked, the node's default location in the menu hierarchy will be forced to match the breadcrumb trail. This does not automatically add an entry for the node to existing menus, but will cause those menus to be displayed as 'active' when the node in question is viewed."),
+     '#return_value' => TRUE,
+     '#default_value' => $bid ? $breadcrumb->set_active_menu : FALSE
   );
-  
 
   $form['buttons']['submit'] = array(
     '#type' => 'submit',
@@ -117,8 +171,8 @@ function custom_breadcrumbs_form() {
 }
 
 function custom_breadcrumbs_form_validate($form, &$form_state) {
-  $path_count = count(explode("\n", $form_state['values']['paths']));
-  $title_count = count(explode("\n", $form_state['values']['titles']));
+  $path_count = count(explode("\n", trim($form_state['values']['paths'])));
+  $title_count = count(explode("\n", trim($form_state['values']['titles'])));
   if ($title_count != $path_count) {
     form_set_error(t('Every link path must have a matching title. There are !paths paths, and !titles titles.', array('!paths' => $path_count, '!titles' => $title_count)));
   }
@@ -126,11 +180,25 @@ function custom_breadcrumbs_form_validat
 
 function custom_breadcrumbs_form_submit($form, &$form_state) {
   $breadcrumb = (object)$form_state['values'];
+  $viewtag = constant("CB_VIEW_TAG");
+  if (drupal_substr($breadcrumb->node_type, 0, drupal_strlen($viewtag)) == $viewtag) {
+    $breadcrumb->titles = $breadcrumb->node_type ."\n". $breadcrumb->titles;
+    $breadcrumb->paths = drupal_substr($breadcrumb->node_type, drupal_strlen($viewtag)) ."\n". $breadcrumb->paths;
+    $breadcrumb->node_type = 'Views Page';
+  }
+
   custom_breadcrumbs_save_breadcrumb($breadcrumb);
+  if ($breadcrumb->node_type == 'Views Page') {
+    menu_rebuild(); // must rebuild the menu router so we can replace page callback function for views
+  }
   $form_state['redirect'] = 'admin/build/custom_breadcrumbs';
 }
 
 function custom_breadcrumbs_form_delete($form, &$form_state) {
   _custom_breadcrumbs_delete_breadcrumb($form_state['values']['bid']);
+  $viewtag = CB_VIEW_TAG;
+  if (drupal_substr($form_state['values']['node_type'], 0, drupal_strlen($viewtag)) == $viewtag) {
+    menu_rebuild();  // must rebuild the menu router to remove custom breadcrumbs callback
+  }
   $form_state['redirect'] = 'admin/build/custom_breadcrumbs';
 }
--- custom_breadcrumbs-6.x-1.4/custom_breadcrumbs.module	2008-09-21 21:25:43.000000000 -0500
+++ ./custom_breadcrumbs.module	2008-10-25 18:13:32.000000000 -0500
@@ -6,10 +6,13 @@
  */
 function custom_breadcrumbs_menu() {
   $items = array();
-
+  $desc = 'Add custom breadcrumb trails for content types';
+  if (module_exists('views')) {
+    $desc .= ' and views';
+  }
   $items['admin/build/custom_breadcrumbs'] = array(
     'title' => 'Custom breadcrumbs',
-    'description' => 'Add custom breadcrumb trails for content types.',
+    'description' => $desc,
     'page callback' => 'custom_breadcrumbs_page',
     'access callback' => 'user_access',
     'access arguments' => array('administer custom breadcrumbs'),
@@ -39,42 +42,132 @@ function custom_breadcrumbs_menu() {
   return $items;
 }
 
+/**
+ * Implementation of hook_menu_alter().
+ * Add breadcrumbs based on paths of views breadcrumbs
+ *  @param $callbacks
+ *    Associative array of menu router definitions.
+ */
+
+function custom_breadcrumbs_menu_alter(&$callbacks) {
+  // Search through all the custom breadcrumb views paths and menu callback paths for a match
+  // if a match exists, check to see if its already set to our new callback function
+  // if not, change the callback function to our new one
+  // push the original callback function name onto the page arguments array
+  // push the breadcrumb id onto the page arguments array
+  // these will be popped off for use by _custom_breadcrumb_menu_redirect()
+
+  $pattern = '/%\w+/';  // match any wildcard starting with %
+  $candidates = array();
+  foreach ($callbacks as $path => $callback) {
+    if ($callback['page callback'] == 'views_page') {
+      $newpath = preg_replace($pattern, '%', $path);   // replace with a simple %
+      $candidates[$path] = $newpath;
+    }
+  }
+  $list = _custom_breadcrumbs_get_first_path('Views Page');
+  foreach ($list as $item) {
+    $fixed = preg_replace($pattern, '%', $item['path']);
+    foreach ($candidates as $path => $candidate) {
+      if (trim($fixed) == trim($candidate)) {
+        if (isset($callbacks[$path])) {
+          $oldcb = $callbacks[$path]['page callback'];
+          $newcb = '_custom_breadcrumb_menu_redirect';
+          if ($oldcb != $newcb) {
+            $callbacks[$path]['page callback'] = $newcb;
+            $callbacks[$path]['page arguments'][] = $oldcb;
+            $callbacks[$path]['page arguments'][] = $item['bid'];
+          }
+        }
+        unset($candidates[$path]);  // no longer a candidate
+        break;
+      }
+    }
+  }
+}
+
+/*
+ *  Private callback function. It sets the custom breadcrumb and
+ *  then calls the original page callback function with the original page arguments.
+ */
+
+function _custom_breadcrumb_menu_redirect() {
+  for ($i = 0; $i < func_num_args(); ++$i) {
+    $params[] = func_get_arg($i);
+  }
+  $bid = array_pop($params);   // the last param is the id...need this to retrieve breadcrumb
+  $original_callback = array_pop($params);
+  if (function_exists($original_callback)) {
+    $output = call_user_func_array($original_callback, $params);
+    $breadcrumb = _custom_breadcrumbs_load_breadcrumb($bid);
+    _custom_breadcrumbs_set_breadcrumb($breadcrumb, 1);   // breadcrumb information is offset by one row
+    return $output;
+  }
+}
+
 function custom_breadcrumbs_perm() {
   return array('administer custom breadcrumbs', 'use php in custom breadcrumbs');
 }
 
 function custom_breadcrumbs_nodeapi($node, $op, $teaser, $page) {
   if ($op == 'alter' && !$teaser && $page) {
-    if ($breadcrumb = _custom_breadcrumbs_load_for_type($node)) {
-      $titles = preg_split("/[\n]+/", $breadcrumb->titles);
-      $paths = preg_split("/[\n]+/", $breadcrumb->paths);
-
-      $titles = module_exists('token') ? token_replace($titles, 'node', $node) : $titles;
-      $paths = module_exists('token') ? token_replace($paths, 'node', $node) : $paths;
-
-      $trail = array(l(t('Home'), '<front>'));
-      $location = array();
-      for ($i = 0; $i < count($titles); $i++) {
-        // Skip empty titles
-        if ($title = trim($titles[$i])) {
-          // Output plaintext instead of a link if there is a title without a path.
-          $path = trim($paths[$i]);
-          if (strlen($path) > 0 && $path != '<none>') {
-            $trail[] = l($title, trim($paths[$i]));
-            $location[$i] = menu_get_item(drupal_get_normal_path($paths[$i]));          }
-          else {
-            $trail[] = check_plain($title);
-          }
-        }
+    $curpath = $_REQUEST['q'];
+    $breadcrumb = _custom_breadcrumbs_load('Specify Path', $curpath); // check for breadcrumb for this path
+    if ($breadcrumb) {
+      $offset = 1;
+    }
+    else {
+      $breadcrumb = _custom_breadcrumbs_load($node->type); // check for breadcrumb for this node type
+      $offset = 0;
+    }
+    if ($breadcrumb) {
+      _custom_breadcrumbs_set_breadcrumb($breadcrumb, $offset, $node);
+    }
+  }
+}
+
+/**
+ * Private function to set custom breadcrumb
+ *
+ * @param $breadcrumb
+ *   The $beadcrumb object
+ * @param $offset
+ *   Set to 1 to skip the custom breadcrumb path information
+ *   when constructing the breadcrumb.
+ * @param $node
+ *   The node object (if available) for building token substituions
+ *
+ */
+
+function _custom_breadcrumbs_set_breadcrumb($breadcrumb, $offset, $node = NULL) {
+  if ($breadcrumb) {
+    global $user;  // for user tokens
+    $titles = preg_split("/[\n]+/", $breadcrumb->titles);
+    $paths = preg_split("/[\n]+/", $breadcrumb->paths);
+    if (module_exists('token')) {  // Do token replacement
+      $types = array();
+      if ($node) {
+        $types['node'] = $node;
       }
-      drupal_set_breadcrumb($trail);
-      if ($breadcrumb->set_active_menu) {
-        $location[] = menu_get_item();
-        menu_set_active_trail($location);
+      $types['user'] = $user;
+      $types['global'] = NULL;
+      $titles = token_replace_multiple($titles, $types);
+      $paths = token_replace_multiple($paths, $types);
+    }
+    $trail = array(l(t('Home'), '<front>'));
+    $location = array();
+    for ($i = $offset; $i < count($titles); $i++) {
+      if ($title = trim($titles[$i])) { // create breadcrumb
+        $trail[] = _custom_breadcrumbs_create_crumb($title, trim($paths[$i]));
+        $location[$i] = menu_get_item(drupal_get_normal_path($paths[$i]));
       }
     }
+    drupal_set_breadcrumb($trail);
+    if ($breadcrumb->set_active_menu) {
+      $location[] = menu_get_item();
+      menu_set_active_trail($location);
+    }
   }
-
 }
 
 function _custom_breadcrumbs_load_breadcrumb($bid) {
@@ -84,25 +177,57 @@ function _custom_breadcrumbs_load_breadc
   return $breadcrumb;
 }
 
-function _custom_breadcrumbs_load_for_type($node) {
+/**
+ * Helper function to obtain bid and path for breadcrumbs
+ *
+ */
+function _custom_breadcrumbs_get_first_path($node_type) {
   $sql = "SELECT * FROM {custom_breadcrumb} WHERE node_type = '%s'";
-  $result = db_query($sql, $node->type);
+  $result = db_query($sql, $node_type);
+  $list = array();
   while ($breadcrumb = db_fetch_object($result)) {
-    if (!empty($breadcrumb->visibility_php)) {
-      // Use PHP code to check the visibility.
-      ob_start();
-      $result = eval(trim($breadcrumb->visibility_php));
-      ob_end_clean();
-      if ($result == TRUE) {
+    $list[] = array( 'bid' => $breadcrumb->bid, 'path' => trim(drupal_substr($breadcrumb->paths, 0, strpos($breadcrumb->paths, "\n"))));
+  }
+  return $list;
+}
+
+/**
+ * Private function to load custom breadcrumb
+ *
+ * @param $type
+ *   The type of the breadcrumb to load ($node->type, 'Views Page' or 'Specify Path')
+ * @param $curpath
+ *   The path to check for custom breadcrumb
+ */
+function _custom_breadcrumbs_load($type, $curpath = NULL) {
+  $sql = "SELECT * FROM {custom_breadcrumb} WHERE node_type = '%s'";
+  $result = db_query($sql, $type);
+  while ($breadcrumb = db_fetch_object($result)) {
+    $path = drupal_substr($breadcrumb->paths, 0, strpos($breadcrumb->paths, "\n"));
+    if ((($curpath == NULL) && ($type != 'Specify Path')) || (trim($curpath) == trim($path))) {
+      if (!empty($breadcrumb->visibility_php)) {
+        // Use PHP code to check the visibility.
+        ob_start();
+        $visibility = eval(trim($breadcrumb->visibility_php));
+        ob_end_clean();
+        if ($visibility == TRUE) {
+          return $breadcrumb;
+        }
+      }
+      else {
         return $breadcrumb;
       }
     }
-    else {
-      return $breadcrumb;
-    }
   }
 }
 
+/*
+ * Private function to load all breadcrumbs from database.
+ * Current breadcrumbs are held as static variable
+ *
+ * @param $refresh
+ *   If set to TRUE, reload breadcrumbs from database.
+ */
 function _custom_breadcrumbs_load_all_breadcrumbs($refresh = FALSE) {
   static $breadcrumbs;
   if ($refresh || !isset($breadcrumbs)) {
@@ -117,6 +242,12 @@ function _custom_breadcrumbs_load_all_br
   return $breadcrumbs;
 }
 
+/*
+ * Private function to save breadcrumb to database.
+ *
+ * @param $breadcrumb
+ *   The breadcrumb to save.
+ */
 function custom_breadcrumbs_save_breadcrumb($breadcrumb = NULL) {
   if (is_array($breadcrumb->paths)) {
     $breadcrumb->paths = implode("\n", $breadcrumb->paths);
@@ -124,7 +255,6 @@ function custom_breadcrumbs_save_breadcr
   if (is_array($breadcrumb->titles)) {
     $breadcrumb->titles = implode("\n", $breadcrumb->titles);
   }
-
   if (isset($breadcrumb->bid)) {
     drupal_write_record('custom_breadcrumb', $breadcrumb, 'bid');
   }
@@ -133,7 +263,72 @@ function custom_breadcrumbs_save_breadcr
   }
 }
 
+/*
+ * Private function to delete breadcrumb from database.
+ *
+ * @param $bid
+ *   The unique breadcrumb id to be deleted.
+ */
 function _custom_breadcrumbs_delete_breadcrumb($bid) {
   $sql = 'DELETE FROM {custom_breadcrumb} WHERE bid = %d';
   db_query($sql, $bid);
 }
+
+/**
+ * Private function for custom breadcrumb to create a crumb item
+ *
+ * @param $title
+ *   The human readable title to be rendered by the browser
+ * @param $original_path
+ *   The desired URI and/or special identifier
+ */
+function _custom_breadcrumbs_create_crumb($title, $original_path) {
+  list($identifier, $path) = explode("|", $original_path, 2);
+  // decode title to properly handle special characters
+  // Suggested by aangle http://drupal.org/node/318272
+  $title = decode_entities($title);
+  if ($path) {
+    switch ($identifier) {
+      case '<pathauto>':
+        if (module_exists('pathauto')) {
+          $crumb = l(t($title), pathauto_cleanstring($path, FALSE));
+        }
+        break;
+      default:
+        $crumb = l(t($title), $original_path);
+    }
+  }
+  else { // This may be just be a single identifier
+    switch ($identifier) {
+      case '<none>':
+        $crumb = check_plain(t($title));
+        break;
+      default:
+        $crumb = l(t($title), $original_path);
+    }
+  }
+  return $crumb;
+}
+
+/**
+ * Builds a table of identifiers and their behaviors
+ */
+function theme_custom_breadcrumbs_help_identifiers()  {
+  $headers = array(t('Identifier'), t('Behaviour'));
+  $rows = array();
+
+  // <none> identifier
+  $row = array();
+  $row[] = check_plain('<none>');
+  $row[] = t('This will result in a plain text crumb. This identifier should not be used with the pipe (|) symbol.');
+  $rows[] = $row;
+
+  // <pathauto> identifier
+  if (module_exists('pathauto')) {
+    $row = array();
+    $row[] = check_plain('<pathauto>');
+    $row[] = t('Cleans the given path using your pathauto replacement rules.');
+    $rows[] = $row;
+  }
+return theme('table', $headers, $rows, array('class' => 'description'));
+}
