[ Index ]
 

Code source de Horde 3.1.3

Accédez au Source d'autres logiciels libresSoutenez Angelica Josefina !

title

Body

[fermer]

/lib/Horde/Block/Layout/ -> Manager.php (source)

   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  }


Généré le : Sun Feb 25 18:01:28 2007 par Balluche grâce à PHPXref 0.7