[ Index ]

PHP Cross Reference of Drupal 6 (gatewave)

title

Body

[close]

/sites/all/modules/panels/plugins/layouts/flexible/ -> flexible.inc (source)

   1  <?php
   2  // $Id: flexible.inc,v 1.1.2.36 2010/10/22 18:20:50 merlinofchaos Exp $
   3  
   4  /**
   5   * Implementation of hook_panels_layouts()
   6   */
   7  // Plugin definition
   8  $plugin = array(
   9    'title' => t('Flexible'),
  10    'category' => t('Builders'),
  11    'icon' => 'flexible.png',
  12    'theme' => 'panels_flexible',
  13    'admin theme' => 'panels_flexible_admin',
  14    'css' => 'flexible.css',
  15    'admin css' => 'flexible-admin.css',
  16    'settings form' => 'panels_flexible_settings_form',
  17    'settings submit' => 'panels_flexible_settings_submit',
  18    'settings validate' => 'panels_flexible_settings_validate',
  19    'panels function' => 'panels_flexible_panels',
  20    'hook menu' => 'panels_flexible_menu',
  21  
  22    // Reuisable layout Builder specific directives
  23    'builder' => TRUE,
  24    'builder tab title' => 'Add flexible layout', // menu so translated elsewhere
  25  
  26    'get child' => 'panels_flexible_get_sublayout',
  27    'get children' => 'panels_flexible_get_sublayouts',
  28  
  29    // Define ajax callbacks
  30    'ajax' => array(
  31      'settings' => 'panels_ajax_flexible_edit_settings',
  32      'add' => 'panels_ajax_flexible_edit_add',
  33      'remove' => 'panels_ajax_flexible_edit_remove',
  34      'resize' => 'panels_ajax_flexible_edit_resize',
  35      'reuse' => 'panels_ajax_flexible_edit_reuse',
  36    ),
  37  );
  38  
  39  /**
  40   * Merge the main flexible plugin with a layout to create a sub plugin.
  41   *
  42   * This is used for both panels_flexible_get_sublayout and
  43   * panels_flexible_get_sublayouts.
  44   */
  45  function panels_flexible_merge_plugin($plugin, $layout) {
  46    $plugin['name'] = 'flexible:' . $layout->name;
  47    $plugin['category'] = !empty($layout->category) ? check_plain($layout->category) : t('Miscellaneous');
  48    $plugin['title'] = check_plain($layout->admin_title);
  49    $plugin['description'] = check_plain($layout->admin_description);
  50    $plugin['layout'] = $layout;
  51    $plugin['builder'] = FALSE;
  52    $plugin['builder tab title'] = NULL;
  53    return $plugin;
  54  }
  55  
  56  /**
  57   * Callback to provide a single stored flexible layout.
  58   */
  59  function panels_flexible_get_sublayout($plugin, $layout_name, $sublayout_name) {
  60    // Do not worry about caching; Panels is handling that for us.
  61    ctools_include('export');
  62    $item = ctools_export_crud_load('panels_layout', $sublayout_name);
  63    if ($item) {
  64      return panels_flexible_merge_plugin($plugin, $item);
  65    }
  66  }
  67  
  68  /**
  69   * Callback to provide all stored flexible layouts.
  70   */
  71  function panels_flexible_get_sublayouts($plugin, $layout_name) {
  72    $layouts[$layout_name] = $plugin;
  73    ctools_include('export');
  74    $items = ctools_export_load_object('panels_layout', 'conditions', array('plugin' => 'flexible'));
  75    foreach ($items as $name => $item) {
  76      $layouts['flexible:' . $name] = panels_flexible_merge_plugin($plugin, $item);
  77    }
  78  
  79    return $layouts;
  80  }
  81  
  82  /**
  83   * Convert settings from old style to new, or provide defaults for
  84   * empty settings.
  85   * @param <type> $settings
  86   */
  87  function panels_flexible_convert_settings(&$settings, &$layout) {
  88    // This indicates that this is a layout that they used the checkbox
  89    // on. The layout is still 'flexible' but it's actually pointing
  90    // to another stored one and we have to load it.
  91    if (!empty($settings['layout'])) {
  92      $layout = panels_get_layout('flexible:' . $settings['layout']);
  93    }
  94  
  95    if (!empty($layout['layout'])) {
  96      $settings = $layout['layout']->settings;
  97      return $settings;
  98    }
  99  
 100    if (empty($settings)) {
 101      // set up a default
 102      $settings = array(
 103        'items' => array(
 104          // The 'canvas' is a special row that does not get rendered
 105          // normally, but is used to contain the columns.
 106          'canvas' => array(
 107            'type' => 'row',
 108            'contains' => 'column',
 109            'children' => array('main'),
 110            'parent' => NULL,
 111          ),
 112          'main' => array(
 113            'type' => 'column',
 114            'width' => 100,
 115            'width_type' => '%',
 116            'children' => array('main-row'),
 117            'parent' => 'canvas',
 118          ),
 119          'main-row' => array(
 120            'type' => 'row',
 121            'contains' => 'region',
 122            'children' => array('center'),
 123            'parent' => 'main',
 124          ),
 125          'center' => array(
 126            'type' => 'region',
 127            'title' => t('Center'),
 128            'width' => 100,
 129            'width_type' => '%',
 130            'parent' => 'main-row',
 131          ),
 132        ),
 133      );
 134    }
 135    else if (!isset($settings['items'])) {
 136      // Convert an old style flexible to a new style flexible.
 137      $old = $settings;
 138      $settings = array();
 139      $settings['items']['canvas'] = array(
 140        'type' => 'row',
 141        'contains' => 'column',
 142        'children' => array(),
 143        'parent' => NULL,
 144      );
 145      // add the left sidebar column, row and region if it exists.
 146      if (!empty($old['sidebars']['left'])) {
 147        $settings['items']['canvas']['children'][] = 'sidebar-left';
 148        $settings['items']['sidebar-left'] = array(
 149          'type' => 'column',
 150          'width' => $old['sidebars']['left_width'],
 151          'width_type' => $old['sidebars']['width_type'],
 152          'children' => array('sidebar-left-row'),
 153          'parent' => 'canvas',
 154        );
 155        $settings['items']['sidebar-left-row'] = array(
 156          'type' => 'row',
 157          'contains' => 'region',
 158          'children' => array('sidebar_left'),
 159          'parent' => 'sidebar-left',
 160        );
 161        $settings['items']['sidebar_left'] = array(
 162          'type' => 'region',
 163          'title' => t('Left sidebar'),
 164          'width' => 100,
 165          'width_type' => '%',
 166          'parent' => 'sidebar-left-row',
 167        );
 168      }
 169  
 170      $settings['items']['canvas']['children'][] = 'main';
 171  
 172      if (!empty($old['sidebars']['right'])) {
 173        $settings['items']['canvas']['children'][] = 'sidebar-right';
 174        $settings['items']['sidebar-right'] = array(
 175          'type' => 'column',
 176          'width' => $old['sidebars']['right_width'],
 177          'width_type' => $old['sidebars']['width_type'],
 178          'children' => array('sidebar-right-row'),
 179          'parent' => 'canvas',
 180        );
 181        $settings['items']['sidebar-right-row'] = array(
 182          'type' => 'row',
 183          'contains' => 'region',
 184          'children' => array('sidebar_right'),
 185          'parent' => 'sidebar-right',
 186        );
 187        $settings['items']['sidebar_right'] = array(
 188          'type' => 'region',
 189          'title' => t('Right sidebar'),
 190          'width' => 100,
 191          'width_type' => '%',
 192          'parent' => 'sidebar-right-row',
 193        );
 194      }
 195  
 196      // Add the main column.
 197      $settings['items']['main'] = array(
 198        'type' => 'column',
 199        'width' => 100,
 200        'width_type' => '%',
 201        'children' => array(),
 202        'parent' => 'canvas',
 203      );
 204  
 205      // Add rows and regions.
 206      for ($row = 1; $row <= intval($old['rows']); $row++) {
 207        // Create entry for the row
 208        $settings['items']["row_$row"] = array(
 209          'type' => 'row',
 210          'contains' => 'region',
 211          'children' => array(),
 212          'parent' => 'main',
 213        );
 214        // Add the row to the parent's children
 215        $settings['items']['main']['children'][] = "row_$row";
 216  
 217        for ($col = 1; $col <= intval($old["row_$row"]['columns']); $col++) {
 218          // Create entry for the region
 219          $settings['items']["row_$row}_$col"] = array(
 220            'type' => 'region',
 221            'width' => $old["row_$row"]["width_$col"],
 222            'width_type' => '%',
 223            'parent' => "row_$row",
 224          );
 225          // Add entry for the region to the row's children
 226          $settings['items']["row_$row"]['children'][] = "row_$row}_$col";
 227  
 228          // Apply the proper title to the region
 229          if (!empty($old["row_$row"]['names'][$col - 1])) {
 230            $settings['items']["row_$row}_$col"]['title'] = $old["row_$row"]['names'][$col - 1];
 231          }
 232          else {
 233            $settings['items']["row_$row}_$col"]['title'] = t("Row @row, Column @col", array('@row' => $row, '@col' => $col));
 234          }
 235        }
 236      }
 237    }
 238    else if (isset($settings['canvas'])) {
 239      // Convert the old 'canvas' to the new canvas row.
 240      $settings['items']['canvas'] = array(
 241        'type' => 'row',
 242        'contains' => 'column',
 243        'children' => $settings['canvas'],
 244        'parent' => NULL,
 245      );
 246      unset($settings['canvas']);
 247    }
 248  }
 249  
 250  /**
 251   * Define the actual list of columns and rows for this flexible panel.
 252   */
 253  function panels_flexible_panels($display, $settings, $layout) {
 254    $items = array();
 255    panels_flexible_convert_settings($settings, $layout);
 256    foreach ($settings['items'] as $id => $item) {
 257      if ($item['type'] == 'region') {
 258        $items[$id] = $item['title'];
 259      }
 260    }
 261  
 262    return $items;
 263  }
 264  
 265  /**
 266   * Create a renderer object.
 267   *
 268   * The renderer object contains data that is passed around from function
 269   * to function allowing us to render our CSS and HTML easily.
 270   *
 271   * @todo Convert the functions to methods and make this properly OO.
 272   */
 273  function panels_flexible_create_renderer($admin, $id, $content, $settings, &$display, $layout, $handler) {
 274    $renderer = new stdClass;
 275    $renderer->settings = $settings;
 276    $renderer->content = $content;
 277    $renderer->css_id = $id;
 278    $renderer->did = &$display->did;
 279    if ($admin) {
 280      // always scale in admin mode.
 281      $renderer->scale_base = 99.0;
 282    }
 283    else {
 284      $renderer->scale_base = !empty($settings['items']['canvas']['no_scale']) ? 100.0 : 99.0;
 285    }
 286    $renderer->id_str = $id ? 'id="' . $id . '"' : '';
 287    $renderer->admin = $admin;
 288    $renderer->handler = $handler;
 289  
 290    // Set up basic classes for all of our components.
 291    $renderer->name                 = !empty($layout['layout']) ? $layout['layout']->name : $display->did;
 292    $renderer->base_class           = $renderer->name;
 293    $renderer->item_class['column'] = 'panels-flexible-column';
 294    $renderer->item_class['row']    = 'panels-flexible-row';
 295    $renderer->item_class['region'] = 'panels-flexible-region';
 296    $renderer->base['canvas']       = 'panels-flexible-' . $renderer->base_class;
 297  
 298    // Override these if selected from the UI and not in admin mode.
 299    if (!$admin) {
 300      if (!empty($settings['items']['canvas']['class'])) {
 301        $renderer->base_class = $settings['items']['canvas']['class'];
 302        $renderer->base['canvas'] = $renderer->base_class;
 303      }
 304      if (!empty($settings['items']['canvas']['column_class'])) {
 305        $renderer->item_class['column'] = $settings['items']['canvas']['column_class'];
 306      }
 307      if (!empty($settings['items']['canvas']['row_class'])) {
 308        $renderer->item_class['row'] = $settings['items']['canvas']['row_class'];
 309      }
 310      if (!empty($settings['items']['canvas']['region_class'])) {
 311        $renderer->item_class['region'] = $settings['items']['canvas']['region_class'];
 312      }
 313    }
 314  
 315    // Get the separation values out of the canvas settings.
 316    $renderer->column_separation = !empty($settings['items']['canvas']['column_separation']) ? $settings['items']['canvas']['column_separation'] : '0.5em';
 317  
 318    $renderer->region_separation = !empty($settings['items']['canvas']['region_separation']) ? $settings['items']['canvas']['region_separation'] : '0.5em';
 319  
 320    $renderer->row_separation = !empty($settings['items']['canvas']['row_separation']) ? $settings['items']['canvas']['row_separation'] : '0.5em';
 321  
 322    // Make some appended classes so it's easier to reference them.
 323  
 324    $renderer->base['column'] = $renderer->item_class['column'] . '-' . $renderer->base_class;
 325    $renderer->base['row']    = $renderer->item_class['row'] . '-' . $renderer->base_class;
 326    $renderer->base['region'] = $renderer->item_class['region'] . '-' . $renderer->base_class;
 327  
 328    if ($renderer->name != 'new') {
 329      // Use v2 to guarantee all CSS gets regenerated to account for changes in
 330      // how some divs will be rendered.
 331      $renderer->css_cache_name = 'flexiblev2:' . $renderer->name;
 332      if ($admin) {
 333        ctools_include('css');
 334        ctools_css_clear($renderer->css_cache_name);
 335      }
 336    }
 337    return $renderer;
 338  }
 339  
 340  /**
 341   * Draw the flexible layout.
 342   */
 343  function theme_panels_flexible($id, $content, $settings, $display, $layout, $handler) {
 344    panels_flexible_convert_settings($settings, $layout);
 345  
 346    $renderer = panels_flexible_create_renderer(FALSE, $id, $content, $settings, $display, $layout, $handler);
 347  
 348    // CSS must be generated because it reports back left/middle/right
 349    // positions.
 350    $css = panels_flexible_render_css($renderer);
 351  
 352    if (!empty($renderer->css_cache_name) && empty($display->editing_layout)) {
 353      ctools_include('css');
 354      // Generate an id based upon rows + columns:
 355      $filename = ctools_css_retrieve($renderer->css_cache_name);
 356      if (!$filename) {
 357        $filename = ctools_css_store($renderer->css_cache_name, $css, FALSE);
 358      }
 359  
 360      // Give the CSS to the renderer to put where it wants.
 361      if ($handler) {
 362        $handler->add_css($filename, 'module', 'all', FALSE);
 363      }
 364      else {
 365        ctools_css_add_css($filename, 'module', 'all', FALSE);
 366      }
 367    }
 368    else {
 369      // If the id is 'new' we can't reliably cache the CSS in the filesystem
 370      // because the display does not truly exist, so we'll stick it in the
 371      // head tag. We also do this if we've been told we're in the layout
 372      // editor so that it always gets fresh CSS.
 373      drupal_set_html_head("<style type=\"text/css\">\n$css</style>\n");
 374    }
 375  
 376    // Also store the CSS on the display in case the live preview or something
 377    // needs it
 378    $display->add_css = $css;
 379  
 380    $output = "<div class=\"panel-flexible " . $renderer->base['canvas'] . " clear-block\" $renderer->id_str>\n";
 381    $output .= "<div class=\"panel-flexible-inside " . $renderer->base['canvas'] . "-inside\">\n";
 382  
 383    $output .= panels_flexible_render_items($renderer, $settings['items']['canvas']['children'], $renderer->base['canvas']);
 384  
 385    // Wrap the whole thing up nice and snug
 386    $output .= "</div>\n</div>\n";
 387  
 388    return $output;
 389  }
 390  
 391  /**
 392   * Draw the flexible layout.
 393   */
 394  function theme_panels_flexible_admin($id, $content, $settings, $display, $layout, $handler) {
 395    // We never draw stored flexible layouts in admin mode; they must be edited
 396    // from the stored layout UI at that point.
 397    if (!empty($layout['layout'])) {
 398      return theme_panels_flexible($id, $content, $settings, $display, $layout, $handler);
 399    }
 400  
 401    panels_flexible_convert_settings($settings, $layout);
 402    $renderer = panels_flexible_create_renderer(TRUE, $id, $content, $settings, $display, $layout, $handler);
 403  
 404    $css = panels_flexible_render_css($renderer);
 405  
 406    // For the administrative view, add CSS directly to head.
 407    drupal_set_html_head("<style type=\"text/css\">\n$css</style>\n");
 408  
 409    if (empty($display->editing_layout)) {
 410      $output = '<input type="submit" id="panels-flexible-toggle-layout" value ="' .
 411        t('Show layout designer') . '">';
 412      if (user_access('administer panels layouts')) {
 413        $output .= '<input type="hidden" class="panels-flexible-reuse-layout-url" value="' . url($handler->get_url('layout', 'reuse'), array('absolute' => TRUE)) . '">';
 414        $output .= '<input type="submit" id="panels-flexible-reuse-layout" class="ctools-use-modal" value ="' .
 415          t('Reuse layout') . '">';
 416      }
 417      $output .= "<div class=\"panel-flexible " . $renderer->base['canvas'] . " clear-block panel-flexible-admin panel-flexible-no-edit-layout\" $renderer->id_str>\n";
 418    }
 419    else {
 420      $output = "<div class=\"panel-flexible " . $renderer->base['canvas'] . " clear-block panel-flexible-admin panel-flexible-edit-layout\" $renderer->id_str>\n";
 421    }
 422    $output .= "<div class=\"panel-flexible-inside " . $renderer->base['canvas'] . "-inside \">\n";
 423  
 424    $content = panels_flexible_render_items($renderer, $settings['items']['canvas']['children'], $renderer->base['row'] . '-canvas');
 425    $output .= panels_flexible_render_item($renderer, $settings['items']['canvas'], $content, 'canvas', 0, 0, TRUE);
 426  
 427    // Wrap the whole thing up nice and snug
 428    $output .= "</div>\n</div>\n";
 429  
 430    drupal_add_js($layout['path'] . '/flexible-admin.js');
 431    drupal_add_js(array('flexible' => array('resize' => url($handler->get_url('layout', 'resize'), array('absolute' => TRUE)))), 'setting');
 432    return $output;
 433  }
 434  
 435  /**
 436   * Render a piece of a flexible layout.
 437   */
 438  function panels_flexible_render_items($renderer, $list, $owner_id) {
 439    $output = '';
 440    $groups = array('left' => '', 'middle' => '', 'right' => '');
 441    $max = count($list) - 1;
 442    $prev = NULL;
 443  
 444    foreach ($list as $position => $id) {
 445      $item = $renderer->settings['items'][$id];
 446      $location = isset($renderer->positions[$id]) ? $renderer->positions[$id] : 'middle';
 447  
 448      if ($renderer->admin && $item['type'] != 'row' && $prev ) {
 449        $groups[$location] .= panels_flexible_render_splitter($renderer, $prev, $id);
 450      }
 451  
 452      switch ($item['type']) {
 453        case 'column':
 454          $content = panels_flexible_render_items($renderer, $item['children'], $renderer->base['column'] . '-' . $id);
 455          $groups[$location] .= panels_flexible_render_item($renderer, $item, $content, $id, $position, $max);
 456          break;
 457        case 'row':
 458          $content = panels_flexible_render_items($renderer, $item['children'], $renderer->base['row'] . '-' . $id);
 459          $groups[$location] .= panels_flexible_render_item($renderer, $item, $content, $id, $position, $max, TRUE);
 460          break;
 461        case 'region':
 462          $content = isset($renderer->content[$id]) ? $renderer->content[$id] : "&nbsp;";
 463          $groups[$location] .= panels_flexible_render_item($renderer, $item, $content, $id, $position, $max);
 464          break;
 465      }
 466  
 467      // If all items are fixed then we have a special splitter on the right to
 468      // control the overall width.
 469      if (!empty($renderer->admin) && $max == $position && $location == 'left') {
 470        $groups[$location] .= panels_flexible_render_splitter($renderer, $id, NULL);
 471      }
 472      $prev = $id;
 473    }
 474  
 475    $group_count = count(array_filter($groups));
 476  
 477    // Render each group. We only render the group div if we're in admin mode
 478    // or if there are multiple groups.
 479    foreach ($groups as $position => $content) {
 480      if (!empty($content) || $renderer->admin) {
 481        if ($group_count > 1 || $renderer->admin) {
 482          $output .= '<div class="' . $owner_id . '-' . $position . '">' . $content . '</div>';
 483        }
 484        else {
 485          $output .= $content;
 486        }
 487      }
 488    }
 489  
 490    return $output;
 491  }
 492  
 493  /**
 494   * Render a column in the flexible layout.
 495   */
 496  function panels_flexible_render_item($renderer, $item, $content, $id, $position, $max, $clear = FALSE) {
 497  
 498    // If we are rendering a row and there is just one row, we don't need to
 499    // render the row unless there is fixed_width content inside it.
 500    if (empty($renderer->admin) && $item['type'] == 'row' && $max == 0) {
 501      $fixed = FALSE;
 502      foreach ($item['children'] as $id) {
 503        if ($renderer->settings['items'][$id]['width_type'] != '%') {
 504          $fixed = TRUE;
 505          break;
 506        }
 507      }
 508  
 509      if (!$fixed) {
 510        return $content;
 511      }
 512    }
 513  
 514    // If we are rendering a column and there is just one column, we don't
 515    // need to render the column unless it has a fixed_width.
 516    if (empty($renderer->admin) && $item['type'] == 'column' && $max == 0 && $item['width_type'] == '%') {
 517      return $content;
 518    }
 519  
 520    $base = $renderer->item_class[$item['type']];
 521    $output = '<div class="' . $base . ' ' . $renderer->base[$item['type']] . '-' . $id;
 522    if ($position == 0) {
 523      $output .= ' ' . $base . '-first';
 524    }
 525    if ($position == $max) {
 526      $output .= ' ' . $base . '-last';
 527    }
 528    if ($clear) {
 529      $output .= ' clear-block';
 530    }
 531  
 532    if (isset($item['class'])) {
 533      $output .= ' ' . check_plain($item['class']);
 534    }
 535  
 536    $output .= '">' . "\n";
 537  
 538    if (!empty($renderer->admin)) {
 539      $output .= panels_flexible_render_item_links($renderer, $id, $item);
 540    }
 541  
 542    $output .= '  <div class="inside ' . $base . '-inside ' . $base . '-' . $renderer->base_class . '-' . $id . '-inside';
 543    if ($position == 0) {
 544      $output .= ' ' . $base . '-inside-first';
 545    }
 546    if ($position == $max) {
 547      $output .= ' ' . $base . '-inside-last';
 548    }
 549    if ($clear) {
 550      $output .= ' clear-block';
 551    }
 552  
 553    $output .= "\">\n";
 554    $output .= $content;
 555    $output .= '  </div>' . "\n";
 556    $output .= '</div>' . "\n";
 557  
 558    return $output;
 559  }
 560  /**
 561   * Render a splitter div to place between the $left and $right items.
 562   *
 563   * If the right ID is NULL that means there isn't actually a box to the
 564   * right, but we need a splitter anyway. We'll mostly use info about the
 565   * left, but pretend it's 'fluid' so that the javascript won't actually
 566   * modify the right item.
 567   */
 568  function panels_flexible_render_splitter($renderer, $left_id, $right_id) {
 569    $left = $renderer->settings['items'][$left_id];
 570  
 571    $left_class = $renderer->base[$left['type']] . '-' . $left_id;
 572    if ($right_id) {
 573      $right = $renderer->settings['items'][$right_id];
 574      $right_class = $renderer->base[$left['type']] . '-' . $right_id;
 575    }
 576    else {
 577      $right = $left;
 578      $right_class = $left_class;
 579    }
 580  
 581    $output = '<div class="panels-flexible-splitter flexible-splitter-for-' . $left_class . '">';
 582  
 583    // Name the left object
 584    $output .= '<span class="panels-flexible-splitter-left">';
 585    $output .= '.' . $left_class;
 586    $output .= '</span>';
 587  
 588    $output .= '<span class="panels-flexible-splitter-left-id">';
 589    $output .= $left_id;
 590    $output .= '</span>';
 591  
 592    $output .= '<span class="panels-flexible-splitter-left-width ' . $left_class . '-width">';
 593    $output .= $left['width'];
 594    $output .= '</span>';
 595  
 596    $output .= '<span class="panels-flexible-splitter-left-scale">';
 597    $output .= isset($renderer->scale[$left_id]) ? $renderer->scale[$left_id] : 1;
 598    $output .= '</span>';
 599  
 600    $output .= '<span class="panels-flexible-splitter-left-width-type">';
 601    $output .= $left['width_type'];
 602    $output .= '</span>';
 603  
 604    // Name the right object
 605    $output .= '<span class="panels-flexible-splitter-right">';
 606    $output .= '.' . $right_class;
 607    $output .= '</span>';
 608  
 609    $output .= '<span class="panels-flexible-splitter-right-id">';
 610    $output .= $right_id;
 611    $output .= '</span>';
 612  
 613    $output .= '<span class="panels-flexible-splitter-right-width ' . $right_class . '-width">';
 614    $output .= $right['width'];
 615    $output .= '</span>';
 616  
 617    $output .= '<span class="panels-flexible-splitter-right-scale">';
 618    $output .= isset($renderer->scale[$right_id]) ? $renderer->scale[$right_id] : 1;
 619    $output .= '</span>';
 620  
 621    $output .= '<span class="panels-flexible-splitter-right-width-type">';
 622    // If there is no right, make it fluid.
 623    $output .= $right_id ? $right['width_type'] : '%';
 624    $output .= '</span>';
 625  
 626    $output .= '</div>';
 627    return $output;
 628  }
 629  
 630  /**
 631   * Render the dropdown links for an item.
 632   */
 633  function panels_flexible_render_item_links($renderer, $id, $item) {
 634    $links = array();
 635    $remove = '';
 636    $add = '';
 637    if ($item['type'] == 'column') {
 638      $title = t('Column');
 639      $settings = t('Column settings');
 640      if (empty($item['children'])) {
 641        $remove = t('Remove column');
 642        $add = t('Add row');
 643      }
 644      else {
 645        $add = t('Add row to top');
 646        $add2 = t('Add row to bottom');
 647      }
 648    }
 649    else if ($item['type'] == 'row') {
 650      if ($id == 'canvas') {
 651        $title = t('Canvas');
 652        $settings = t('Canvas settings');
 653      }
 654      else {
 655        $title = t('Row');
 656        $settings = t('Row settings');
 657      }
 658      if (empty($item['children'])) {
 659        if ($id != 'canvas') {
 660          $remove = t('Remove row');
 661        }
 662        $add = $item['contains'] == 'region' ? t('Add region') : t('Add column');
 663      }
 664      else {
 665        $add = $item['contains'] == 'region' ? t('Add region to left') : t('Add column to left');
 666        $add2 = $item['contains'] == 'region' ? t('Add region to right') : t('Add column to right');
 667      }
 668    }
 669    else if ($item['type'] == 'region') {
 670      $title = t('Region');
 671      $settings = t('Region settings');
 672      $remove = t('Remove region');
 673    }
 674  
 675    if (!empty($settings)) {
 676      $links[] = array(
 677        'title' => $settings,
 678        'href' => $renderer->handler->get_url('layout', 'settings', $id),
 679        'attributes' => array('class' => 'ctools-use-modal'),
 680      );
 681    }
 682    if ($add) {
 683      $links[] = array(
 684        'title' => $add,
 685        'href' => $renderer->handler->get_url('layout', 'add', $id),
 686        'attributes' => array('class' => 'ctools-use-modal'),
 687      );
 688    }
 689    if (isset($add2)) {
 690      $links[] = array(
 691        'title' => $add2,
 692        'href' => $renderer->handler->get_url('layout', 'add', $id, 'right'),
 693        'attributes' => array('class' => 'ctools-use-modal'),
 694      );
 695    }
 696    if ($remove) {
 697      $links[] = array(
 698        'title' => $remove,
 699        'href' => $renderer->handler->get_url('layout', 'remove', $id),
 700        'attributes' => array('class' => 'ctools-use-ajax'),
 701      );
 702    }
 703  
 704    return theme('ctools_dropdown', $title, $links, FALSE,
 705        'flexible-layout-only flexible-links flexible-title flexible-links-' . $id);
 706  }
 707  /**
 708   * Provide CSS for a flexible layout.
 709   */
 710  function panels_flexible_render_css($renderer) {
 711    if ($renderer->admin) {
 712      $parent_class = '.' . $renderer->base['row'] . '-canvas';
 713    }
 714    else {
 715      $parent_class = '.' . $renderer->base['canvas'];
 716    }
 717    return panels_flexible_render_css_group($renderer, $renderer->settings['items']['canvas']['children'], $parent_class, 'column', 'canvas');
 718  }
 719  
 720  /**
 721   * Render the CSS for a group of items to be displayed together.
 722   *
 723   * Columns and regions, when displayed as a group, need to cooperate in
 724   * order to share margins and make sure that percent widths add up
 725   * to the right total.
 726   */
 727  function panels_flexible_render_css_group($renderer, $list, $owner_id, $type, $id) {
 728    $css = array();
 729  
 730    // Start off with some generic CSS to properly pad regions
 731    $css['.' . $renderer->item_class['region']] = array(
 732      'padding' => '0',
 733    );
 734  
 735    $css['.' . $renderer->item_class['region'] . '-inside'] = array(
 736      'padding-right' => $renderer->region_separation,
 737      'padding-left' => $renderer->region_separation,
 738    );
 739  
 740    $css['.' . $renderer->item_class['region'] . '-inside-first'] = array(
 741      'padding-left' => '0',
 742    );
 743  
 744    $css['.' . $renderer->item_class['region'] . '-inside-last'] = array(
 745      'padding-right' => '0',
 746    );
 747  
 748    $css['.' . $renderer->item_class['column']] = array(
 749      'padding' => '0',
 750    );
 751  
 752    $css['.' . $renderer->item_class['column'] . '-inside'] = array(
 753      'padding-right' => $renderer->column_separation,
 754      'padding-left' => $renderer->column_separation,
 755    );
 756  
 757    $css['.' . $renderer->item_class['column'] . '-inside-first'] = array(
 758      'padding-left' => '0',
 759    );
 760  
 761    $css['.' . $renderer->item_class['column'] . '-inside-last'] = array(
 762      'padding-right' => '0',
 763    );
 764  
 765    // And properly pad rows too
 766    $css['.' . $renderer->item_class['row']] = array(
 767      'padding' => '0 0 ' . $renderer->row_separation . ' 0',
 768      'margin' => '0',
 769    );
 770  
 771    $css['.' . $renderer->item_class['row'] . '-last'] = array(
 772      'padding-bottom' => '0',
 773    );
 774  
 775    panels_flexible_get_css_group($css, $renderer, $list, $owner_id, $type, $id);
 776  
 777    ctools_include('css');
 778    return ctools_css_assemble($css);
 779  }
 780  
 781  /**
 782   * Construct an array with all of the CSS properties for a group.
 783   *
 784   * This will parse down into children and produce all of the CSS needed if you
 785   * start from the top.
 786   */
 787  function panels_flexible_get_css_group(&$css, $renderer, $list, $owner_id, $type, $item_id) {
 788    if ($type != 'row') {
 789      // Go through our items and break up into right/center/right groups so we
 790      // can figure out our offsets.
 791  
 792      // right == any items on the right that are 'fixed'.
 793      // middle == all fluid items.
 794      // right == any items on the right that are 'fixed'.
 795      $left = $middle = $right = array();
 796      $left_total = $right_total = $middle_total = 0;
 797      $current = 'left';
 798      foreach ($list as $id) {
 799        if ($renderer->settings['items'][$id]['width_type'] == 'px') {
 800          // fixed
 801          if ($current == 'left') {
 802            $left[] = $id;
 803            $renderer->positions[$id] = 'left';
 804            $left_total += $renderer->settings['items'][$id]['width'];
 805          }
 806          else {
 807            $current = 'right';
 808            $right[] = $id;
 809            $renderer->positions[$id] = 'right';
 810            $right_total += $renderer->settings['items'][$id]['width'];
 811          }
 812        }
 813        else {
 814          // fluid
 815          if ($current != 'right') {
 816            $current = 'middle';
 817            $middle[] = $id;
 818            $renderer->positions[$id] = 'middle';
 819            $middle_total += $renderer->settings['items'][$id]['width'];
 820          }
 821          // fall through: if current is 'right' and we ran into a 'fluid' then
 822          // it gets *dropped* because that is invalid.
 823        }
 824      }
 825  
 826      // Go through our right sides and create CSS.
 827      foreach ($left as $id) {
 828        $class = "." . $renderer->base[$type] . "-$id";
 829        $css[$class] = array(
 830          'position' => 'relative',
 831          'float' => 'left',
 832          'background-color' => 'transparent',
 833          'width' => $renderer->settings['items'][$id]['width'] . "px",
 834        );
 835      }
 836  
 837      // Do the same for right.
 838      $right_pixels = 0;
 839  
 840      foreach ($right as $id) {
 841        $class = "." . $renderer->base[$type] . "-$id";
 842        $css[$class] = array(
 843          'float' => 'left',
 844          'width' => $renderer->settings['items'][$id]['width'] . "px",
 845        );
 846      }
 847  
 848      $max = count($middle) - 1;
 849  
 850      if ($middle_total) {
 851        // Because we love IE so much, auto scale everything to 99%. This
 852        // means adding up the actual widths and then providing a multiplier
 853        // to each so that the total is 99%.
 854        $scale = $renderer->scale_base / $middle_total;
 855        foreach ($middle as $position => $id) {
 856          $class = "." . $renderer->base[$type] . "-$id";
 857          $css[$class] = array(
 858            'float' => 'left',
 859            'width' => number_format($renderer->settings['items'][$id]['width'] * $scale, 4, '.', '') . "%",
 860          );
 861  
 862          // Store this so we can use it later.
 863          // @todo: Store the scale, not the new width, so .js can adjust
 864          // bi-directionally.
 865          $renderer->scale[$id] = $scale;
 866        }
 867      }
 868  
 869      // If there is any total remaining, we need to offset the splitter
 870      // by this much too.
 871      if ($left_total) {
 872        // Add this even if it's 0 so we can handle removals.
 873        $css["$owner_id-inside"]['padding-left'] = '0px';
 874        if ($renderer->admin || count($middle)) {
 875          $css["$owner_id-middle"]['margin-left'] = $left_total . 'px';
 876          // IE hack
 877          $css["* html $owner_id-left"]['left'] = $left_total . "px";
 878          // Make this one very specific to the admin CSS so that preview
 879          // does not stomp it.
 880          $css[".panel-flexible-admin $owner_id-inside"]['padding-left'] = '0px';
 881        }
 882        else {
 883          $css["$owner_id-inside"]['margin-left'] = '-' . $left_total . 'px';
 884          $css["$owner_id-inside"]['padding-left'] = $left_total . 'px';
 885          // IE hack
 886          $css["* html $owner_id-inside"]['left'] = $left_total . "px";
 887        }
 888      }
 889      if ($right_total) {
 890        $css["$owner_id-middle"]['margin-right'] = $right_total . 'px';
 891      }
 892      $css["$owner_id-inside"]['padding-right'] = '0px';
 893    }
 894  
 895    // If the canvas has a fixed width set, and this is the canvas, fix the
 896    // width.
 897    if ($item_id == 'canvas') {
 898      $item = $renderer->settings['items'][$item_id];
 899  
 900      if (!empty($item['fixed_width']) && intval($item['fixed_width'])) {
 901        $css['.' . $renderer->base['canvas']]['width'] = intval($item['fixed_width']) . 'px';
 902      }
 903      else {
 904        $css['.' . $renderer->base['canvas']]['width'] = 'auto';
 905      }
 906    }
 907  
 908    // Go through each item and process children.
 909    foreach ($list as $id) {
 910      $item = $renderer->settings['items'][$id];
 911      if (empty($item['children'])) {
 912        continue;
 913      }
 914  
 915      if ($type == 'column') {
 916        // Columns can only contain rows.
 917        $child_type = 'row';
 918      }
 919      else {
 920        $child_type = isset($item['contains']) ? $item['contains'] : 'region';
 921      }
 922  
 923      $class = "." . $renderer->base[$type] . "-$id";
 924      panels_flexible_get_css_group($css, $renderer, $item['children'], $class, $child_type, $id);
 925    }
 926  }
 927  
 928  /**
 929   * AJAX responder to edit flexible settings for an item.
 930   *
 931   * $handler object
 932   *   The display renderer handler object.
 933   */
 934  function panels_ajax_flexible_edit_settings($handler, $id) {
 935    $settings = &$handler->display->layout_settings;
 936    panels_flexible_convert_settings($settings, $handler->plugins['layout']);
 937  
 938    if (empty($settings['items'][$id])) {
 939      ctools_modal_render(t('Error'), t('Invalid item id.'));
 940    }
 941  
 942    $item = &$settings['items'][$id];
 943    $siblings = array();
 944  
 945    if ($id != 'canvas') {
 946      $siblings = $settings['items'][$item['parent']]['children'];
 947    }
 948  
 949  
 950    switch ($item['type']) {
 951      case 'column':
 952        $title = t('Configure column');
 953        break;
 954      case 'row':
 955        if ($id == 'canvas') {
 956          $title = t('Configure canvas');
 957        }
 958        else {
 959          $title = t('Configure row');
 960        }
 961        break;
 962      case 'region':
 963        $title = t('Configure region');
 964        break;
 965    }
 966  
 967    $form_state = array(
 968      'display' => &$handler->display,
 969      'item' => &$item,
 970      'id' => $id,
 971      'siblings' => $siblings,
 972      'settings' => &$settings,
 973      'ajax' => TRUE,
 974      'title' => $title,
 975      'op' => 'edit',
 976    );
 977  
 978    $output = ctools_modal_form_wrapper('panels_flexible_config_item_form', $form_state);
 979    if (empty($output)) {
 980      // If the width type changed then other nearby items will have
 981      // to have their widths adjusted.
 982      panels_edit_cache_set($handler->cache);
 983  
 984      $css_id = isset($handler->display->css_id) ? $handler->display->css_id : '';
 985      $renderer = panels_flexible_create_renderer(TRUE, $css_id, array(), $settings, $handler->display, $handler->plugins['layout'], $handler);
 986  
 987      $output = array();
 988      // If the item is a region, replace the title.
 989      $class = $renderer->base[$item['type']] . '-' . $id;
 990      if ($item['type'] == 'region') {
 991        $output[] = ctools_ajax_command_replace(".$class h2.label",
 992          '<h2 class="label">' . check_plain($item['title']) . '</h2>');
 993      }
 994  
 995      // Rerender our links in case something changed.
 996      $output[] = ctools_ajax_command_replace('.flexible-links-' . $id,
 997        panels_flexible_render_item_links($renderer, $id, $item));
 998  
 999      // If editing the canvas, reset the CSS width
1000      if ($id == 'canvas') {
1001        // update canvas CSS.
1002        $css = array(
1003          '.' . $renderer->item_class['column'] . '-inside' => array(
1004            'padding-left' => $renderer->column_separation,
1005            'padding-right' => $renderer->column_separation,
1006          ),
1007          '.' . $renderer->item_class['region'] . '-inside' => array(
1008            'padding-left' => $renderer->region_separation,
1009            'padding-right' => $renderer->region_separation,
1010          ),
1011          '.' . $renderer->item_class['row'] => array(
1012            'padding-bottom' => $renderer->row_separation,
1013          ),
1014        );
1015        if (!empty($item['fixed_width']) && intval($item['fixed_width'])) {
1016          $css['.' . $renderer->base['canvas']] = array('width' => intval($item['fixed_width']) . 'px');
1017        }
1018        else {
1019          $css['.' . $renderer->base['canvas']] = array('width' => 'auto');
1020        }
1021        foreach ($css as $selector => $data) {
1022          $output[] = ctools_ajax_command_css($selector, $data);
1023        }
1024      }
1025  
1026      $output[] = ctools_modal_command_dismiss();
1027    }
1028  
1029    $handler->commands = $output;
1030  }
1031  
1032  /**
1033   * Configure a row, column or region on the flexible page.
1034   *
1035   * @param <type> $form_state
1036   * @return <type>
1037   */
1038  function panels_flexible_config_item_form(&$form_state) {
1039    $display = &$form_state['display'];
1040    $item = &$form_state['item'];
1041    $siblings = &$form_state['siblings'];
1042    $settings = &$form_state['settings'];
1043    $id = &$form_state['id'];
1044  
1045    if ($item['type'] == 'region') {
1046      $form['title'] = array(
1047        '#title' => t('Region title'),
1048        '#type' => 'textfield',
1049        '#default_value' => $item['title'],
1050        '#required' => TRUE,
1051      );
1052    }
1053  
1054    if ($id == 'canvas') {
1055      $form['class'] = array(
1056        '#title' => t('Canvas class'),
1057        '#type' => 'textfield',
1058        '#default_value' => isset($item['class']) ? $item['class'] : '',
1059        '#description' => t('This class will the primary class for this layout. It will also be appended to all column, row and region_classes to ensure that layouts within layouts will not inherit CSS from each other. If left blank, the name of the layout or ID of the display will be used.'),
1060      );
1061  
1062      $form['column_class'] = array(
1063        '#title' => t('Column class'),
1064        '#type' => 'textfield',
1065        '#default_value' => isset($item['column_class']) ? $item['column_class'] : '',
1066        '#description' => t('This class will be applied to all columns of the layout. If left blank this will be panels-flexible-column.'),
1067      );
1068  
1069      $form['row_class'] = array(
1070        '#title' => t('Row class'),
1071        '#type' => 'textfield',
1072        '#default_value' => isset($item['row_class']) ? $item['row_class'] : '',
1073        '#description' => t('This class will be applied to all rows of the layout. If left blank this will be panels-flexible-row.'),
1074      );
1075  
1076      $form['region_class'] = array(
1077        '#title' => t('Region class'),
1078        '#type' => 'textfield',
1079        '#default_value' => isset($item['region_class']) ? $item['region_class'] : '',
1080        '#description' => t('This class will be applied to all regions of the layout. If left blank this will be panels-flexible-region.'),
1081      );
1082  
1083      $form['no_scale'] = array(
1084        '#type' => 'checkbox',
1085        '#title' => t('Scale fluid widths for IE6'),
1086        '#description' => t('IE6 does not do well with 100% widths. If checked, width will be scaled to 99% to compensate.'),
1087        '#default_value' => empty($item['no_scale']),
1088      );
1089  
1090      $form['fixed_width'] = array(
1091        '#type' => 'textfield',
1092        '#title' => t('Fixed width'),
1093        '#description' => t('If a value is entered, the layout canvas will be fixed to the given pixel width.'),
1094        '#default_value' => isset($item['fixed_width']) ? $item['fixed_width'] : '',
1095      );
1096  
1097      $form['column_separation'] = array(
1098        '#type' => 'textfield',
1099        '#title' => t('Column separation'),
1100        '#description' => t('How much padding to put on columns that are that are next to other columns. Note that this is put on both columns so the real amount is doubled.'),
1101        '#default_value' => isset($item['column_separation']) ? $item['column_separation'] : '0.5em',
1102      );
1103  
1104      $form['region_separation'] = array(
1105        '#type' => 'textfield',
1106        '#title' => t('Region separation'),
1107        '#description' => t('How much padding to put on regions that are that are next to other regions. Note that this is put on both regions so the real amount is doubled.'),
1108        '#default_value' => isset($item['region_separation']) ? $item['region_separation'] : '0.5em',
1109      );
1110  
1111      $form['row_separation'] = array(
1112        '#type' => 'textfield',
1113        '#title' => t('Row separation'),
1114        '#description' => t('How much padding to put on beneath rows to separate them from each other. Because this is placed only on the bottom, not hte top, this is NOT doubled like column/region separation.'),
1115        '#default_value' => isset($item['row_separation']) ? $item['row_separation'] : '0.5em',
1116      );
1117    }
1118    else {
1119      $form['class'] = array(
1120        '#title' => t('CSS class'),
1121        '#type' => 'textfield',
1122        '#default_value' => isset($item['class']) ? $item['class'] : '',
1123        '#description' => t('Enter a CSS class that will be used. This can be used to apply automatic styling from your theme, for example.'),
1124      );
1125  
1126      if ($item['type'] != 'row') {
1127        // Test to see if there are fluid items to the left or the right. If there
1128        // are fluid items on both sides, this item cannot be set to fixed.
1129        $left = $right = FALSE;
1130        $current = 'left';
1131        foreach ($siblings as $sibling) {
1132          if ($sibling == $id) {
1133            $current = 'right';
1134          }
1135          else if ($settings['items'][$sibling]['width_type'] == '%') {
1136            $$current = TRUE; // Indirection.
1137          }
1138        }
1139  
1140        $form['width_type'] = array(
1141          '#type' => 'select',
1142          '#title' => t('Width'),
1143          '#default_value' => $item['width_type'],
1144          '#options' => array(
1145            '%' => t('Fluid'),
1146            'px' => t('Fixed'),
1147          ),
1148          '#disabled' => TRUE,
1149        );
1150      }
1151      else {
1152        $form['contains'] = array(
1153          '#type' => 'select',
1154          '#title' => t('Contains'),
1155          '#default_value' => $item['contains'],
1156          '#options' => array(
1157            'region' => t('Regions'),
1158            'column' => t('Columns'),
1159          ),
1160        );
1161  
1162        if (!empty($item['children'])) {
1163          $form['contains']['#disabled'] = TRUE;
1164          $form['contains']['#value'] = $item['contains'];
1165          $form['contains']['#description'] = t('You must remove contained items to change the row container type.');
1166        }
1167      }
1168    }
1169  
1170    $form['save'] = array(
1171      '#type' => 'submit',
1172      '#value' => t('Save'),
1173    );
1174  
1175    return $form;
1176  }
1177  
1178  /**
1179   * Submit handler for editing a flexible item.
1180   */
1181  function panels_flexible_config_item_form_submit(&$form, &$form_state) {
1182    $item = &$form_state['item'];
1183    if ($item['type'] == 'region') {
1184      $item['title'] = $form_state['values']['title'];
1185    }
1186  
1187    $item['class'] = $form_state['values']['class'];
1188  
1189    if ($form_state['id'] == 'canvas') {
1190      $item['column_class'] = $form_state['values']['column_class'];
1191      $item['row_class'] = $form_state['values']['row_class'];
1192      $item['region_class'] = $form_state['values']['region_class'];
1193      // Reverse this as the checkbox is backward from how we actually store
1194      // it to make it simpler to default to scaling.
1195      $item['no_scale'] = !$form_state['values']['no_scale'];
1196      $item['fixed_width'] = $form_state['values']['fixed_width'];
1197      $item['column_separation'] = $form_state['values']['column_separation'];
1198      $item['region_separation'] = $form_state['values']['region_separation'];
1199      $item['row_separation'] = $form_state['values']['row_separation'];
1200    }
1201    else if ($item['type'] != 'row') {
1202      $item['width_type'] = $form_state['values']['width_type'];
1203    }
1204    else {
1205      $item['contains'] = $form_state['values']['contains'];
1206    }
1207  
1208  }
1209  
1210  /**
1211   * AJAX responder to add a new row, column or region to a flexible layout.
1212   */
1213  function panels_ajax_flexible_edit_add($handler, $id, $location = 'left') {
1214    ctools_include('modal');
1215    ctools_include('ajax');
1216    $settings = &$handler->display->layout_settings;
1217    panels_flexible_convert_settings($settings, $handler->plugins['layout']);
1218  
1219    if (empty($settings['items'][$id])) {
1220      ctools_modal_render(t('Error'), t('Invalid item id.'));
1221    }
1222  
1223    $parent = &$settings['items'][$id];
1224  
1225    switch ($parent['type']) {
1226      case 'column':
1227        $title = t('Add row');
1228        // Create the new item with defaults.
1229        $item = array(
1230          'type' => 'row',
1231          'contains' => 'region',
1232          'children' => array(),
1233          'parent' => $id,
1234        );
1235        break;
1236      case 'row':
1237        switch ($parent['contains']) {
1238          case 'region':
1239            $title = $location == 'left' ? t('Add region to left') : t('Add region to right');
1240            $item = array(
1241              'type' => 'region',
1242              'title' => '',
1243              'width' => 100,
1244              'width_type' => '%',
1245              'parent' => $id,
1246            );
1247            break;
1248          case 'column':
1249            $title = $location == 'left' ? t('Add column to left') : t('Add column to right');
1250            $item = array(
1251              'type' => 'column',
1252              'width' => 100,
1253              'width_type' => '%',
1254              'parent' => $id,
1255              'children' => array(),
1256            );
1257            break;
1258        }
1259        // Create the new item with defaults.
1260        break;
1261      case 'region':
1262        // Cannot add items to regions.
1263        break;
1264    }
1265  
1266    $form_state = array(
1267      'display' => &$handler->display,
1268      'parent' => &$parent,
1269      'item' => &$item,
1270      'id' => $id,
1271      'settings' => &$settings,
1272      'ajax' => TRUE,
1273      'title' => $title,
1274      'location' => $location,
1275    );
1276  
1277    $output = ctools_modal_form_wrapper('panels_flexible_add_item_form', $form_state);
1278    if (empty($output)) {
1279      // If the width type changed then other nearby items will have
1280      // to have their widths adjusted.
1281      panels_edit_cache_set($handler->cache);
1282      $output = array();
1283  
1284      $css_id = isset($handler->display->css_id) ? $handler->display->css_id : '';
1285      // Create a renderer object so we can render our new stuff.
1286      $renderer = panels_flexible_create_renderer(TRUE, $css_id, array(), $settings, $handler->display, $handler->plugins['layout'], $handler);
1287  
1288      $content = '';
1289      if ($item['type'] == 'region') {
1290        $handler->plugins['layout']['panels'][$form_state['key']] = $item['title'];
1291  
1292        $content = $handler->render_region($form_state['key'], array());
1293  
1294        // Manually add the hidden field that our region uses to store pane info.
1295        $content .= '<input type="hidden" name="panel[pane][' .
1296          $form_state['key'] . ']" id="edit-panel-pane-' . $form_state['key'] . '" value="" />';
1297  
1298      }
1299      else {
1300        // We need to make sure the left/middle/right divs exist inside this
1301        // so that more stuff can be added inside it as needed.
1302        foreach (array('left', 'middle', 'right') as $position) {
1303          if (!empty($content) || $renderer->admin) {
1304            $content .= '<div class="' . $renderer->base[$item['type']] . '-' . $form_state['key'] . '-' . $position . '"></div>';
1305          }
1306        }
1307  
1308      }
1309  
1310      // render the item
1311      $parent_class =  $renderer->base[$parent['type']] . '-' . $id;
1312      $item_output = panels_flexible_render_item($renderer, $item, $content, $form_state['key'], 0, 0, $item['type'] == 'row');
1313  
1314      // Get all the CSS necessary for the entire row (as width adjustments may
1315      // have cascaded).
1316      $css = array();
1317      panels_flexible_get_css_group($css, $renderer, $parent['children'], '.' . $parent_class, $item['type'], $id);
1318  
1319      $position = isset($renderer->positions[$form_state['key']]) ? $renderer->positions[$form_state['key']] : 'middle';
1320      // If there's a nearby item, add the splitter and rewrite the width
1321      // of the nearby item as it probably got adjusted.
1322      // The blocks of code in this else look very similar but are not actually
1323      // duplicated because the order changes based on left or right.
1324      switch ($position) {
1325        case 'left':
1326          if ($location == 'left') {
1327            $item_output .= panels_flexible_render_splitter($renderer, $form_state['key'], $form_state['sibling']);
1328            $output[] = ctools_ajax_command_prepend('#panels-dnd-main .' . $parent_class . '-left', $item_output);
1329          }
1330          else if ($location == 'right') {
1331            // If we are adding to the right side of the left box, there is
1332            // a splitter that we have to remove; then we add our box normally,
1333            // and then add a new splitter for just our guy.
1334            $output[] = ctools_ajax_command_remove('panels-flexible-splitter-for-' . $renderer->base[$item['type']] . '-' . $form_state['key']);
1335            $item_output = panels_flexible_render_splitter($renderer, $form_state['sibling'], $form_state['key']) .  $item_output;
1336            $item_output .= panels_flexible_render_splitter($renderer, $form_state['key'], NULL);
1337            $output[] = ctools_ajax_command_append('#panels-dnd-main .' . $parent_class . '-left', $item_output);
1338          }
1339          break;
1340        case 'right':
1341          if (!empty($form_state['sibling'])) {
1342            $item_output = panels_flexible_render_splitter($renderer, $form_state['sibling'], $form_state['key']) .  $item_output;
1343          }
1344          $output[] = ctools_ajax_command_append('#panels-dnd-main .' . $parent_class . '-right', $item_output);
1345          break;
1346        case 'middle':
1347          if ($location == 'left') {
1348            if (!empty($form_state['sibling'])) {
1349              $item_output .= panels_flexible_render_splitter($renderer, $form_state['key'], $form_state['sibling']);
1350            }
1351            $output[] = ctools_ajax_command_prepend('#panels-dnd-main .' . $parent_class . '-middle', $item_output);
1352          }
1353          else {
1354            if (!empty($form_state['sibling'])) {
1355              $item_output = panels_flexible_render_splitter($renderer, $form_state['sibling'], $form_state['key']) .  $item_output;
1356            }
1357            $output[] = ctools_ajax_command_append('#panels-dnd-main .' . $parent_class . '-middle', $item_output);
1358          }
1359          break;
1360  
1361      }
1362  
1363      // Send our fix height command.
1364      $output[] = array('command' => 'flexible_fix_height');
1365  
1366      if (!empty($form_state['sibling'])) {
1367        $sibling_width = '#panels-dnd-main .' . $renderer->base[$item['type']] . '-' . $form_state['sibling'] . '-width';
1368        $output[] = ctools_ajax_command_html($sibling_width, $settings['items'][$form_state['sibling']]['width']);
1369      }
1370      foreach ($css as $selector => $data) {
1371        $output[] = ctools_ajax_command_css($selector, $data);
1372      }
1373  
1374      // Rerender our parent item links:
1375      $output[] = ctools_ajax_command_replace('.flexible-links-' . $id,
1376        panels_flexible_render_item_links($renderer, $id, $parent));
1377  
1378      $output[] = array(
1379        'command' => 'flexible_fix_firstlast',
1380        'selector' => '.' . $parent_class . '-inside',
1381        'base' => 'panels-flexible-' . $item['type'],
1382      );
1383  
1384      $output[] = ctools_modal_command_dismiss();
1385    }
1386  
1387    $handler->commands = $output;
1388  }
1389  /**
1390   * Form to add a row, column or region to a flexible layout.
1391   * @param <type> $form_state
1392   * @return <type>
1393   */
1394  function panels_flexible_add_item_form(&$form_state) {
1395    $display = &$form_state['display'];
1396    $item = &$form_state['item'];
1397    $parent = &$form_state['parent'];
1398    $settings = &$form_state['settings'];
1399    $location = &$form_state['location'];
1400    $id = &$form_state['id'];
1401  
1402    if ($item['type'] == 'region') {
1403      $form['title'] = array(
1404        '#title' => t('Region title'),
1405        '#type' => 'textfield',
1406        '#default_value' => $item['title'],
1407        '#required' => TRUE,
1408      );
1409    }
1410  
1411    $form['class'] = array(
1412      '#title' => t('CSS Class'),
1413      '#type' => 'textfield',
1414      '#default_value' => isset($item['class']) ? $item['class'] : '',
1415      '#description' => t('Enter a CSS class that will be used. This can be used to apply automatic styling from your theme, for example.'),
1416    );
1417  
1418    if ($item['type'] != 'row') {
1419      // If there is a 'fixed' type on the side we're adding to, then this
1420      // must also be fixed. Otherwise it can be either and should default to
1421      // fluid.
1422      $restrict = FALSE;
1423  
1424      if (!empty($parent['children'])) {
1425        if ($location == 'left') {
1426          $sibling = reset($parent['children']);
1427        }
1428        else {
1429          $sibling = end($parent['children']);
1430        }
1431        if ($settings['items'][$sibling]['width_type'] == 'px') {
1432          $restrict = TRUE;
1433          $item['width_type'] = 'px';
1434        }
1435      }
1436  
1437      $form['width_type'] = array(
1438        '#type' => 'select',
1439        '#title' => t('Width'),
1440        '#default_value' => $item['width_type'],
1441        '#options' => array(
1442          '%' => t('Fluid'),
1443          'px' => t('Fixed'),
1444        ),
1445        '#disabled' => $restrict,
1446      );
1447      if ($restrict) {
1448        // This forces the value because disabled items don't always send
1449        // their data back.
1450        $form['width_type']['#value'] = $item['width_type'];
1451        $form['width_type']['#description'] = t('Items cannot be set to fluid if there are fixed items already on that side.');
1452      }
1453    }
1454    else {
1455      $form['contains'] = array(
1456        '#type' => 'select',
1457        '#title' => t('Contains'),
1458        '#default_value' => $item['contains'],
1459        '#options' => array(
1460          'region' => t('Regions'),
1461          'column' => t('Columns'),
1462        ),
1463      );
1464    }
1465  
1466    $form['save'] = array(
1467      '#type' => 'submit',
1468      '#value' => t('Save'),
1469    );
1470  
1471    return $form;
1472  }
1473  
1474  /**
1475   * Submit handler for editing a flexible item.
1476   */
1477  function panels_flexible_add_item_form_submit(&$form, &$form_state) {
1478    $item = &$form_state['item'];
1479    $parent = &$form_state['parent'];
1480    $location = &$form_state['location'];
1481    $settings = &$form_state['settings'];
1482  
1483    $item['class'] = $form_state['values']['class'];
1484  
1485    if ($item['type'] == 'region') {
1486      $item['title'] = $form_state['values']['title'];
1487    }
1488  
1489    if ($item['type'] != 'row') {
1490      $item['width_type'] = $form_state['values']['width_type'];
1491    }
1492    else {
1493      $item['contains'] = $form_state['values']['contains'];
1494    }
1495  
1496    if ($item['type'] == 'region') {
1497      // derive the region key from the title
1498      $key = preg_replace("/[^a-z0-9]/", '_', drupal_strtolower($item['title']));
1499      while (isset($settings['items'][$key])) {
1500        $key .= '_';
1501      }
1502      $form_state['key'] = $key;
1503    }
1504    else {
1505      $form_state['key'] = $key = max(array_keys($settings['items'])) + 1;
1506    }
1507  
1508    $form_state['sibling'] = NULL;
1509    if ($item['type'] != 'row' && !empty($parent['children'])) {
1510      // Figure out what the width should be and adjust our sibling if
1511      // necessary.
1512      if ($location == 'left') {
1513        $form_state['sibling'] = reset($parent['children']);
1514      }
1515      else {
1516        $form_state['sibling'] = end($parent['children']);
1517  
1518      }
1519  
1520      // If there is no sibling, or the sibling is of a different type,
1521      // the default 100 will work for either fixed or fluid.
1522      if ($form_state['sibling'] && $settings['items'][$form_state['sibling']]['width_type'] == $item['width_type']) {
1523        // steal half of the sibling's space.
1524        $width = $settings['items'][$form_state['sibling']]['width'] / 2;
1525        $settings['items'][$form_state['sibling']]['width'] = $width;
1526        $item['width'] = $width;
1527      }
1528    }
1529  
1530    // Place the item.
1531    $settings['items'][$key] = $item;
1532    if ($location == 'left') {
1533      array_unshift($parent['children'], $key);
1534    }
1535    else {
1536      $parent['children'][] = $key;
1537    }
1538  }
1539  
1540  /**
1541   * AJAX responder to remove an existing row, column or region from a flexible
1542   * layout.
1543   */
1544  function panels_ajax_flexible_edit_remove($handler, $id) {
1545    $settings = &$handler->display->layout_settings;
1546    panels_flexible_convert_settings($settings, $handler->plugins['layout']);
1547  
1548    if (empty($settings['items'][$id])) {
1549      ctools_ajax_render_error(t('Invalid item id.'));
1550    }
1551  
1552    $item = &$settings['items'][$id];
1553    $css_id = isset($handler->display->css_id) ? $handler->display->css_id : '';
1554    // Create a renderer object so we can render our new stuff.
1555    $renderer = panels_flexible_create_renderer(TRUE, $css_id, array(), $settings, $handler->display, $handler->plugins['layout'], $handler);
1556  
1557  
1558    $siblings = &$settings['items'][$item['parent']]['children'];
1559    $parent_class = '.'  . $renderer->base[$settings['items'][$item['parent']]['type']] . '-' . $item['parent'];
1560  
1561    // Find the offset of our array. This will also be the key because
1562    // this is a simple array.
1563    $offset = array_search($id, $siblings);
1564  
1565    // Only bother with this stuff if our item is fluid, since fixed is
1566    // as fixed does.
1567    if ($item['type'] != 'row') {
1568      if (isset($siblings[$offset + 1])) {
1569        $next = $siblings[$offset + 1];
1570      }
1571      if (isset($siblings[$offset - 1])) {
1572        $prev = $siblings[$offset - 1];
1573      }
1574  
1575      if ($item['width_type'] == '%') {
1576        // First, try next.
1577        if (isset($next) && $settings['items'][$next]['width_type'] == '%') {
1578          $settings['items'][$next]['width'] += $item['width'];
1579        }
1580        // If that failed, try the previous one.
1581        else if (isset($prev) && $settings['items'][$prev]['width_type'] == '%') {
1582          $settings['items'][$prev]['width'] += $item['width'];
1583        }
1584      }
1585      // Not sure what happens if they both failed. Maybe nothing.
1586    }
1587  
1588    // Remove the item.
1589    array_splice($siblings, $offset, 1);
1590  
1591    unset($settings['items'][$id]);
1592  
1593    // Save our new state.
1594    panels_edit_cache_set($handler->cache);
1595    $class = $renderer->base[$item['type']] . '-' . $id;
1596    $output = array();
1597  
1598    $output[] = ctools_ajax_command_remove('#panels-dnd-main .' . $class);
1599  
1600    // Regenerate the CSS for siblings.
1601    if (!empty($siblings)) {
1602      // Get all the CSS necessary for the entire row (as width adjustments may
1603      // have cascaded).
1604      $css = array();
1605      panels_flexible_get_css_group($css, $renderer, $siblings, $parent_class, $item['type'], $item['parent']);
1606      foreach ($css as $selector => $data) {
1607        $output[] = ctools_ajax_command_css($selector, $data);
1608      }
1609    }
1610  
1611    // There are potentially two splitters linked to this item to be removed.
1612    if (!empty($prev)) {
1613      $output[] = ctools_ajax_command_remove('.flexible-splitter-for-' . $renderer->base[$item['type']] . '-' . $prev);
1614    }
1615  
1616    // Try to remove the 'next' one even if there isn't a $next.
1617    $output[] = ctools_ajax_command_remove('.flexible-splitter-for-' . $renderer->base[$item['type']] . '-' . $id);
1618  
1619    if (!empty($prev) && !empty($next)) {
1620      // Add a new splitter that links $prev and $next:
1621      $splitter = panels_flexible_render_splitter($renderer, $prev, $next);
1622      $prev_class = '#panels-dnd-main .' . $renderer->base[$item['type']] . '-' . $prev;
1623      $output[] = ctools_ajax_command_after($prev_class, $splitter);
1624      // Send our fix height command.
1625      $output[] = array('command' => 'flexible_fix_height');
1626    }
1627    // Rerender our parent item links:
1628    $output[] = ctools_ajax_command_replace('.flexible-links-' . $item['parent'],
1629      panels_flexible_render_item_links($renderer, $item['parent'], $settings['items'][$item['parent']]));
1630  
1631    $output[] = array(
1632      'command' => 'flexible_fix_firstlast',
1633      'selector' => $parent_class . '-inside',
1634      'base' => 'panels-flexible-' . $item['type'],
1635    );
1636  
1637    $handler->commands = $output;
1638  }
1639  
1640  /**
1641   * AJAX responder to store resize information when the user adjusts the
1642   * splitter.
1643   */
1644  function panels_ajax_flexible_edit_resize($handler) {
1645    ctools_include('ajax');
1646    $settings = &$handler->display->layout_settings;
1647    panels_flexible_convert_settings($settings, $handler->plugins['layout']);
1648  
1649    $settings['items'][$_POST['left']]['width'] = $_POST['left_width'];
1650    if (!empty($_POST['right']) && $_POST['right'] != $_POST['left']) {
1651      $settings['items'][$_POST['right']]['width'] = $_POST['right_width'];
1652    }
1653  
1654    // Save our new state.
1655    panels_edit_cache_set($handler->cache);
1656  
1657    $handler->commands = array('ok');
1658  }
1659  
1660  /**
1661   * AJAX form to bring up the "reuse" modal.
1662   */
1663  function panels_ajax_flexible_edit_reuse($handler) {
1664    $settings = &$handler->display->layout_settings;
1665    panels_flexible_convert_settings($settings, $handler->plugins['layout']);
1666  
1667    $form_state = array(
1668      'display' => &$handler->display,
1669      'settings' => &$settings,
1670      'ajax' => TRUE,
1671      'title' => t('Save this layout for reuse'),
1672    );
1673  
1674    $output = ctools_modal_form_wrapper('panels_flexible_reuse_form', $form_state);
1675    if (empty($output)) {
1676      // Create the new layout.
1677      ctools_include('export');
1678      $layout = ctools_export_crud_new('panels_layout');
1679      $layout->plugin = 'flexible';
1680      $layout->name = $form_state['values']['name'];
1681      $layout->admin_title = $form_state['values']['admin_title'];
1682      $layout->admin_description = $form_state['values']['admin_description'];
1683      $layout->category = $form_state['values']['category'];
1684      $layout->settings = $handler->display->layout_settings;
1685  
1686      // Save it.
1687      ctools_export_crud_save('panels_layout', $layout);
1688  
1689      if (empty($form_state['values']['keep'])) {
1690        // Set the actual layout_settings to now use the newly minted layout:
1691        $handler->display->layout = 'flexible:' . $layout->name;
1692        $handler->display->layout_settings = array();
1693  
1694        // Save our new state.
1695        panels_edit_cache_set($handler->cache);
1696      }
1697  
1698      // Dismiss the modal.
1699      $output[] = ctools_modal_command_dismiss();
1700    }
1701  
1702    $handler->commands = $output;
1703  }
1704  
1705  function panels_flexible_reuse_form(&$form_state) {
1706    $form['markup'] = array(
1707      '#prefix' => '<div class="description">',
1708      '#suffix' => '</div>',
1709      '#value' => t('If you save this layout for reuse it will appear in the list of reusable layouts at admin/build/panels/layouts, and you will need to go there to edit it. This layout will then become an option for all future panels you make.'),
1710    );
1711  
1712    $form['admin_title'] = array(
1713      '#type' => 'textfield',
1714      '#title' => t('Administrative title'),
1715      '#description' => t('This will appear in the administrative interface to easily identify it.'),
1716    );
1717  
1718    $form['name'] = array(
1719      '#type' => 'textfield',
1720      '#title' => t('Machine name'),
1721      '#description' => t('The machine readable name of this layout. It must be unique, and it must contain only alphanumeric characters and underscores. Once created, you will not be able to change this value!'),
1722    );
1723  
1724    $form['category'] = array(
1725      '#type' => 'textfield',
1726      '#title' => t('Category'),
1727      '#description' => t('What category this layout should appear in. If left blank the category will be "Miscellaneous".'),
1728    );
1729  
1730    $form['admin_description'] = array(
1731      '#type' => 'textarea',
1732      '#title' => t('Administrative description'),
1733      '#description' => t('A description of what this layout is, does or is for, for administrative use.'),
1734    );
1735  
1736    $form['keep'] = array(
1737      '#type' => 'checkbox',
1738      '#title' => t('Keep current panel layout flexible'),
1739      '#description' => t('If checked, this panel will continue to use a generic flexible layout and will not use the saved layout. Use this option if you wish to clone this layout.'),
1740    );
1741  
1742    $form['submit'] = array(
1743      '#type' => 'submit',
1744      '#value' => t('Save'),
1745    );
1746  
1747    return $form;
1748  }
1749  
1750  function panels_flexible_reuse_form_validate(&$form, &$form_state) {
1751    if (empty($form_state['values']['name'])) {
1752      form_error($form['name'], t('You must choose a machine name.'));
1753    }
1754  
1755    ctools_include('export');
1756    $test = ctools_export_crud_load('panels_layout', $form_state['values']['name']);
1757    if ($test) {
1758      form_error($form['name'], t('That name is used by another layout: @layout', array('@layout' => $test->admin_title)));
1759    }
1760  
1761    // Ensure name fits the rules:
1762    if (preg_match('/[^a-zA-Z0-9_]/', $form_state['values']['name'])) {
1763      form_error($form['name'], t('Name must be alphanumeric or underscores only.'));
1764    }
1765  }


Generated: Thu Mar 24 11:18:33 2011 Cross-referenced by PHPXref 0.7