| [ Index ] |
|
Code source de Horde 3.1.3 |
1 <?php 2 3 require_once 'Horde/Array.php'; 4 5 /** 6 * The Horde_Block_Layout_Manager class allows manipulation of 7 * Horde_Block layouts. 8 * 9 * $Horde: framework/Block/Block/Layout/Manager.php,v 1.4.2.3 2006/03/08 17:54:08 chuck Exp $ 10 * 11 * Copyright 2003-2006 Mike Cochrane <mike@graftonhall.co.nz> 12 * Copyright 2003-2006 Jan Schneider <jan@horde.org> 13 * 14 * See the enclosed file COPYING for license information (LGPL). If you 15 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. 16 * 17 * @author Mike Cochrane <mike@graftonhall.co.nz> 18 * @author Jan Schneider <jan@horde.org> 19 * @since Horde 3.2 20 * @package Horde_Block 21 */ 22 class Horde_Block_Layout_Manager { 23 24 /** 25 * Our Horde_Block_Collection instance. 26 * 27 * @var Horde_Block_Collection 28 */ 29 var $_collection; 30 31 /** 32 * The current block layout. 33 * 34 * @var array 35 */ 36 var $_layout = array(); 37 38 /** 39 * A cache for the block objects. 40 * 41 * @var array 42 */ 43 var $_blocks = array(); 44 45 /** 46 * The maximum number of columns. 47 * 48 * @var integer 49 */ 50 var $_columns = 0; 51 52 /** 53 * Has the layout been updated since it was instantiated. 54 * 55 * @var boolean 56 */ 57 var $_updated = false; 58 59 /** 60 * The current block (array: [row, col]). 61 * 62 * @var array 63 */ 64 var $_currentBlock = array(null, null); 65 66 /** 67 * The new row of the last changed block. 68 * 69 * @var integer 70 */ 71 var $_changed_row = null; 72 73 /** 74 * The new column of the last changed block. 75 * 76 * @var integer 77 */ 78 var $_changed_col = null; 79 80 /** 81 * Constructor. 82 * 83 * @param Horde_Block_Collection &$collection 84 * @param string $data 85 */ 86 function Horde_Block_Layout_Manager(&$collection, $data = '') 87 { 88 $this->_collection = &$collection; 89 $this->unserialize($data); 90 91 // Fill the _covered caches and empty rows. 92 $rows = count($this->_layout); 93 $empty = array(); 94 for ($row = 0; $row < $rows; $row++) { 95 $cols = count($this->_layout[$row]); 96 if (!isset($empty[$row])) { 97 $empty[$row] = true; 98 } 99 for ($col = 0; $col < $cols; $col++) { 100 if (!isset($this->_layout[$row][$col])) { 101 $this->_blocks[$row][$col] = PEAR::raiseError(_("No block exists at the requested position"), 'horde.error'); 102 } elseif (is_array($this->_layout[$row][$col])) { 103 $field = $this->_layout[$row][$col]; 104 105 $empty[$row] = false; 106 if (isset($field['width'])) { 107 for ($i = 1; $i < $field['width']; $i++) { 108 $this->_layout[$row][$col + $i] = 'covered'; 109 } 110 } 111 if (isset($field['height'])) { 112 if (!isset($field['width'])) { 113 $field['width'] = 1; 114 } 115 for ($i = 1; $i < $field['height']; $i++) { 116 $this->_layout[$row + $i][$col] = 'covered'; 117 for ($j = 1; $j < $field['width']; $j++) { 118 $this->_layout[$row + $i][$col + $j] = 'covered'; 119 } 120 $empty[$row + $i] = false; 121 } 122 } 123 } 124 } 125 126 // Strip empty blocks from the end of the rows. 127 for ($col = $cols - 1; $col >= 0; $col--) { 128 if (!isset($this->_layout[$row][$col]) || 129 $this->_layout[$row][$col] == 'empty') { 130 unset($this->_layout[$row][$col]); 131 } else { 132 break; 133 } 134 } 135 136 $this->_columns = max($this->_columns, count($this->_layout[$row])); 137 } 138 139 // Fill all rows up to the same length. 140 $layout = array(); 141 for ($row = 0; $row < $rows; $row++) { 142 $cols = count($this->_layout[$row]); 143 if ($cols < $this->_columns) { 144 for ($col = $cols; $col < $this->_columns; $col++) { 145 $this->_layout[$row][$col] = 'empty'; 146 } 147 } 148 $layout[] = $this->_layout[$row]; 149 } 150 151 $this->_layout = $layout; 152 } 153 154 /** 155 * Serialize and return the current block layout. 156 */ 157 function serialize() 158 { 159 return serialize($this->_layout); 160 } 161 162 /** 163 * Resets the current layout to the value stored in the preferences. 164 */ 165 function unserialize($data) 166 { 167 $this->_layout = @unserialize($data); 168 } 169 170 /** 171 * Returns a single instance of the Horde_Block_Layout_Manager class. 172 * 173 * @param string $name 174 * @param Horde_Block_Collection &$collection 175 * @param string $data 176 * 177 * @static 178 * 179 * @return Horde_Block_Layout_Manager The Horde_Block_Layout_Manager instance. 180 */ 181 function &singleton($name, &$collection, $data = '') 182 { 183 static $instances = array(); 184 if (!isset($instances[$name])) { 185 $instances[$name] = new Horde_Block_Layout_Manager($collection, $data); 186 } 187 return $instances[$name]; 188 } 189 190 /** 191 * Process a modification to the current layout. 192 * 193 * @var string $action 194 * @var integer $row 195 * @var integer $col 196 */ 197 function handle($action, $row, $col) 198 { 199 switch ($action) { 200 case 'moveUp': 201 case 'moveDown': 202 case 'moveLeft': 203 case 'moveRight': 204 case 'expandUp': 205 case 'expandDown': 206 case 'expandLeft': 207 case 'expandRight': 208 case 'shrinkLeft': 209 case 'shrinkRight': 210 case 'shrinkUp': 211 case 'shrinkDown': 212 case 'removeBlock': 213 $result = call_user_func(array(&$this, $action), $row, $col); 214 if (is_a($result, 'PEAR_Error')) { 215 $GLOBALS['notification']->push($result); 216 } else { 217 $this->_updated = true; 218 } 219 break; 220 221 // Save the changes made to a block. 222 case 'save': 223 // Save the changes made to a block and continue editing. 224 case 'save-resume': 225 // Get requested block type. 226 list($newapp, $newtype) = explode(':', Util::getFormData('app')); 227 228 // Is this a new block? 229 $new = false; 230 if ($this->isEmpty($row, $col) || 231 !$this->rowExists($row) || 232 !$this->colExists($col)) { 233 // Check permissions. 234 if (Horde::hasPermission('max_blocks') !== true && 235 Horde::hasPermission('max_blocks') <= $this->count()) { 236 $message = @htmlspecialchars(sprintf(_("You are not allowed to create more than %d blocks."), Horde::hasPermission('max_blocks')), ENT_COMPAT, NLS::getCharset()); 237 if (!empty($conf['hooks']['permsdenied'])) { 238 $message = Horde::callHook('_perms_hook_denied', array('horde:max_blocks'), 'horde', $message); 239 } 240 $GLOBALS['notification']->push($message, 'horde.error', array('content.raw')); 241 break; 242 } 243 244 $new = true; 245 // Make sure there is somewhere to put it. 246 $this->addBlock($row, $col); 247 } 248 249 // Or an existing one? 250 $exists = false; 251 $changed = false; 252 if (!$new) { 253 // Get target block info. 254 $info = $this->getBlockInfo($row, $col); 255 $exists = $this->isBlock($row, $col); 256 // Has a different block been selected? 257 if ($exists && 258 ($info['app'] != $newapp || 259 $info['block'] != $newtype)) { 260 $changed = true; 261 } 262 } 263 264 if ($new || $changed) { 265 // Change app or type. 266 $info = array(); 267 $info['app'] = $newapp; 268 $info['block'] = $newtype; 269 $params = $this->_collection->getParams($newapp, $newtype); 270 foreach ($params as $newparam) { 271 $info['params'][$newparam] = $this->_collection->getDefaultValue($newapp, $newtype, $newparam); 272 } 273 $this->setBlockInfo($row, $col, $info); 274 } elseif ($exists) { 275 // Change values. 276 $this->setBlockInfo($row, $col, array('params' => Util::getFormData('params', array()))); 277 } 278 $this->_updated = true; 279 if ($action == 'save') { 280 break; 281 } 282 283 // Make a block the current block for editing. 284 case 'edit': 285 $this->_currentBlock = array($row, $col); 286 break; 287 } 288 } 289 290 /** 291 * Has the layout been changed since it was instantiated? 292 * 293 * @return boolean 294 */ 295 function updated() 296 { 297 return $this->_updated; 298 } 299 300 /** 301 * Get the current block row and column. 302 * 303 * @return array [row, col] 304 */ 305 function getCurrentBlock() 306 { 307 return $this->_currentBlock; 308 } 309 310 /** 311 * Returns the Horde_Block at the specified position. 312 * 313 * @param integer $row A layout row. 314 * @param integer $col A layout column. 315 * 316 * @return Horde_Block The block from that position or a PEAR_Error 317 * if something went wrong. 318 */ 319 function &getBlock($row, $col) 320 { 321 if (!isset($this->_blocks[$row][$col])) { 322 $field = $this->_layout[$row][$col]; 323 $this->_blocks[$row][$col] = 324 &Horde_Block_Collection::getBlock($field['app'], $field['params']['type'], $field['params']['params']); 325 } 326 327 return $this->_blocks[$row][$col]; 328 } 329 330 /** 331 * Returns the coordinates of the block covering the specified 332 * field. 333 * 334 * @param integer $row A layout row. 335 * @param integer $col A layout column. 336 * 337 * @return array The top-left row-column-coordinate of the block 338 * covering the specified field or null if the field 339 * is empty. 340 */ 341 function getBlockAt($row, $col) 342 { 343 /* Trivial cases first. */ 344 if ($this->isEmpty($row, $col)) { 345 return null; 346 } elseif (!$this->isCovered($row, $col)) { 347 return array($row, $col); 348 } 349 350 /* This is a covered field. */ 351 for ($test = $row - 1; $test >= 0; $test--) { 352 if (!$this->isCovered($test, $col) && 353 !$this->isEmpty($test, $col) && 354 $test + $this->getHeight($test, $col) - 1 == $row) { 355 return array($test, $col); 356 } 357 } 358 for ($test = $col - 1; $test >= 0; $test--) { 359 if (!$this->isCovered($row, $test) && 360 !$this->isEmpty($test, $col) && 361 $test + $this->getWidth($row, $test) - 1 == $col) { 362 return array($row, $test); 363 } 364 } 365 } 366 367 /** 368 * Returns a hash with some useful information about the specified 369 * block. 370 * 371 * Returned hash values: 372 * 'app': application name 373 * 'block': block name 374 * 'params': parameter hash 375 * 376 * @param integer $row A layout row. 377 * @param integer $col A layout column. 378 * 379 * @return array The information hash. 380 */ 381 function getBlockInfo($row, $col) 382 { 383 if (!isset($this->_layout[$row][$col]) || 384 $this->_layout[$row][$col] == 'empty') { 385 return PEAR::raiseError(_("No block exists at the requested position"), 'horde.error'); 386 } 387 388 return array('app' => $this->_layout[$row][$col]['app'], 389 'block' => $this->_layout[$row][$col]['params']['type'], 390 'params' => $this->_layout[$row][$col]['params']['params']); 391 } 392 393 /** 394 * Sets a batch of information about the specified block. 395 * 396 * @param integer $row A layout row. 397 * @param integer $col A layout column. 398 * @param array $info A hash with information values. 399 * Possible elements are: 400 * 'app': application name 401 * 'block': block name 402 * 'params': parameter hash 403 */ 404 function setBlockInfo($row, $col, $info = array()) 405 { 406 if (!isset($this->_layout[$row][$col])) { 407 return PEAR::raiseError(_("No block exists at the requested position"), 'horde.error'); 408 } 409 410 if (isset($info['app'])) { 411 $this->_layout[$row][$col]['app'] = $info['app']; 412 } 413 if (isset($info['block'])) { 414 $this->_layout[$row][$col]['params']['type'] = $info['block']; 415 } 416 if (isset($info['params'])) { 417 $this->_layout[$row][$col]['params']['params'] = $info['params']; 418 } 419 420 $this->_changed_row = $row; 421 $this->_changed_col = $col; 422 } 423 424 /** 425 * Returns the number of blocks in the current layout. 426 * 427 * @since Horde 3.1 428 * 429 * @return integer The number of blocks. 430 */ 431 function count() 432 { 433 $rows = $this->rows(); 434 $count = 0; 435 436 for ($row = 0; $row < $rows; $row++) { 437 $cols = $this->columns($row); 438 for ($col = 0; $col < $cols; $col++) { 439 if (!$this->isEmpty($row, $col) && 440 !$this->isCovered($row, $col)) { 441 $count++; 442 } 443 } 444 } 445 446 return $count; 447 } 448 449 /** 450 * Returns the number of rows in the current layout. 451 * 452 * @return integer The number of rows. 453 */ 454 function rows() 455 { 456 return count($this->_layout); 457 } 458 459 /** 460 * Returns the number of columns in the specified row of the 461 * current layout. 462 * 463 * @param integer $row The row to return the number of columns from. 464 * 465 * @return integer The number of columns. 466 */ 467 function columns($row) 468 { 469 if (!isset($this->_layout[$row])) { 470 return PEAR::raiseError(sprintf(_("The specified row (%d) does not exist."), $row), 'horde.error'); 471 } 472 return count($this->_layout[$row]); 473 } 474 475 /** 476 * Checks to see if a given location if being used by a block 477 * 478 * @param integer $row A layout row. 479 * @param integer $col A layout column. 480 * 481 * @return boolean True if the location is empty 482 * False is the location is being used. 483 */ 484 function isEmpty($row, $col) 485 { 486 return !isset($this->_layout[$row][$col]) || $this->_layout[$row][$col] == 'empty'; 487 } 488 489 /** 490 * Returns if the field at the specified position is covered by 491 * another block. 492 * 493 * @param integer $row A layout row. 494 * @param integer $col A layout column. 495 * 496 * @return boolean True if the specified field is covered. 497 */ 498 function isCovered($row, $col) 499 { 500 return isset($this->_layout[$row][$col]) ? $this->_layout[$row][$col] == 'covered' : false; 501 } 502 503 /** 504 * Returns if the specified location is the top left field of 505 * a block. 506 * 507 * @param integer $row A layout row. 508 * @param integer $col A layout column. 509 * 510 * @return boolean True if the specified position is a block, false if 511 * the field doesn't exist, is empty or covered. 512 */ 513 function isBlock($row, $col) 514 { 515 return $this->rowExists($row) && $this->colExists($col) && 516 !$this->isEmpty($row, $col) && !$this->isCovered($row, $col); 517 } 518 519 /** 520 * Returns if the specified block has been changed last. 521 * 522 * @param integer $row A layout row. 523 * @param integer $col A layout column. 524 * 525 * @return boolean True if this block is the last one that was changed. 526 */ 527 function isChanged($row, $col) 528 { 529 return $this->_changed_row === $row && $this->_changed_col === $col; 530 } 531 532 /** 533 * Returns if the specified block may be removed. 534 * 535 * 536 * @param integer $row A layout row. 537 * @param integer $col A layout column. 538 * 539 * @return boolean True if this block may be removed. 540 */ 541 function isRemovable($row, $col) 542 { 543 global $conf; 544 545 $app = $this->_layout[$row][$col]['app']; 546 $type = $this->_layout[$row][$col]['params']['type']; 547 $block = $app . ':' . $type; 548 549 /* Check if the block is a fixed block. */ 550 if (!in_array($block, $conf['portal']['fixed_blocks'])) { 551 return true; 552 } 553 554 /* Check if we have still another block of the same type. */ 555 $found = false; 556 foreach ($this->_layout as $cur_row) { 557 foreach ($cur_row as $cur_col) { 558 if (isset($cur_col['app']) && 559 $cur_col['app'] == $app && 560 $cur_col['params']['type'] == $type) { 561 if ($found) { 562 return true; 563 } else { 564 $found = true; 565 } 566 } 567 } 568 } 569 570 return false; 571 } 572 573 /** 574 * Returns an URL triggering an action to a block 575 * 576 * @param string $action An action to trigger. 577 * @param integer $row A layout row. 578 * @param integer $col A layout column. 579 * 580 * @return string An URL with all necessary parameters. 581 */ 582 function getActionUrl($action, $row, $col) 583 { 584 $url = Util::addParameter(Horde::selfUrl(), 585 array('col' => $col, 586 'row' => $row, 587 'action' => $action, 588 'nocache' => base_convert(microtime(), 10, 36))); 589 return $url . '#block'; 590 } 591 592 /** 593 * Returns a control (linked arrow) for a certain action on the 594 * specified block. 595 * 596 * @param string $type A control type in the form 597 * "modification/direction". Possible values for 598 * modification: expand, shrink, move. Possible values 599 * for direction: up, down, left, right. 600 * @param integer $row A layout row. 601 * @param integer $col A layout column. 602 * 603 * @return string A link containing an arrow representing the requested 604 * control. 605 */ 606 function getControl($type, $row, $col) 607 { 608 $type = explode('/', $type); 609 $action = $type[0] . ucfirst($type[1]); 610 $url = $this->getActionUrl($action, $row, $col); 611 612 switch ($type[0]) { 613 case 'expand': 614 $title = _("Expand"); 615 $img = 'large_' . $type[1]; 616 break; 617 618 case 'shrink': 619 $title = _("Shrink"); 620 $img = 'large_'; 621 622 switch ($type[1]) { 623 case 'up': 624 $img .= 'down'; 625 break; 626 627 case 'down': 628 $img .= 'up'; 629 break; 630 631 case 'left': 632 $img .= 'right'; 633 break; 634 635 case 'right': 636 $img .= 'left'; 637 break; 638 } 639 break; 640 641 case 'move': 642 switch ($type[1]) { 643 case 'up': 644 $title = _("Move Up"); 645 break; 646 647 case 'down': 648 $title = _("Move Down"); 649 break; 650 651 case 'left': 652 $title = _("Move Left"); 653 break; 654 655 case 'right': 656 $title = _("Move Right"); 657 break; 658 } 659 660 $img = $type[1]; 661 break; 662 } 663 664 return Horde::link($url, $title) . 665 Horde::img('block/' . $img . '.png', $title, '', $GLOBALS['registry']->getImageDir('horde')) . '</a>'; 666 } 667 668 /** 669 * Does a row exist? 670 * 671 * @param integer $row The row to look for 672 * 673 * @return boolean True if the row exists 674 */ 675 function rowExists($row) 676 { 677 return $row < count($this->_layout); 678 } 679 680 /** 681 * Does a column exist? 682 * 683 * @param integer $col The column to look for 684 * 685 * @return boolean True if the column exists 686 */ 687 function colExists($col) 688 { 689 return $col < $this->_columns; 690 } 691 692 /** 693 * Get the width of the block at a given location. 694 * This returns the width if there is a block at this 695 * location, otherwise returns 1. 696 * 697 * @param integer $row A layout row. 698 * @param integer $col A layout column. 699 * 700 * @return integer The number of columns this block spans 701 */ 702 function getWidth($row, $col) 703 { 704 if (!is_array($this->_layout[$row][$col])) { 705 return 1; 706 } 707 if (!isset($this->_layout[$row][$col]['width'])) { 708 $this->_layout[$row][$col]['width'] = 1; 709 } 710 return $this->_layout[$row][$col]['width']; 711 } 712 713 /** 714 * Get the height of the block at a given location. 715 * This returns the height if there is a block at this 716 * location, otherwise returns 1. 717 * 718 * @param integer $row A layout row. 719 * @param integer $col A layout column. 720 * 721 * @return integer The number of rows this block spans 722 */ 723 function getHeight($row, $col) 724 { 725 if (!is_array($this->_layout[$row][$col])) { 726 return 1; 727 } 728 if (!isset($this->_layout[$row][$col]['height'])) { 729 $this->_layout[$row][$col]['height'] = 1; 730 } 731 return $this->_layout[$row][$col]['height']; 732 } 733 734 /** 735 * Adds an empty block at the specified position. 736 * 737 * @param integer $row A layout row. 738 * @param integer $col A layout column. 739 */ 740 function addBlock($row, $col) 741 { 742 if (!$this->rowExists($row)) { 743 $this->addRow($row); 744 } 745 if (!$this->colExists($col)) { 746 $this->addCol($col); 747 } 748 749 $this->_layout[$row][$col] = array('app' => null, 750 'height' => 1, 751 'width' => 1, 752 'params' => array('type' => null, 753 'params' => array())); 754 } 755 756 /** 757 * Adds a new row to the layout. 758 * 759 * @param integer $row The number of the row to add 760 */ 761 function addRow($row) 762 { 763 if ($this->_columns > 0) { 764 $this->_layout[$row] = array_fill(0, $this->_columns, 'empty'); 765 } 766 } 767 768 /** 769 * Adds a new column to the layout. 770 * 771 * @param integer $col The number of the column to add 772 */ 773 function addCol($col) 774 { 775 foreach ($this->_layout as $id => $val) { 776 $this->_layout[$id][$col] = 'empty'; 777 } 778 $this->_columns++; 779 } 780 781 /** 782 * Removes a block. 783 * 784 * @param integer $row A layout row. 785 * @param integer $col A layout column. 786 */ 787 function removeBlock($row, $col) 788 { 789 $width = $this->getWidth($row, $col); 790 $height = $this->getHeight($row, $col); 791 for ($i = $height - 1; $i >= 0; $i--) { 792 for ($j = $width - 1; $j >= 0; $j--) { 793 $this->_layout[$row + $i][$col + $j] = 'empty'; 794 if (!$this->colExists($col + $j + 1)) { 795 $this->removeColIfEmpty($col + $j); 796 } 797 } 798 if (!$this->rowExists($row + $i + 1) && $this->rowExists($row + $i)) { 799 $this->removeRowIfEmpty($row + $i); 800 } 801 } 802 803 $this->_changed_row = $row; 804 $this->_changed_col = $col; 805 806 if (!$this->rowExists($row)) { 807 $row--; 808 while ($row >= 0 && $this->removeRowIfEmpty($row)) { 809 $row--; 810 } 811 } 812 if (!$this->colExists($col)) { 813 $col--; 814 while ($col >= 0 && $this->removeColIfEmpty($col)) { 815 $col--; 816 } 817 } 818 } 819 820 /** 821 * Removes a row if it's empty. 822 * 823 * @param integer $row The number of the row to to check 824 * 825 * @return boolean True if the row is now removed. 826 * False if the row still exists. 827 */ 828 function removeRowIfEmpty($row) 829 { 830 if (!$this->rowExists($row)) { 831 return true; 832 } 833 834 $rows = count($this->_layout[$row]); 835 for ($i = 0; $i < $rows; $i++) { 836 if (isset($this->_layout[$row][$i]) && $this->_layout[$row][$i] != 'empty') { 837 return false; 838 } 839 } 840 unset($this->_layout[$row]); 841 842 return true; 843 } 844 845 /** 846 * Removes a column if it's empty. 847 * 848 * @param integer $col The number of the column to to check 849 * 850 * @return boolean True if the column is now removed. 851 * False if the column still exists. 852 */ 853 function removeColIfEmpty($col) 854 { 855 if (!$this->colExists($col)) { 856 return true; 857 } 858 859 $cols = count($this->_layout); 860 for ($i = 0; $i < $cols; $i++) { 861 if (isset($this->_layout[$i][$col]) && $this->_layout[$i][$col] != 'empty') { 862 return false; 863 } 864 } 865 866 for ($i = 0; $i < $cols; $i++) { 867 unset($this->_layout[$i][$col]); 868 } 869 870 return true; 871 } 872 873 /** 874 * Moves a block one row up. 875 * 876 * @param integer $row A layout row. 877 * @param integer $col A layout column. 878 */ 879 function moveUp($row, $col) 880 { 881 if ($this->rowExists($row - 1)) { 882 $width = $this->getWidth($row, $col); 883 // See if there's room to move into 884 for ($i = 0; $i < $width; $i++) { 885 if (!$this->isEmpty($row - 1, $col + $i)) { 886 $in_way = $this->getBlockAt($row - 1, $col + $i); 887 if (!is_null($in_way) && 888 $in_way[1] == $col && 889 $this->getWidth($in_way[0], $in_way[1]) == $width) { 890 // We need to swap the blocks. 891 $rec1 = Horde_Array::getRectangle($this->_layout, $row, $col, 892 $this->getHeight($row, $col), $this->getWidth($row, $col)); 893 $rec2 = Horde_Array::getRectangle($this->_layout, $in_way[0], $in_way[1], 894 $this->getHeight($in_way[0], $in_way[1]), $this->getWidth($in_way[0], $in_way[1])); 895 for ($j = 0; $j < count($rec1); $j++) { 896 for ($k = 0; $k < count($rec1[$j]); $k++) { 897 $this->_layout[$in_way[0] + $j][$in_way[1] + $k] = $rec1[$j][$k]; 898 } 899 } 900 for ($j = 0; $j < count($rec2); $j++) { 901 for ($k = 0; $k < count($rec2[$j]); $k++) { 902 $this->_layout[$in_way[0] + count($rec1) + $j][$in_way[1] + $k] = $rec2[$j][$k]; 903 } 904 } 905 $this->_changed_row = $in_way[0]; 906 $this->_changed_col = $in_way[1]; 907 return; 908 } 909 // Nowhere to go. 910 return PEAR::raiseError(_("Shrink or move neighbouring block(s) out of the way first"), 'horde.warning'); 911 } 912 } 913 914 $lastrow = $row + $this->getHeight($row, $col) - 1; 915 for ($i = 0; $i < $width; $i++) { 916 $prev = $this->_layout[$row][$col + $i]; 917 // Move top edge 918 $this->_layout[$row - 1][$col + $i] = $prev; 919 $this->_layout[$row][$col + $i] = 'covered'; 920 // Move bottom edge 921 $this->_layout[$lastrow][$col + $i] = 'empty'; 922 } 923 924 if (!$this->rowExists($lastrow + 1)) { 925 // Was on the bottom row 926 $this->removeRowIfEmpty($lastrow); 927 } 928 } 929 930 $this->_changed_row = $row - 1; 931 $this->_changed_col = $col; 932 } 933 934 /** 935 * Moves a block one row down. 936 * 937 * @param integer $row A layout row. 938 * @param integer $col A layout column. 939 */ 940 function moveDown($row, $col) 941 { 942 $width = $this->getWidth($row, $col); 943 $lastrow = $row + $this->getHeight($row, $col); 944 if ($this->rowExists($lastrow)) { 945 // See if there's room to move into 946 for ($i = 0; $i < $width; $i++) { 947 if (!$this->isEmpty($lastrow, $col + $i)) { 948 $in_way = $this->getBlockAt($lastrow, $col + $i); 949 if (!is_null($in_way) && 950 $in_way[1] == $col && 951 $this->getWidth($in_way[0], $in_way[1]) == $width) { 952 // We need to swap the blocks. 953 $rec1 = Horde_Array::getRectangle($this->_layout, $row, $col, 954 $this->getHeight($row, $col), $this->getWidth($row, $col)); 955 $rec2 = Horde_Array::getRectangle($this->_layout, $in_way[0], $in_way[1], 956 $this->getHeight($in_way[0], $in_way[1]), $this->getWidth($in_way[0], $in_way[1])); 957 for ($j = 0; $j < count($rec2); $j++) { 958 for ($k = 0; $k < count($rec2[$j]); $k++) { 959 $this->_layout[$row + $j][$col + $k] = $rec2[$j][$k]; 960 } 961 } 962 for ($j = 0; $j < count($rec1); $j++) { 963 for ($k = 0; $k < count($rec1[$j]); $k++) { 964 $this->_layout[$row + count($rec2) + $j][$col + $k] = $rec1[$j][$k]; 965 } 966 } 967 $this->_changed_row = $in_way[0]; 968 $this->_changed_col = $in_way[1]; 969 return; 970 } 971 // No where to go 972 return PEAR::raiseError(_("Shrink or move neighbouring block(s) out of the way first"), 'horde.warning'); 973 } 974 } 975 } else { 976 // Make room to move into 977 $this->addRow($lastrow); 978 } 979 980 for ($i = 0; $i < $width; $i++) { 981 $prev = $this->_layout[$row][$col + $i]; 982 // Move bottom edge 983 $this->_layout[$lastrow][$col + $i] = 'covered'; 984 // Move top edge 985 $this->_layout[$row + 1][$col + $i] = $prev; 986 $this->_layout[$row][$col + $i] = 'empty'; 987 } 988 989 $this->_changed_row = $row + 1; 990 $this->_changed_col = $col; 991 } 992 993 /** 994 * Moves all blocks below a certain row one row down. 995 * 996 * @param integer $row A layout row. 997 * 998 * @return boolean True if all rows could be moved down. 999 */ 1000 function moveDownBelow($row) 1001 { 1002 $moved = array(); 1003 for ($y = count($this->_layout) - 1; $y > $row; $y--) { 1004 for ($x = 0; $x < $this->_columns; $x++) { 1005 $block = $this->getBlockAt($y, $x); 1006 if (empty($block)) { 1007 continue; 1008 } 1009 if (empty($moved[$block[1] . ':' . $block[0]])) { 1010 $moved[$block[1] . ':' . $block[0]] = true; 1011 $result = $this->moveDown($block[0], $block[1]); 1012 if (is_a($result, 'PEAR_Error')) { 1013 return false; 1014 } 1015 } 1016 } 1017 } 1018 return true; 1019 } 1020 1021 /** 1022 * Moves a block one column left. 1023 * 1024 * @param integer $row A layout row. 1025 * @param integer $col A layout column. 1026 */ 1027 function moveLeft($row, $col) 1028 { 1029 if ($this->colExists($col - 1)) { 1030 $height = $this->getHeight($row, $col); 1031 // See if there's room to move into. 1032 for ($i = 0; $i < $height; $i++) { 1033 if (!$this->isEmpty($row + $i, $col - 1)) { 1034 $in_way = $this->getBlockAt($row + $i, $col - 1); 1035 if (!is_null($in_way) && 1036 $in_way[0] == $row && 1037 $this->getHeight($in_way[0], $in_way[1]) == $height) { 1038 // We need to swap the blocks. 1039 $rec1 = Horde_Array::getRectangle($this->_layout, $row, $col, 1040 $this->getHeight($row, $col), $this->getWidth($row, $col)); 1041 $rec2 = Horde_Array::getRectangle($this->_layout, $in_way[0], $in_way[1], 1042 $this->getHeight($in_way[0], $in_way[1]), $this->getWidth($in_way[0], $in_way[1])); 1043 for ($j = 0; $j < count($rec1); $j++) { 1044 for ($k = 0; $k < count($rec1[$j]); $k++) { 1045 $this->_layout[$in_way[0] + $j][$in_way[1] + $k] = $rec1[$j][$k]; 1046 } 1047 } 1048 for ($j = 0; $j < count($rec2); $j++) { 1049 for ($k = 0; $k < count($rec2[$j]); $k++) { 1050 $this->_layout[$in_way[0] + $j][$in_way[1] + count($rec1[$j]) + $k] = $rec2[$j][$k]; 1051 } 1052 } 1053 $this->_changed_row = $in_way[0]; 1054 $this->_changed_col = $in_way[1]; 1055 return; 1056 } 1057 // No where to go 1058 return PEAR::raiseError(_("Shrink or move neighbouring block(s) out of the way first"), 'horde.warning'); 1059 } 1060 } 1061 1062 $lastcol = $col + $this->getWidth($row, $col) - 1; 1063 for ($i = 0; $i < $height; $i++) { 1064 $prev = $this->_layout[$row + $i][$col]; 1065 // Move left hand edge 1066 $this->_layout[$row + $i][$col - 1] = $prev; 1067 $this->_layout[$row + $i][$col] = 'covered'; 1068 // Move right hand edge 1069 $this->_layout[$row + $i][$lastcol] = 'empty'; 1070 } 1071 1072 if (!$this->colExists($lastcol + 1)) { 1073 // Was on the right-most column 1074 $this->removeColIfEmpty($lastcol); 1075 } 1076 1077 $this->_changed_row = $row; 1078 $this->_changed_col = $col - 1; 1079 } 1080 } 1081 1082 /** 1083 * Moves a block one column right. 1084 * 1085 * @param integer $row A layout row. 1086 * @param integer $col A layout column. 1087 */ 1088 function moveRight($row, $col) 1089 { 1090 $height = $this->getHeight($row, $col); 1091 $lastcol = $col + $this->getWidth($row, $col); 1092 if ($this->colExists($lastcol)) { 1093 // See if there's room to move into. 1094 for ($i = 0; $i < $height; $i++) { 1095 if (!$this->isEmpty($row + $i, $lastcol)) { 1096 $in_way = $this->getBlockAt($row + $i, $lastcol); 1097 if (!is_null($in_way) && 1098 $in_way[0] == $row && 1099 $this->getHeight($in_way[0], $in_way[1]) == $height) { 1100 // We need to swap the blocks. 1101 $rec1 = Horde_Array::getRectangle($this->_layout, $row, $col, 1102 $this->getHeight($row, $col), $this->getWidth($row, $col)); 1103 $rec2 = Horde_Array::getRectangle($this->_layout, $in_way[0], $in_way[1], 1104 $this->getHeight($in_way[0], $in_way[1]), $this->getWidth($in_way[0], $in_way[1])); 1105 for ($j = 0; $j < count($rec2); $j++) { 1106 for ($k = 0; $k < count($rec2[$j]); $k++) { 1107 $this->_layout[$row + $j][$col + $k] = $rec2[$j][$k]; 1108 } 1109 } 1110 for ($j = 0; $j < count($rec1); $j++) { 1111 for ($k = 0; $k < count($rec1[$j]); $k++) { 1112 $this->_layout[$row + $j][$col + count($rec2[$j]) + $k] = $rec1[$j][$k]; 1113 } 1114 } 1115 $this->_changed_row = $in_way[0]; 1116 $this->_changed_col = $in_way[1]; 1117 return; 1118 } 1119 // No where to go 1120 return PEAR::raiseError(_("Shrink or move neighbouring block(s) out of the way first"), 'horde.warning'); 1121 } 1122 } 1123 } else { 1124 // Make room to move into. 1125 $this->addCol($lastcol); 1126 } 1127 1128 for ($i = 0; $i < $height; $i++) { 1129 $prev = $this->_layout[$row + $i][$col]; 1130 // Move right hand edge 1131 $this->_layout[$row + $i][$lastcol] = 'covered'; 1132 // Move left hand edge 1133 $this->_layout[$row + $i][$col + 1] = $prev; 1134 $this->_layout[$row + $i][$col] = 'empty'; 1135 } 1136 1137 $this->_changed_row = $row; 1138 $this->_changed_col = $col + 1; 1139 } 1140 1141 /** 1142 * Moves all blocks after a certain column one column right. 1143 * 1144 * @param integer $col A layout column. 1145 * 1146 * @return boolean True if all columns could be moved right. 1147 */ 1148 function moveRightAfter($col) 1149 { 1150 $moved = array(); 1151 for ($x = $this->_columns - 1; $x > $col; $x--) { 1152 for ($y = 0; $y < count($this->_layout); $y++) { 1153 $block = $this->getBlockAt($y, $x); 1154 if (empty($block)) { 1155 continue; 1156 } 1157 if (empty($moved[$block[1] . ':' . $block[0]])) { 1158 $moved[$block[1] . ':' . $block[0]] = true; 1159 $result = $this->moveRight($block[0], $block[1]); 1160 if (is_a($result, 'PEAR_Error')) { 1161 return false; 1162 } 1163 } 1164 } 1165 } 1166 return true; 1167 } 1168 1169 /** 1170 * Makes a block one row taller by moving the top up. 1171 * 1172 * @param integer $row A layout row. 1173 * @param integer $col A layout column. 1174 */ 1175 function expandUp($row, $col) 1176 { 1177 if ($this->rowExists($row - 1)) { 1178 $width = $this->getWidth($row, $col); 1179 // See if there's room to expand into 1180 for ($i = 0; $i < $width; $i++) { 1181 if (!$this->isEmpty($row - 1, $col + $i)) { 1182 if (!$this->moveDownBelow($row - 1)) { 1183 return PEAR::raiseError(_("Shrink or move neighbouring block(s) out of the way first"), 'horde.warning'); 1184 } else { 1185 $row++; 1186 } 1187 } 1188 } 1189 1190 for ($i = 0; $i < $width; $i++) { 1191 $this->_layout[$row - 1][$col + $i] = $this->_layout[$row][$col + $i]; 1192 $this->_layout[$row][$col + $i] = 'covered'; 1193 } 1194 $this->_layout[$row - 1][$col]['height'] = $this->getHeight($row - 1, $col) + 1; 1195 1196 $this->_changed_row = $row - 1; 1197 $this->_changed_col = $col; 1198 } 1199 } 1200 1201 /** 1202 * Makes a block one row taller by moving the bottom down. 1203 * 1204 * @param integer $row A layout row. 1205 * @param integer $col A layout column. 1206 */ 1207 function expandDown($row, $col) 1208 { 1209 $width = $this->getWidth($row, $col); 1210 $lastrow = $row + $this->getHeight($row, $col) - 1; 1211 if (!$this->rowExists($lastrow + 1)) { 1212 // Add a new row. 1213 $this->addRow($lastrow + 1); 1214 for ($i = 0; $i < $width; $i++) { 1215 $this->_layout[$lastrow + 1][$col + $i] = 'covered'; 1216 } 1217 $this->_layout[$row][$col]['height'] = $this->getHeight($row, $col) + 1; 1218 } else { 1219 // See if there's room to expand into 1220 for ($i = 0; $i < $width; $i++) { 1221 if (!$this->isEmpty($lastrow + 1, $col + $i)) { 1222 if (!$this->moveDownBelow($lastrow)) { 1223 return PEAR::raiseError(_("Shrink or move neighbouring block(s) out of the way first"), 'horde.warning'); 1224 } 1225 } 1226 } 1227 1228 for ($i = 0; $i < $width; $i++) { 1229 $this->_layout[$lastrow + 1][$col + $i] = 'covered'; 1230 } 1231 $this->_layout[$row][$col]['height'] = $this->getHeight($row, $col) + 1; 1232 } 1233 1234 $this->_changed_row = $row; 1235 $this->_changed_col = $col; 1236 } 1237 1238 /** 1239 * Makes a block one column wider by moving the left side out. 1240 * 1241 * @param integer $row A layout row. 1242 * @param integer $col A layout column. 1243 */ 1244 function expandLeft($row, $col) 1245 { 1246 if ($this->colExists($col - 1)) { 1247 $height = $this->getHeight($row, $col); 1248 // See if there's room to expand into 1249 for ($i = 0; $i < $height; $i++) { 1250 if (!$this->isEmpty($row + $i, $col - 1)) { 1251 if (!$this->moveRightAfter($col - 1)) { 1252 return PEAR::raiseError(_("Shrink or move neighbouring block(s) out of the way first"), 'horde.warning'); 1253 } else { 1254 $col++; 1255 } 1256 } 1257 } 1258 1259 for ($i = 0; $i < $height; $i++) { 1260 $this->_layout[$row + $i][$col - 1] = $this->_layout[$row + $i][$col]; 1261 $this->_layout[$row + $i][$col] = 'covered'; 1262 } 1263 $this->_layout[$row][$col - 1]['width'] = $this->getWidth($row, $col - 1) + 1; 1264 1265 $this->_changed_row = $row; 1266 $this->_changed_col = $col - 1; 1267 } 1268 } 1269 1270 /** 1271 * Makes a block one column wider by moving the right side out. 1272 * 1273 * @param integer $row A layout row. 1274 * @param integer $col A layout column. 1275 */ 1276 function expandRight($row, $col) 1277 { 1278 $height = $this->getHeight($row, $col); 1279 $lastcol = $col + $this->getWidth($row, $col) - 1; 1280 if ($this->colExists($lastcol + 1)) { 1281 // See if there's room to expand into 1282 for ($i = 0; $i < $height; $i++) { 1283 if (!$this->isEmpty($row + $i, $lastcol + 1)) { 1284 if (!$this->moveRightAfter($lastcol)) { 1285 return PEAR::raiseError(_("Shrink or move neighbouring block(s) out of the way first"), 'horde.warning'); 1286 } 1287 } 1288 } 1289 1290 for ($i = 0; $i < $height; $i++) { 1291 $this->_layout[$row + $i][$lastcol + 1] = 'covered'; 1292 } 1293 $this->_layout[$row][$col]['width'] = $this->getWidth($row, $col) + 1; 1294 } else { 1295 // Add new column 1296 $this->addCol($lastcol + 1); 1297 for ($i = 0; $i < $height; $i++) { 1298 $this->_layout[$row + $i][$lastcol + 1] = 'covered'; 1299 } 1300 $this->_layout[$row][$col]['width'] = $this->getWidth($row, $col) + 1; 1301 } 1302 1303 $this->_changed_row = $row; 1304 $this->_changed_col = $col; 1305 } 1306 1307 /** 1308 * Makes a block one row lower by moving the top down. 1309 * 1310 * @param integer $row A layout row. 1311 * @param integer $col A layout column. 1312 */ 1313 function shrinkUp($row, $col) 1314 { 1315 if ($this->getHeight($row, $col) > 1) { 1316 $width = $this->getWidth($row, $col); 1317 for ($i = 0; $i < $width; $i++) { 1318 $this->_layout[$row + 1][$col + $i] = $this->_layout[$row][$col + $i]; 1319 $this->_layout[$row][$col + $i] = 'empty'; 1320 } 1321 $this->_layout[$row + 1][$col]['height'] = $this->getHeight($row + 1, $col) - 1; 1322 1323 $this->_changed_row = $row + 1; 1324 $this->_changed_col = $col; 1325 } 1326 } 1327 1328 /** 1329 * Makes a block one row lower by moving the bottom up. 1330 * 1331 * @param integer $row A layout row. 1332 * @param integer $col A layout column. 1333 */ 1334 function shrinkDown($row, $col) 1335 { 1336 if ($this->getHeight($row, $col) > 1) { 1337 $lastrow = $row + $this->getHeight($row, $col) - 1; 1338 $width = $this->getWidth($row, $col); 1339 for ($i = 0; $i < $width; $i++) { 1340 $this->_layout[$lastrow][$col + $i] = 'empty'; 1341 } 1342 $this->_layout[$row][$col]['height'] = $this->getHeight($row, $col) - 1; 1343 if (!$this->rowExists($lastrow + 1)) { 1344 // Was on the bottom row 1345 $this->removeRowIfEmpty($lastrow); 1346 } 1347 1348 $this->_changed_row = $row; 1349 $this->_changed_col = $col; 1350 } 1351 } 1352 1353 /** 1354 * Makes a block one column narrower by moving the left side in. 1355 * 1356 * @param integer $row A layout row. 1357 * @param integer $col A layout column. 1358 */ 1359 function shrinkLeft($row, $col) 1360 { 1361 if ($this->getWidth($row, $col) > 1) { 1362 $height = $this->getHeight($row, $col); 1363 for ($i = 0; $i < $height; $i++) { 1364 $this->_layout[$row + $i][$col + 1] = $this->_layout[$row + $i][$col]; 1365 $this->_layout[$row + $i][$col] = 'empty'; 1366 } 1367 $this->_layout[$row][$col + 1]['width'] = $this->getWidth($row, $col + 1) - 1; 1368 1369 $this->_changed_row = $row; 1370 $this->_changed_col = $col + 1; 1371 } 1372 } 1373 1374 /** 1375 * Makes a block one column narrower by moving the right side in. 1376 * 1377 * @param integer $row A layout row. 1378 * @param integer $col A layout column. 1379 */ 1380 function shrinkRight($row, $col) 1381 { 1382 if ($this->getWidth($row, $col) > 1) { 1383 $lastcol = $col + $this->getWidth($row, $col) - 1; 1384 $height = $this->getHeight($row, $col); 1385 for ($i = 0; $i < $height; $i++) { 1386 $this->_layout[$row + $i][$lastcol] = 'empty'; 1387 } 1388 $this->_layout[$row][$col]['width'] = $this->getWidth($row, $col) - 1; 1389 $this->removeColIfEmpty($lastcol); 1390 1391 $this->_changed_row = $row; 1392 $this->_changed_col = $col; 1393 } 1394 } 1395 1396 }
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
| Généré le : Sun Feb 25 18:01:28 2007 | par Balluche grâce à PHPXref 0.7 |