5 * Implementation of hook_panels_layouts()
9 'title' => t('Flexible'),
10 'icon' => 'flexible.png',
11 'theme' => 'panels_flexible',
12 'admin theme' => 'panels_flexible_admin',
13 'css' => 'flexible.css',
14 'admin css' => 'flexible-admin.css',
15 'settings form' => 'panels_flexible_settings_form',
16 'settings submit' => 'panels_flexible_settings_submit',
17 'settings validate' => 'panels_flexible_settings_validate',
18 'panels function' => 'panels_flexible_panels',
19 'hook menu' => 'panels_flexible_menu',
23 * Delegated implementation of hook_menu().
25 function panels_flexible_menu(&$items, &$info) {
27 'access arguments' => array('access content'),
28 'page arguments' => array(4),
29 'type' => MENU_CALLBACK
,
30 'file' => $info['file'],
31 'file path' => $info['path'],
34 $items['panels/ajax/flexible/settings/%panels_edit_cache'] = array(
35 'page callback' => 'panels_ajax_flexible_edit_settings',
37 $items['panels/ajax/flexible/add/%panels_edit_cache'] = array(
38 'page callback' => 'panels_ajax_flexible_edit_add',
40 $items['panels/ajax/flexible/remove/%panels_edit_cache'] = array(
41 'page callback' => 'panels_ajax_flexible_edit_remove',
43 $items['panels/ajax/flexible/resize/%panels_edit_cache'] = array(
44 'page callback' => 'panels_ajax_flexible_edit_resize',
49 * Convert settings from old style to new, or provide defaults for
51 * @param <type> $settings
53 function panels_flexible_convert_settings(&$settings) {
54 if (empty($settings)) {
58 // The 'canvas' is a special row that does not get rendered
59 // normally, but is used to contain the columns.
62 'contains' => 'column',
63 'children' => array('main'),
70 'children' => array('main-row'),
75 'contains' => 'region',
76 'children' => array('center'),
81 'title' => t('Center'),
84 'parent' => 'main-row',
89 else if (!isset($settings['items'])) {
90 // Convert an old style flexible to a new style flexible.
93 $settings['items']['canvas'] = array(
95 'contains' => 'column',
96 'children' => array(),
99 // add the left sidebar column, row and region if it exists.
100 if (!empty($old['sidebars']['left'])) {
101 $settings['items']['canvas']['children'][] = 'sidebar-left';
102 $settings['items']['sidebar-left'] = array(
104 'width' => $old['sidebars']['left_width'],
105 'width_type' => $old['sidebars']['width_type'],
106 'children' => array('sidebar-left-row'),
107 'parent' => 'canvas',
109 $settings['items']['sidebar-left-row'] = array(
111 'contains' => 'region',
112 'children' => array('sidebar_left'),
113 'parent' => 'sidebar-left',
115 $settings['items']['sidebar_left'] = array(
117 'title' => t('Left sidebar'),
120 'parent' => 'sidebar-left-row',
124 $settings['items']['canvas']['children'][] = 'main';
126 if (!empty($old['sidebars']['right'])) {
127 $settings['items']['canvas']['children'][] = 'sidebar-right';
128 $settings['items']['sidebar-right'] = array(
130 'width' => $old['sidebars']['right_width'],
131 'width_type' => $old['sidebars']['width_type'],
132 'children' => array('sidebar-right-row'),
133 'parent' => 'canvas',
135 $settings['items']['sidebar-right-row'] = array(
137 'contains' => 'region',
138 'children' => array('sidebar_right'),
139 'parent' => 'sidebar-right',
141 $settings['items']['sidebar_right'] = array(
143 'title' => t('Right sidebar'),
146 'parent' => 'sidebar-right-row',
150 // Add the main column.
151 $settings['items']['main'] = array(
155 'children' => array(),
156 'parent' => 'canvas',
159 // Add rows and regions.
160 for ($row = 1; $row <= intval($old['rows']); $row++) {
161 // Create entry for the row
162 $settings['items']["row_$row"] = array(
164 'contains' => 'region',
165 'children' => array(),
168 // Add the row to the parent's children
169 $settings['items']['main']['children'][] = "row_$row";
171 for ($col = 1; $col <= intval($old["row_$row"]['columns']); $col++) {
172 // Create entry for the region
173 $settings['items']["row_${row}_$col"] = array(
175 'width' => $old["row_$row"]["width_$col"],
177 'parent' => "row_$row",
179 // Add entry for the region to the row's children
180 $settings['items']["row_$row"]['children'][] = "row_${row}_$col";
182 // Apply the proper title to the region
183 if (!empty($old["row_$row"]['names'][$col - 1])) {
184 $settings['items']["row_${row}_$col"]['title'] = $old["row_$row"]['names'][$col - 1];
187 $settings['items']["row_${row}_$col"]['title'] = t("Row @row, Column @col", array('@row' => $row, '@col' => $col));
192 else if (isset($settings['canvas'])) {
193 // Convert the old 'canvas' to the new canvas row.
194 $settings['items']['canvas'] = array(
196 'contains' => 'column',
197 'children' => $settings['canvas'],
200 unset($settings['canvas']);
205 * Define the actual list of columns and rows for this flexible panel.
207 function panels_flexible_panels($display, $settings) {
209 panels_flexible_convert_settings($settings);
210 foreach ($settings['items'] as
$id => $item) {
211 if ($item['type'] == 'region') {
212 $items[$id] = $item['title'];
220 * Draw the flexible layout.
222 function theme_panels_flexible($id, $content, $settings, $display) {
223 panels_flexible_convert_settings($settings);
225 $renderer = new stdClass
;
226 $renderer->settings
= $settings;
227 $renderer->content
= $content;
228 $renderer->css_id
= $id;
229 $renderer->did
= $display->did
;
230 $renderer->id_str
= $id ?
'id="' .
$id .
'"' : '';
231 $renderer->admin
= FALSE
;
233 // CSS must be generated because it reports back left/middle/right
235 $css = panels_flexible_render_css($renderer);
237 if ($display->did
&& $display->did
!= 'new') {
238 ctools_include('css');
239 // Generate an id based upon rows + columns:
240 $css_id = 'flexible:' .
$display->did
;
241 $filename = ctools_css_retrieve($css_id);
243 $filename = ctools_css_store($css_id, $css, FALSE
);
245 drupal_add_css($filename, 'module', 'all', FALSE
);
248 // If the id is 'new' we can't reliably cache the CSS in the filesystem
249 // because the display does not truly exist, so we'll stick it in the
251 drupal_set_html_head("<style type=\"text/css\">\n$css</style>\n");
254 $output = "<div class=\"panel-flexible panel-flexible-$renderer->did clear-block\" $renderer->id_str>\n";
255 $output .
= "<div class=\"panel-flexible-inside panel-flexible-$renderer->did-inside\">\n";
257 $output .
= panels_flexible_render_items($renderer, $settings['items']['canvas']['children'], 'panel-flexible-' .
$renderer->did
);
259 // Wrap the whole thing up nice and snug
260 $output .
= "</div>\n</div>\n";
266 * Draw the flexible layout.
268 function theme_panels_flexible_admin($id, $content, $settings, $display) {
269 panels_flexible_convert_settings($settings);
271 $renderer = new stdClass
;
272 $renderer->settings
= $settings;
273 $renderer->content
= $content;
274 $renderer->css_id
= $id;
275 $renderer->did
= $display->did
;
276 $renderer->cache_key
= $display->cache_key
;
277 $renderer->id_str
= $id ?
'id="' .
$id .
'"' : '';
278 $renderer->admin
= TRUE
;
280 if ($display->did
&& $display->did
!= 'new') {
281 // Automatically remove any cached CSS for this display since it's
282 // being edited. The next time it's viewed CSS will be auto generated
284 $css_id = 'flexible:' .
$display->did
;
285 ctools_include('css');
286 ctools_css_clear($css_id);
288 $css = panels_flexible_render_css($renderer);
290 // For the administrative view, add CSS directly to head.
291 drupal_set_html_head("<style type=\"text/css\">\n$css</style>\n");
293 $output = '<input type="submit" id="panels-flexible-toggle-layout" value ="' .
294 t('Show layout designer') .
'">';
295 $output .
= "<div class=\"panel-flexible panel-flexible-$renderer->did clear-block panel-flexible-admin panel-flexible-no-edit-layout\" $renderer->id_str>\n";
296 $output .
= "<div class=\"panel-flexible-inside panel-flexible-$renderer->did-inside \">\n";
298 $content = panels_flexible_render_items($renderer, $settings['items']['canvas']['children'], 'panels-flexible-row-' .
$renderer->did .
'-canvas');
299 $output .
= panels_flexible_render_item($renderer, $settings['items']['canvas'], 'panels-flexible-row', $content, 'canvas', 0, 0, TRUE
);
301 // Wrap the whole thing up nice and snug
302 $output .
= "</div>\n</div>\n";
304 drupal_add_js(panels_get_path('plugins/layouts/flexible/flexible-admin.js'));
305 drupal_add_js(array('flexible' => array('resize' => url('panels/ajax/flexible/resize/' .
$display->cache_key
, array('absolute' => TRUE
)))), 'setting');
310 * Render a piece of a flexible layout.
312 function panels_flexible_render_items($renderer, $list, $owner_id) {
314 $groups = array('left' => '', 'middle' => '', 'right' => '');
315 $max = count($list) - 1;
318 foreach ($list as
$position => $id) {
319 $item = $renderer->settings
['items'][$id];
320 $location = isset($renderer->positions
[$id]) ?
$renderer->positions
[$id] : 'middle';
322 if ($renderer->admin
&& $item['type'] != 'row' && $prev ) {
323 $groups[$location] .
= panels_flexible_render_splitter($renderer, $prev, $id);
326 switch ($item['type']) {
328 $content = panels_flexible_render_items($renderer, $item['children'], 'panels-flexible-column-' .
$renderer->did .
'-' .
$id);
329 $groups[$location] .
= panels_flexible_render_item($renderer, $item, 'panels-flexible-column', $content, $id, $position, $max);
332 $content = panels_flexible_render_items($renderer, $item['children'], 'panels-flexible-row-' .
$renderer->did .
'-' .
$id);
333 $groups[$location] .
= panels_flexible_render_item($renderer, $item, 'panels-flexible-row', $content, $id, $position, $max, TRUE
);
336 $content = isset($renderer->content
[$id]) ?
$renderer->content
[$id] : " ";
337 $groups[$location] .
= panels_flexible_render_item($renderer, $item, 'panels-flexible-region', $content, $id, $position, $max);
341 // If all items are fixed then we have a special splitter on the right to
342 // control the overall width.
343 if (!empty($renderer->admin
) && $max == $position && $location == 'left') {
344 $groups[$location] .
= panels_flexible_render_splitter($renderer, $id, NULL
);
349 foreach ($groups as
$position => $content) {
350 if (!empty($content) || $renderer->admin
) {
351 $output .
= '<div class="' .
$owner_id .
'-' .
$position .
'">' .
$content .
'</div>';
359 * Render a column in the flexible layout.
361 function panels_flexible_render_item($renderer, $item, $base, $content, $id, $position, $max, $clear = FALSE
) {
362 $output = '<div class="' .
$base .
' ' .
$base .
'-' .
$renderer->did .
'-' .
$id;
363 if ($position == 0) {
364 $output .
= ' ' .
$base .
'-first';
366 if ($position == $max) {
367 $output .
= ' ' .
$base .
'-last';
370 $output .
= ' clear-block';
373 if (isset($item['class'])) {
374 $output .
= ' ' .
check_plain($item['class']);
377 $output .
= '">' .
"\n";
379 if (!empty($renderer->admin
)) {
380 $output .
= panels_flexible_render_item_links($renderer, $id, $item);
383 $output .
= ' <div class="inside ' .
$base .
'-inside ' .
$base .
'-' .
$renderer->did .
'-' .
$id .
'-inside';
384 if ($position == 0) {
385 $output .
= ' ' .
$base .
'-inside-first';
387 if ($position == $max) {
388 $output .
= ' ' .
$base .
'-inside-last';
391 $output .
= ' clear-block';
396 $output .
= ' </div>' .
"\n";
397 $output .
= '</div>' .
"\n";
402 * Render a splitter div to place between the $left and $right items.
404 * If the right ID is NULL that means there isn't actually a box to the
405 * right, but we need a splitter anyway. We'll mostly use info about the
406 * left, but pretend it's 'fluid' so that the javascript won't actually
407 * modify the right item.
409 function panels_flexible_render_splitter($renderer, $left_id, $right_id) {
410 $left = $renderer->settings
['items'][$left_id];
412 $left_class = 'panels-flexible-' .
$left['type'] .
'-' .
$renderer->did .
'-' .
$left_id;
414 $right = $renderer->settings
['items'][$right_id];
415 $right_class = 'panels-flexible-' .
$right['type'] .
'-' .
$renderer->did .
'-' .
$right_id;
419 $right_class = $left_class;
422 $output = '<div class="panels-flexible-splitter flexible-splitter-for-' .
$left_class .
'">';
424 // Name the left object
425 $output .
= '<span class="panels-flexible-splitter-left">';
426 $output .
= '.' .
$left_class;
427 $output .
= '</span>';
429 $output .
= '<span class="panels-flexible-splitter-left-id">';
431 $output .
= '</span>';
433 $output .
= '<span class="panels-flexible-splitter-left-width ' .
$left_class .
'-width">';
434 $output .
= $left['width'];
435 $output .
= '</span>';
437 $output .
= '<span class="panels-flexible-splitter-left-scale">';
438 $output .
= isset($renderer->scale
[$left_id]) ?
$renderer->scale
[$left_id] : 1;
439 $output .
= '</span>';
441 $output .
= '<span class="panels-flexible-splitter-left-width-type">';
442 $output .
= $left['width_type'];
443 $output .
= '</span>';
445 // Name the right object
446 $output .
= '<span class="panels-flexible-splitter-right">';
447 $output .
= '.' .
$right_class;
448 $output .
= '</span>';
450 $output .
= '<span class="panels-flexible-splitter-right-id">';
451 $output .
= $right_id;
452 $output .
= '</span>';
454 $output .
= '<span class="panels-flexible-splitter-right-width ' .
$right_class .
'-width">';
455 $output .
= $right['width'];
456 $output .
= '</span>';
458 $output .
= '<span class="panels-flexible-splitter-right-scale">';
459 $output .
= isset($renderer->scale
[$right_id]) ?
$renderer->scale
[$right_id] : 1;
460 $output .
= '</span>';
462 $output .
= '<span class="panels-flexible-splitter-right-width-type">';
463 // If there is no right, make it fluid.
464 $output .
= $right_id ?
$right['width_type'] : '%';
465 $output .
= '</span>';
472 * Render the dropdown links for an item.
474 function panels_flexible_render_item_links($renderer, $id, $item) {
478 if ($item['type'] == 'column') {
479 $title = t('Column');
480 $settings = t('Column settings');
481 if (empty($item['children'])) {
482 $remove = t('Remove column');
486 $add = t('Add row to top');
487 $add2 = t('Add row to bottom');
490 else if ($item['type'] == 'row') {
491 if ($id == 'canvas') {
492 $title = t('Canvas');
496 $settings = t('Row settings');
498 if (empty($item['children'])) {
499 if ($item != 'canvas') {
500 $remove = t('Remove row');
502 $add = $item['contains'] == 'region' ?
t('Add region') : t('Add column');
505 $add = $item['contains'] == 'region' ?
t('Add region to left') : t('Add column to left');
506 $add2 = $item['contains'] == 'region' ?
t('Add region to right') : t('Add column to right');
509 else if ($item['type'] == 'region') {
510 $title = t('Region');
511 $settings = t('Region settings');
512 $remove = t('Remove region');
515 if (!empty($settings)) {
517 'title' => $settings,
518 'href' => 'panels/ajax/flexible/settings/' .
$renderer->cache_key .
'/' .
$id,
519 'attributes' => array('class' => 'ctools-use-modal'),
525 'href' => 'panels/ajax/flexible/add/' .
$renderer->cache_key .
'/' .
$id,
526 'attributes' => array('class' => 'ctools-use-modal'),
532 'href' => 'panels/ajax/flexible/add/' .
$renderer->cache_key .
'/' .
$id .
'/right',
533 'attributes' => array('class' => 'ctools-use-modal'),
539 'href' => 'panels/ajax/flexible/remove/' .
$renderer->cache_key .
'/' .
$id,
540 'attributes' => array('class' => 'ctools-use-ajax'),
544 return theme('ctools_dropdown', $title, $links, FALSE
,
545 'flexible-layout-only flexible-links flexible-title flexible-links-' .
$id);
548 * Provide CSS for a flexible layout.
550 function panels_flexible_render_css($renderer) {
551 $parent_class = $renderer->admin ?
'.panels-flexible-row-' .
$renderer->did .
'-canvas' : '.panel-flexible-' .
$renderer->did
;
552 return panels_flexible_render_css_group($renderer, $renderer->settings
['items']['canvas']['children'], $parent_class, 'column');
556 * Render the CSS for a group of items to be displayed together.
558 * Columns and regions, when displayed as a group, need to cooperate in
559 * order to share margins and make sure that percent widths add up
560 * to the right total.
562 function panels_flexible_render_css_group($renderer, $list, $owner_id, $type) {
564 panels_flexible_get_css_group($css, $renderer, $list, $owner_id, $type);
566 ctools_include('css');
567 return ctools_css_assemble($css);
571 * Construct an array with all of the CSS properties for a group.
573 * This will parse down into children and produce all of the CSS needed if you
574 * start from the top.
576 function panels_flexible_get_css_group(&$css, $renderer, $list, $owner_id, $type) {
577 if ($type != 'row') {
578 // Go through our items and break up into right/center/right groups so we
579 // can figure out our offsets.
581 // right == any items on the right that are 'fixed'.
582 // middle == all fluid items.
583 // right == any items on the right that are 'fixed'.
584 $left = $middle = $right = array();
585 $left_total = $right_total = $middle_total = 0;
587 foreach ($list as
$id) {
588 if ($renderer->settings
['items'][$id]['width_type'] == 'px') {
590 if ($current == 'left') {
592 $renderer->positions
[$id] = 'left';
593 $left_total += $renderer->settings
['items'][$id]['width'];
598 $renderer->positions
[$id] = 'right';
599 $right_total += $renderer->settings
['items'][$id]['width'];
604 if ($current != 'right') {
607 $renderer->positions
[$id] = 'middle';
608 $middle_total += $renderer->settings
['items'][$id]['width'];
610 // fall through: if current is 'right' and we ran into a 'fluid' then
611 // it gets *dropped* because that is invalid.
615 // Go through our right sides and create CSS.
616 foreach ($left as
$id) {
617 $class = ".panels-flexible-$type-" .
$renderer->did .
"-$id";
618 $css[$class] = array(
619 'position' => 'relative',
621 'background-color' => 'transparent',
622 'width' => $renderer->settings
['items'][$id]['width'] .
"px",
626 // Do the same for right.
629 foreach ($right as
$id) {
630 $class = ".panels-flexible-$type-" .
$renderer->did .
"-$id";
631 $css[$class] = array(
633 'width' => $renderer->settings
['items'][$id]['width'] .
"px",
637 $max = count($middle) - 1;
640 // Because we love IE so much, auto scale everything to 99%. This
641 // means adding up the actual widths and then providing a multiplier
642 // to each so that the total is 99%.
643 $scale = 99.0 / $middle_total;
644 foreach ($middle as
$position => $id) {
645 $class = ".panels-flexible-$type-" .
$renderer->did .
"-$id";
646 $css[$class] = array(
648 'width' => ($renderer->settings
['items'][$id]['width'] * $scale) .
"%",
651 // Store this so we can use it later.
652 // @todo: Store the scale, not the new width, so .js can adjust
654 $renderer->scale
[$id] = $scale;
658 // If there is any total remaining, we need to offset the splitter
661 $css["$owner_id-left"]['margin-left'] = '-' .
$left_total .
'px';
663 $css["* html $owner_id-left"]['left'] = $left_total .
"px";
665 // Add this even if it's 0 so we can handle removals.
666 $css["$owner_id-inside"]['padding-left'] = $left_total .
'px';
668 $css["$owner_id-right"]['margin-right'] = '-' .
$right_total .
'px';
670 $css["$owner_id-inside"]['padding-right'] = $right_total .
'px';
674 // Go through each item and process children.
675 foreach ($list as
$id) {
676 $item = $renderer->settings
['items'][$id];
677 if (empty($item['children'])) {
681 if ($type == 'column') {
682 // Columns can only contain rows.
686 $child_type = isset($item['contains']) ?
$item['contains'] : 'region';
689 $class = ".panels-flexible-$type-" .
$renderer->did .
"-$id";
690 panels_flexible_get_css_group($css, $renderer, $item['children'], $class, $child_type);
695 * AJAX responder to edit flexible settings for an item.
697 function panels_ajax_flexible_edit_settings($cache, $id) {
698 $settings = &$cache->display
->layout_settings
;
699 panels_flexible_convert_settings($settings);
701 if (empty($settings['items'][$id])) {
702 ctools_modal_render(t('Error'), t('Invalid item id.'));
705 $item = &$settings['items'][$id];
706 $siblings = $settings['items'][$item['parent']]['children'];
708 switch ($item['type']) {
710 $title = t('Configure column');
713 $title = t('Configure row');
716 $title = t('Configure region');
721 'display' => &$cache->display
,
724 'siblings' => $siblings,
725 'settings' => &$settings,
731 $output = ctools_modal_form_wrapper('panels_flexible_config_item_form', $form_state);
732 if (empty($output)) {
733 // If the width type changed then other nearby items will have
734 // to have their widths adjusted.
735 panels_load_include('display-edit');
736 panels_edit_cache_set($cache);
738 // If the item is a region, replace the title.
739 $class = 'panels-flexible-' .
$item['type'] .
'-' .
$cache->display
->did .
'-' .
$id;
740 if ($item['type'] == 'region') {
741 $output[] = ctools_ajax_command_replace(".$class h2.label",
742 '<h2 class="label">' .
check_plain($item['title']) .
'</h2>');
744 // If not a row, reset the css width if necessary.
746 $output[] = ctools_modal_command_dismiss();
749 ctools_ajax_render($output);
753 * Configure a row, column or region on the flexible page.
755 * @param <type> $form_state
758 function panels_flexible_config_item_form(&$form_state) {
759 $display = &$form_state['display'];
760 $item = &$form_state['item'];
761 $siblings = &$form_state['siblings'];
762 $settings = &$form_state['settings'];
763 $id = &$form_state['id'];
765 // $form['#action'] = $form_state['url'];
767 if ($item['type'] == 'region') {
768 $form['title'] = array(
769 '#title' => t('Region title'),
770 '#type' => 'textfield',
771 '#default_value' => $item['title'],
775 $form['class'] = array(
776 '#title' => t('Region class'),
777 '#type' => 'textfield',
778 '#default_value' => isset($item['class']) ?
$item['class'] : '',
779 '#description' => t('Enter a CSS class that will be used for this region. This can be used to apply automatic styling from your theme, for example.'),
783 if ($item['type'] != 'row') {
784 // Test to see if there are fluid items to the left or the right. If there
785 // are fluid items on both sides, this item cannot be set to fixed.
786 $left = $right = FALSE
;
788 foreach ($siblings as
$sibling) {
789 if ($sibling == $id) {
792 else if ($settings['items'][$sibling]['width_type'] == '%') {
793 $
$current = TRUE
; // Indirection.
797 $form['width_type'] = array(
799 '#title' => t('Width'),
800 '#default_value' => $item['width_type'],
809 $form['contains'] = array(
811 '#title' => t('Contains'),
812 '#default_value' => $item['contains'],
814 'region' => t('Regions'),
815 'column' => t('Columns'),
819 if (!empty($item['children'])) {
820 $form['contains']['#disabled'] = TRUE
;
821 $form['contains']['#value'] = $item['contains'];
822 $form['contains']['#description'] = t('You must remove contained items to change the row container type.');
826 $form['save'] = array(
828 '#value' => t('Save'),
835 * Submit handler for editing a flexible item.
837 function panels_flexible_config_item_form_submit(&$form, &$form_state) {
838 $item = &$form_state['item'];
839 if ($item['type'] == 'region') {
840 $item['title'] = $form_state['values']['title'];
841 $item['class'] = $form_state['values']['class'];
844 if ($item['type'] != 'row') {
845 $item['width_type'] = $form_state['values']['width_type'];
848 $item['contains'] = $form_state['values']['contains'];
853 * AJAX responder to add a new row, column or region to a flexible layout.
855 function panels_ajax_flexible_edit_add($cache, $id, $location = 'left') {
856 ctools_include('modal');
857 ctools_include('ajax');
858 $settings = &$cache->display
->layout_settings
;
859 panels_flexible_convert_settings($settings);
861 if (empty($settings['items'][$id])) {
862 ctools_modal_render(t('Error'), t('Invalid item id.'));
865 $parent = &$settings['items'][$id];
867 switch ($parent['type']) {
869 $title = t('Add row');
870 // Create the new item with defaults.
873 'contains' => 'region',
874 'children' => array(),
879 switch ($parent['contains']) {
881 $title = $location == 'left' ?
t('Add region to left') : t('Add region to right');
891 $title = $location == 'left' ?
t('Add column to left') : t('Add column to right');
897 'children' => array(),
901 // Create the new item with defaults.
904 // Cannot add items to regions.
909 'display' => &$cache->display
,
910 'parent' => &$parent,
913 'settings' => &$settings,
916 'location' => $location,
919 $output = ctools_modal_form_wrapper('panels_flexible_add_item_form', $form_state);
920 if (empty($output)) {
921 // If the width type changed then other nearby items will have
922 // to have their widths adjusted.
923 panels_load_include('display-edit');
924 panels_edit_cache_set($cache);
927 $css_id = isset($cache->display
->css_id
) ?
$cache->display
->css_id
: '';
928 // Create a renderer object so we can render our new stuff.
929 $renderer = new stdClass
;
930 $renderer->settings
= $settings;
931 $renderer->content
= array();
932 $renderer->css_id
= $css_id;
933 $renderer->did
= $cache->display
->did
;
934 $renderer->cache_key
= $cache->display
->cache_key
;
935 $renderer->id_str
= $css_id ?
'id="' .
$css_id .
'"' : '';
936 $renderer->admin
= TRUE
;
939 if ($item['type'] == 'region') {
940 panels_load_include('display-edit');
941 $panel_buttons = panels_edit_panel_get_links($cache->display
, $form_state['key']);
943 $content = panels_render_region_dnd('', $form_state['key'], $item['title'], $panel_buttons);
944 // Manually add the hidden field that our region uses to store pane info.
945 $content .
= '<input type="hidden" name="panel[pane][' .
946 $form_state['key'] .
']" id="edit-panel-pane-' .
$form_state['key'] .
'" value="" />';
950 // We need to make sure the left/middle/right divs exist inside this
951 // so that more stuff can be added inside it as needed.
952 foreach (array('left', 'middle', 'right') as
$position) {
953 if (!empty($content) || $renderer->admin
) {
954 $content .
= '<div class="panels-flexible-' .
$item['type'] .
'-' .
$cache->display
->did .
'-' .
$form_state['key'] .
'-' .
$position .
'"></div>';
961 $parent_class = 'panels-flexible-' .
$parent['type'] .
'-' .
$cache->display
->did .
'-' .
$id;
962 $item_output = panels_flexible_render_item($renderer, $item, 'panels-flexible-' .
$item['type'], $content, $form_state['key'], 0, 0, $item['type'] == 'row');
964 // Get all the CSS necessary for the entire row (as width adjustments may
967 panels_flexible_get_css_group($css, $renderer, $parent['children'], '.' .
$parent_class, $item['type']);
969 $position = isset($renderer->positions
[$form_state['key']]) ?
$renderer->positions
[$form_state['key']] : 'middle';
970 // If there's a nearby item, add the splitter and rewrite the width
971 // of the nearby item as it probably got adjusted.
972 // The blocks of code in this else look very similar but are not actually
973 // duplicated because the order changes based on left or right.
976 if ($location == 'left') {
977 $item_output .
= panels_flexible_render_splitter($renderer, $form_state['key'], $form_state['sibling']);
978 $output[] = ctools_ajax_command_prepend('.' .
$parent_class .
'-left', $item_output);
980 else if ($location == 'right') {
981 // If we are adding to the right side of the left box, there is
982 // a splitter that we have to remove; then we add our box normally,
983 // and then add a new splitter for just our guy.
984 $output[] = ctools_ajax_command_remove('panels-flexible-splitter-for-panels-flexible-' .
$item['type'] .
'-' .
$cache->display
->did .
'-' .
$form_state['key']);
985 $item_output = panels_flexible_render_splitter($renderer, $form_state['sibling'], $form_state['key']) .
$item_output;
986 $item_output .
= panels_flexible_render_splitter($renderer, $form_state['key'], NULL
);
987 $output[] = ctools_ajax_command_append('.' .
$parent_class .
'-left', $item_output);
991 if (!empty($form_state['sibling'])) {
992 $item_output = panels_flexible_render_splitter($renderer, $form_state['sibling'], $form_state['key']) .
$item_output;
994 $output[] = ctools_ajax_command_append('.' .
$parent_class .
'-right', $item_output);
997 if ($location == 'left') {
998 if (!empty($form_state['sibling'])) {
999 $item_output .
= panels_flexible_render_splitter($renderer, $form_state['key'], $form_state['sibling']);
1001 $output[] = ctools_ajax_command_prepend('.' .
$parent_class .
'-middle', $item_output);
1004 if (!empty($form_state['sibling'])) {
1005 $item_output = panels_flexible_render_splitter($renderer, $form_state['sibling'], $form_state['key']) .
$item_output;
1007 $output[] = ctools_ajax_command_append('.' .
$parent_class .
'-middle', $item_output);
1013 // Send our fix height command.
1014 $output[] = array('command' => 'flexible_fix_height');
1016 if (!empty($form_state['sibling'])) {
1017 $sibling_width = '.panels-flexible-' .
$item['type'] .
'-' .
$cache->display
->did .
'-' .
$form_state['sibling'] .
'-width';
1018 $output[] = ctools_ajax_command_html($sibling_width, $settings['items'][$form_state['sibling']]['width']);
1020 foreach ($css as
$selector => $data) {
1021 $output[] = ctools_ajax_command_css($selector, $data);
1024 // Rerender our parent item links:
1025 $output[] = ctools_ajax_command_replace('.flexible-links-' .
$id,
1026 panels_flexible_render_item_links($renderer, $id, $parent));
1029 'command' => 'flexible_fix_firstlast',
1030 'selector' => '.' .
$parent_class .
'-inside',
1031 'base' => 'panels-flexible-' .
$item['type'],
1034 $output[] = ctools_modal_command_dismiss();
1037 ctools_ajax_render($output);
1040 * Form to add a row, column or region to a flexible layout.
1041 * @param <type> $form_state
1044 function panels_flexible_add_item_form(&$form_state) {
1045 $display = &$form_state['display'];
1046 $item = &$form_state['item'];
1047 $parent = &$form_state['parent'];
1048 $settings = &$form_state['settings'];
1049 $location = &$form_state['location'];
1050 $id = &$form_state['id'];
1052 // $form['#action'] = $form_state['url'];
1054 if ($item['type'] == 'region') {
1055 $form['title'] = array(
1056 '#title' => t('Region title'),
1057 '#type' => 'textfield',
1058 '#default_value' => $item['title'],
1059 '#required' => TRUE
,
1063 if ($item['type'] != 'row') {
1064 // If there is a 'fixed' type on the side we're adding to, then this
1065 // must also be fixed. Otherwise it can be either and should default to
1069 if (!empty($parent['children'])) {
1070 if ($location == 'left') {
1071 $sibling = reset($parent['children']);
1074 $sibling = end($parent['children']);
1076 if ($settings['items'][$sibling]['width_type'] == 'px') {
1078 $item['width_type'] = 'px';
1082 $form['width_type'] = array(
1083 '#type' => 'select',
1084 '#title' => t('Width'),
1085 '#default_value' => $item['width_type'],
1086 '#options' => array(
1090 '#disabled' => $restrict,
1093 // This forces the value because disabled items don't always send
1095 $form['width_type']['#value'] = $item['width_type'];
1096 $form['width_type']['#description'] = t('Items cannot be set to fluid if there are fixed items already on that side.');
1100 $form['contains'] = array(
1101 '#type' => 'select',
1102 '#title' => t('Contains'),
1103 '#default_value' => $item['contains'],
1104 '#options' => array(
1105 'region' => t('Regions'),
1106 'column' => t('Columns'),
1111 $form['save'] = array(
1112 '#type' => 'submit',
1113 '#value' => t('Save'),
1120 * Submit handler for editing a flexible item.
1122 function panels_flexible_add_item_form_submit(&$form, &$form_state) {
1123 $item = &$form_state['item'];
1124 $parent = &$form_state['parent'];
1125 $location = &$form_state['location'];
1126 $settings = &$form_state['settings'];
1128 if ($item['type'] == 'region') {
1129 $item['title'] = $form_state['values']['title'];
1132 if ($item['type'] != 'row') {
1133 $item['width_type'] = $form_state['values']['width_type'];
1136 $item['contains'] = $form_state['values']['contains'];
1139 if ($item['type'] == 'region') {
1140 // derive the region key from the title
1141 $key = preg_replace("/[^a-z0-9]/", '_', drupal_strtolower($item['title']));
1142 while (isset($settings['items'][$key])) {
1145 $form_state['key'] = $key;
1148 $form_state['key'] = $key = max(array_keys($settings['items'])) + 1;
1151 $form_state['sibling'] = NULL
;
1152 if ($item['type'] != 'row' && !empty($parent['children'])) {
1153 // Figure out what the width should be and adjust our sibling if
1155 if ($location == 'left') {
1156 $form_state['sibling'] = reset($parent['children']);
1159 $form_state['sibling'] = end($parent['children']);
1163 // If there is no sibling, or the sibling is of a different type,
1164 // the default 100 will work for either fixed or fluid.
1165 if ($form_state['sibling'] && $settings['items'][$form_state['sibling']]['width_type'] == $item['width_type']) {
1166 // steal half of the sibling's space.
1167 $width = $settings['items'][$form_state['sibling']]['width'] / 2;
1168 $settings['items'][$form_state['sibling']]['width'] = $width;
1169 $item['width'] = $width;
1174 $settings['items'][$key] = $item;
1175 if ($location == 'left') {
1176 array_unshift($parent['children'], $key);
1179 $parent['children'][] = $key;
1184 * AJAX responder to remove an existing row, column or region from a flexible
1187 function panels_ajax_flexible_edit_remove($cache, $id) {
1188 $settings = &$cache->display
->layout_settings
;
1189 panels_flexible_convert_settings($settings);
1191 if (empty($settings['items'][$id])) {
1192 ctools_ajax_render_error(print_r($settings['items'], 1));
1193 ctools_ajax_render_error(t('Invalid item id.'));
1196 $item = &$settings['items'][$id];
1198 $siblings = &$settings['items'][$item['parent']]['children'];
1199 $parent_class = '.panels-flexible-' .
$settings['items'][$item['parent']]['type'] .
1200 '-' .
$cache->display
->did .
'-' .
$item['parent'];
1202 // Find the offset of our array. This will also be the key because
1203 // this is a simple array.
1204 $offset = array_search($id, $siblings);
1206 // Only bother with this stuff if our item is fluid, since fixed is
1208 if ($item['type'] != 'row') {
1209 if (isset($siblings[$offset + 1])) {
1210 $next = $siblings[$offset + 1];
1212 if (isset($siblings[$offset - 1])) {
1213 $prev = $siblings[$offset - 1];
1216 if ($item['width_type'] == '%') {
1218 if (isset($next) && $settings['items'][$next]['width_type'] == '%') {
1219 $settings['items'][$next]['width'] += $item['width'];
1221 // If that failed, try the previous one.
1222 else if (isset($prev) && $settings['items'][$prev]['width_type'] == '%') {
1223 $settings['items'][$prev]['width'] += $item['width'];
1226 // Not sure what happens if they both failed. Maybe nothing.
1230 array_splice($siblings, $offset, 1);
1232 unset($settings['items'][$id]);
1234 // Save our new state.
1235 panels_load_include('display-edit');
1236 panels_edit_cache_set($cache);
1237 $class = 'panels-flexible-' .
$item['type'] .
'-' .
$cache->display
->did .
'-' .
$id;
1240 $output[] = ctools_ajax_command_remove('.' .
$class);
1242 $css_id = isset($cache->display
->css_id
) ?
$cache->display
->css_id
: '';
1243 // Create a renderer object so we can render our new stuff.
1244 $renderer = new stdClass
;
1245 $renderer->settings
= $settings;
1246 $renderer->content
= array();
1247 $renderer->css_id
= $css_id;
1248 $renderer->did
= $cache->display
->did
;
1249 $renderer->cache_key
= $cache->display
->cache_key
;
1250 $renderer->id_str
= $css_id ?
'id="' .
$css_id .
'"' : '';
1251 $renderer->admin
= TRUE
;
1253 // Regenerate the CSS for siblings.
1254 if (!empty($siblings)) {
1255 // Get all the CSS necessary for the entire row (as width adjustments may
1258 panels_flexible_get_css_group($css, $renderer, $siblings, $parent_class, $item['type']);
1259 foreach ($css as
$selector => $data) {
1260 $output[] = ctools_ajax_command_css($selector, $data);
1264 // There are potentially two splitters linked to this item to be removed.
1265 if (!empty($prev)) {
1266 $output[] = ctools_ajax_command_remove('.flexible-splitter-for-panels-flexible-' .
$item['type'] .
'-' .
$cache->display
->did .
'-' .
$prev);
1269 // Try to remove the 'next' one even if there isn't a $next.
1270 $output[] = ctools_ajax_command_remove('.flexible-splitter-for-panels-flexible-' .
$item['type'] .
'-' .
$cache->display
->did .
'-' .
$id);
1272 if (!empty($prev) && !empty($next)) {
1273 // Add a new splitter that links $prev and $next:
1274 $splitter = panels_flexible_render_splitter($renderer, $prev, $next);
1275 $prev_class = '.panels-flexible-' .
$item['type'] .
'-' .
$cache->display
->did .
'-' .
$prev;
1276 $output[] = ctools_ajax_command_after($prev_class, $splitter);
1277 // Send our fix height command.
1278 $output[] = array('command' => 'flexible_fix_height');
1280 // Rerender our parent item links:
1281 $output[] = ctools_ajax_command_replace('.flexible-links-' .
$item['parent'],
1282 panels_flexible_render_item_links($renderer, $item['parent'], $settings['items'][$item['parent']]));
1285 'command' => 'flexible_fix_firstlast',
1286 'selector' => $parent_class .
'-inside',
1287 'base' => 'panels-flexible-' .
$item['type'],
1290 ctools_ajax_render($output);
1294 * AJAX responder to store resize information when the user adjusts the
1297 function panels_ajax_flexible_edit_resize($cache) {
1298 ctools_include('ajax');
1299 $settings = &$cache->display
->layout_settings
;
1300 panels_flexible_convert_settings($settings);
1302 $settings['items'][$_POST['left']]['width'] = $_POST['left_width'];
1303 if (!empty($_POST['right']) && $_POST['right'] != $_POST['left']) {
1304 $settings['items'][$_POST['right']]['width'] = $_POST['right_width'];
1307 // Save our new state.
1308 panels_load_include('display-edit');
1309 panels_edit_cache_set($cache);
1311 ctools_ajax_render(array('ok'));