gridlayout.cpp

Go to the documentation of this file.
00001 /*
00002   libwt - Vassilis Virvilis Toolkit - a widget library
00003   Copyright (C) 2006 Vassilis Virvilis <vasvir2@fastmail.fm>
00004  
00005   This library is free software; you can redistribute it and/or
00006   modify it under the terms of the GNU Lesser General Public
00007   License as published by the Free Software Foundation; either
00008   version 2.1 of the License, or (at your option) any later version.
00009   
00010   This library is distributed in the hope that it will be useful,
00011   but WITHOUT ANY WARRANTY; without even the implied warranty of
00012   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013   Lesser General Public License for more details.
00014   
00015   You should have received a copy of the GNU Lesser General Public
00016   License along with this library; if not, write to the
00017   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00018   Boston, MA  02111-1307, SA.
00019 */
00020 
00021 #include <wt/gridlayout.h>
00022 #include <wt/widget.h>
00023 
00024 namespace Wt {
00025 
00026 void adjust_cell_size(int li_size, int sum_size, int cell_span,
00027                       int edge, std::vector<int>& max_size) {
00028     const int delta = li_size - sum_size;
00029     if (delta > 0) {
00030         // adjust all min width
00031         const int div = delta / cell_span;
00032         const int modulo = delta % cell_span;
00033 
00034         for (int d = 0; d < cell_span; d++) {
00035             max_size[edge + d] += div + ((d < modulo) ? 1 : 0);
00036         }
00037     }
00038 }
00039 
00040 void GridLayout::init() {
00041     gridSizeChanged.connect(sigc::mem_fun(*this, &GridLayout::onGridSizeChange));
00042 }
00043 
00044 GridLayout::GridLayout(Widget *parent, int margin, int spacing,
00045                        const std::string &name) :
00046 GridLayoutBase(parent, margin, spacing, name) {
00047     init();
00048 }
00049 
00050 GridLayout::GridLayout(Layout * parent_layout,int margin,
00051                        int spacing,const std::string &name) :
00052 GridLayoutBase(parent_layout, margin, spacing, name) {
00053     init();
00054 }
00055 
00056 GridLayout::GridLayout(int spacing, const std::string &name) :
00057 GridLayoutBase(spacing, name) {
00058     init();
00059 }
00060 
00061 void GridLayout::onGridSizeChange(int rows_old,
00062                                   int cols_old, int rows, int cols) {
00063     trace("gridlayout",
00064           "GridSizeChange: from %dx%d --> %dx%d\n",
00065           rows_old, cols_old, rows, cols);
00066 
00067     if (!rows || !cols)
00068         return;
00069 
00070     row_stretch.resize(rows);
00071     column_stretch.resize(cols);
00072 
00073     max_min_width.resize(cols);
00074     max_min_height.resize(rows);
00075     max_hint_width.resize(cols);
00076     max_hint_height.resize(rows);
00077     row_empty.resize(rows);
00078     col_empty.resize(cols);
00079 
00080     may_grow_horizontally.resize(cols);
00081     may_grow_vertically.resize(rows);
00082     expanding_horizontally.resize(cols);
00083     expanding_vertically.resize(rows);
00084 
00085     for (int r = rows_old; r < rows; r++) {
00086         row_stretch[r] = 0;
00087         max_min_height[r] = 0;
00088         max_hint_height[r] = 0;
00089         row_empty[r] = true;
00090         may_grow_vertically[r] = false;
00091         expanding_vertically[r] = false;
00092     }
00093 
00094     for (int c = 0; c < cols_old; c++) {
00095         column_stretch[c] = 0;
00096         max_min_width[c] = 0;
00097         max_hint_width[c] = 0;
00098         col_empty[c] = true;
00099         may_grow_horizontally[c] = false;
00100         expanding_horizontally[c] = false;
00101     }
00102 }
00103 
00104 /// let's calc again sizeHint, minimumSize etc
00105 void GridLayout::validate() {
00106     int hint_w = 0, hint_h = 0;
00107     int min_w = 0, min_h = 0;
00108     int row_count = 0, col_count = 0;
00109     int r, c;
00110     GridLayout& self = *this;
00111     int sp = spacing;
00112 
00113     trace("gridlayout") << this << " GridLayout::invalidate()" << std::endl;
00114 
00115     for (c = 0; c < numCols(); c++) {
00116         max_min_width[c] = 0;
00117         max_hint_width[c] = 0;
00118         col_empty[c] = true;
00119         may_grow_horizontally[c] = false;
00120         expanding_horizontally[c] = false;
00121     }
00122 
00123     for (r = 0; r < numRows(); r++) {
00124         max_min_height[r] = 0;
00125         max_hint_height[r] = 0;
00126         row_empty[r] = true;
00127         may_grow_vertically[r] = false;
00128         expanding_vertically[r] = false;
00129 
00130         for (c = 0; c < numCols(); c++) {
00131             const LayoutItem *li = self(r, c);
00132             const MatrixLayoutItemValue& v = self(r, c);
00133 
00134             // skip multicells
00135             if (li->isNonEmpty() && !v.isMultiCell()) {
00136                 max_min_width[c] = std::max(max_min_width[c], li->minimumSize().width());
00137                 max_min_height[r] = std::max(max_min_height[r], li->minimumSize().height());
00138                 max_hint_width[c] = std::max(max_hint_width[c], li->sizeHint().width());
00139                 max_hint_height[r] = std::max(max_hint_height[r], li->sizeHint().height());
00140             }
00141             if (li->isNonEmpty()) {
00142                 row_empty[r] = false;
00143                 col_empty[c] = false;
00144                 may_grow_horizontally[c] = may_grow_horizontally[c] ||
00145                                            li->sizePolicy().mayGrowHorizontally();
00146                 may_grow_vertically[r] = may_grow_vertically[r] ||
00147                                          li->sizePolicy().mayGrowVertically();
00148                 expanding_horizontally[c] = expanding_horizontally[c] ||
00149                                             li->sizePolicy().expandingHorizontally();
00150                 expanding_vertically[r] = expanding_vertically[r] ||
00151                                           li->sizePolicy().expandingVertically();
00152             }
00153         }
00154     }
00155 
00156     // for all multicells
00157     for (r = 0; r < numRows(); r++) {
00158         for (c = 0; c < numCols(); c++) {
00159             const LayoutItem *li = self(r, c);
00160             const MatrixLayoutItemValue& v = self(r, c);
00161             if (li->isNonEmpty() && v.isMultiCell() && !v.isReference()) {
00162                 int dr, dc;
00163                 int min_width_sum = 0, hint_width_sum = 0;
00164                 int min_height_sum = 0, hint_height_sum = 0;
00165                 // first mark the relevent row and columns non-empty
00166                 // also let's count the relevent sizes
00167                 for (dr = 0; dr < v.rowSpan(); dr++) {
00168                     row_empty[r + dr] = false;
00169                     min_height_sum += max_min_height[r + dr];
00170                     hint_height_sum += max_hint_height[r + dr];
00171                 }
00172 
00173                 for (dc = 0; dc < v.columnSpan(); dc++) {
00174                     col_empty[c + dc] = false;
00175                     min_width_sum += max_min_width[c + dc];
00176                     hint_width_sum += max_hint_width[c + dc];
00177                 }
00178                 // for rows and columns for hint and min sizes
00179                 adjust_cell_size(li->minimumSize().width(), min_width_sum,
00180                                  v.columnSpan(), c, max_min_width);
00181                 adjust_cell_size(li->minimumSize().height(), min_height_sum,
00182                                  v.rowSpan(), r, max_min_height);
00183                 adjust_cell_size(li->sizeHint().width(), hint_width_sum,
00184                                  v.columnSpan(), c, max_hint_width);
00185                 adjust_cell_size(li->sizeHint().height(), hint_height_sum,
00186                                  v.rowSpan(), r, max_hint_height);
00187             }
00188         }
00189     }
00190 
00191     for (r = 0; r < numRows(); r++) {
00192         if (!row_empty[r]) {
00193             row_count++;
00194             min_h += max_min_height[r];
00195             hint_h += max_hint_height[r];
00196         }
00197     }
00198 
00199     for (c = 0; c < numCols(); c++) {
00200         if (!col_empty[c]) {
00201             col_count++;
00202             min_w += max_min_width[c];
00203             hint_w += max_hint_width[c];
00204         }
00205     }
00206 
00207     // don't forget spacing
00208     if (row_count) {
00209         min_h += (row_count - 1) * sp;
00210         hint_h += (row_count - 1) * sp;
00211     }
00212 
00213     if (col_count) {
00214         min_w += (col_count - 1) * sp;
00215         hint_w += (col_count - 1) * sp;
00216     }
00217 
00218     setSizeHint(Size(hint_w, hint_h));
00219     setMinimumSize(Size(min_w, min_h));
00220     Layout::validate();
00221 }
00222 
00223 //! calcs the width of the columns and the height of the rows
00224 /*! it's not funny if we have to do it two times both for width and height
00225 so we are are heavily using the generic names length and edge to
00226 indicate width or height and row or column.
00227 */
00228 void GridLayout::calcEdge(int total_space, bool width_as_primary) {
00229     int expansion;
00230     bool expand;
00231     int stretch_sum = 0;
00232     int may_grow_num = 0;
00233     int unclaimed_space = 0;
00234     int j;
00235 
00236     setWidthasPrimaryLength(width_as_primary);
00237 
00238     std::vector<int>& hint_length = primaryHintLength();
00239     std::vector<int>& min_length = primaryMinLength();
00240     std::vector<int>& effective_stretch = secondaryEffectiveStretch();
00241     std::vector<int>& stretch = secondaryStretch();
00242     std::vector<int>& extra_space = secondaryExtraSpace();
00243     std::vector<int>& length = primaryCellItemLength();
00244     std::vector<int>& container_length = primaryCellLength();
00245 
00246     int edges = secondaryEdges();
00247 
00248     effective_stretch.resize(edges);
00249     extra_space.resize(edges);
00250     length.resize(edges);
00251     container_length.resize(edges);
00252 
00253     trace("gridlayout", "GridLayout:: Available space for %s %d\n",
00254           (isWidthPrimaryLength()) ? "width" : "height", total_space);
00255     expansion = total_space - Layout::primaryLength(sizeHint());
00256     expand = expansion >= 0;
00257     if (!expand) {
00258         expansion = total_space - Layout::primaryLength(minimumSize());
00259     }
00260 
00261     // initialize effective stretch to zero
00262     for (j = 0; j < edges; j++) {
00263         effective_stretch[j] = 0;
00264     }
00265 
00266     // we have to expand
00267     // first check if there are any stretch values
00268     for (j = 0; j < edges; j++) {
00269         int li_stretch = stretch[j];
00270         if (li_stretch && mayGrowPrimally(j)) {
00271             unclaimed_space += preferredLength(j, expand);
00272             stretch_sum += li_stretch;
00273             effective_stretch[j] = li_stretch;
00274             may_grow_num++;
00275         }
00276     }
00277 
00278     // check if we have to split the extra space among expanding
00279     if (!stretch_sum) {
00280         may_grow_num = 0;
00281         unclaimed_space = 0;
00282         for (j = 0; j < edges; j++) {
00283             if (expandingPrimally(j)) {
00284                 unclaimed_space += preferredLength(j, expand);
00285                 effective_stretch[j] = 1;
00286                 may_grow_num++;
00287             }
00288         }
00289         stretch_sum = may_grow_num;
00290     }
00291 
00292     // check if we have to split the extra space among may Grow simply (preferred)
00293     if (!may_grow_num) {
00294         unclaimed_space = 0;
00295         for (j = 0; j < edges; j++) {
00296             if (mayGrowPrimally(j)) {
00297                 unclaimed_space += preferredLength(j, expand);
00298                 effective_stretch[j] = 1;
00299                 may_grow_num++;
00300             }
00301         }
00302         stretch_sum = may_grow_num;
00303     }
00304 
00305     // none of the fuckers grows -- all fixed size
00306     if (!may_grow_num) {
00307         unclaimed_space = 0;
00308         for (j = 0; j < edges; j++) {
00309             int preferred_length = preferredLength(j, expand);
00310             if (preferred_length) {
00311                 unclaimed_space += preferred_length;
00312                 effective_stretch[j] = 1;
00313                 may_grow_num++;
00314             }
00315         }
00316         stretch_sum = may_grow_num;
00317     }
00318 
00319     // Now we will remove offenders until we can distribute evenly
00320     int available_space;
00321     int claimed_space;
00322 restart:
00323     /// \todo find biggest offender not the first one
00324     available_space = expansion + unclaimed_space;
00325     claimed_space = 0;
00326     for (j = 0; j < edges; j++) {
00327         if (effective_stretch[j]) {
00328             int li_p = (effective_stretch[j] *
00329                         available_space) / stretch_sum;
00330             int preferred_length = preferredLength(j, expand);
00331             if (li_p < preferred_length) {
00332                 // problem: offender found, remove him
00333                 trace("gridlayout", "Removing offender at pos %d with "
00334                       "stretch %d stretch_sum %d unclaimed_space %d\n",
00335                       j, effective_stretch[j], stretch_sum, unclaimed_space);
00336                 unclaimed_space -= preferred_length;
00337                 stretch_sum -= effective_stretch[j];
00338                 effective_stretch[j] = 0;
00339                 may_grow_num--;
00340                 goto restart;
00341             }
00342             claimed_space += li_p;
00343         }
00344     }
00345 
00346     // now we have to distribute evenly the difference
00347     // that got truncated in the integer division
00348     int rest_space = available_space - claimed_space;
00349     int div = 0;
00350     int modulo = 0;
00351     if (may_grow_num) {
00352         div = rest_space / may_grow_num;
00353         modulo = rest_space % may_grow_num;
00354     }
00355 
00356     int count = 0;
00357     for (j = 0; j < edges; j++) {
00358         if (effective_stretch[j]) {
00359             extra_space[j] = div + (count < modulo) ? 1 : 0;
00360         } else {
00361             extra_space[j] = 0;
00362         }
00363         count++;
00364     }
00365 
00366     // finally calc the size
00367     for (j = 0; j < edges; j++) {
00368         const int space = ( (effective_stretch[j]) ?
00369                             (effective_stretch[j] * available_space) /
00370                             stretch_sum :
00371                             preferredLength(j, expand) ) + extra_space[j];
00372         container_length[j] = space;
00373         length[j] = (mayGrowPrimally(j)) ?
00374                     space : (hint_length[j] <= space) ?
00375                     hint_length[j] : min_length[j];
00376         trace("gridlayout", "GridLayout:: Allocating space "
00377               "(%d) %d for edge %d\n", isWidthPrimaryLength(), space, j);
00378     }
00379 }
00380 
00381 /// fixup all managed widgets geometry
00382 void GridLayout::setGeometry(const Rect& r) {
00383     GridLayoutIterator it(*this);
00384     trace("gridlayout") << this <<  " GridLayout::setGeometry()" << std::endl;
00385 
00386     // store geometry first
00387     Layout::setGeometry(r);
00388     // if smaller than the minSize it is possible that
00389     // we have to reacquire the size to perform the layout
00390     Rect rect = LayoutItem::geometry() >> margin;
00391 
00392     // obvious optimization. If they are empty do nothing
00393     bool empty = true;
00394     for (it.start(); !it.finish(); it++) {
00395         LayoutItem *li = *it;
00396         if (li->isNonEmpty()) {
00397             empty = false;
00398             break;
00399         }
00400     }
00401     if (empty)
00402         return;
00403 
00404     calcEdge(rect.width(), true);
00405     calcEdge(rect.height(), false);
00406 
00407     // finally place them
00408     int covered_y = rect.y();
00409     GridLayout& self = *this;
00410     for (int i = 0; i < numRows(); i++) {
00411         int covered_x = rect.x();
00412         for (int j = 0; j < numCols(); j++) {
00413             LayoutItem *li = self(i, j);
00414             const MatrixLayoutItemValue& v = self(i, j);
00415 
00416             if (!li->isNonEmpty())
00417                 continue;
00418 
00419             // let's count the relevent sizes
00420             int container_width = 0;
00421             int container_height = 0;
00422 
00423             const Size& hint_size = li->sizeHint();
00424             const Size& min_size = li->minimumSize();
00425 
00426             for (int dr = 0; dr < v.rowSpan(); dr++)
00427                 container_height += container_row_height[i + dr];
00428             container_height += (v.rowSpan() - 1) * spacing;
00429 
00430             for (int dc = 0; dc < v.columnSpan(); dc++)
00431                 container_width += container_column_width[j + dc];
00432             container_width += (v.columnSpan() - 1) * spacing;
00433 
00434             const Size& container_size = Size(container_width,
00435                                               container_height);
00436 
00437             const int cell_height = (li->sizePolicy().mayGrowVertically())
00438                                     ? container_size.height()
00439                                     : (hint_size.height() <= container_size.height())
00440                                     ? hint_size.height()
00441                                     : min_size.height();
00442 
00443             const int cell_width = (li->sizePolicy().mayGrowHorizontally())
00444                                    ? container_size.width()
00445                                    : (hint_size.width() <= container_size.width())
00446                                    ? hint_size.width()
00447                                    : min_size.width();
00448 
00449             const Size& cell_size = Size(cell_width, cell_height);
00450 
00451             if (!v.isReference()) {
00452                 trace("gridlayout") << "GridLayout:: layoutItem " << li
00453                 << " in ("  << i << "," << j << ") " << cell_size
00454                 << " in " << container_size << std::endl;
00455                 li->setGeometry(
00456                     Rect::align(Rect(mainWidget()->
00457                                      mapFromParent(Point(covered_x, covered_y)),
00458                                      container_size), cell_size, li->alignment));
00459             }
00460             covered_x += column_width[j] + spacing;
00461         }
00462         covered_y += row_height[i] + spacing;
00463     }
00464 }
00465 
00466 void GridLayout::setRowStretch(int row, int stretch) {
00467     row_stretch[row] = stretch;
00468     postLayoutHintEvent();
00469 }
00470 
00471 void GridLayout::setColStretch(int col, int stretch) {
00472     column_stretch[col] = stretch;
00473     postLayoutHintEvent();
00474 }
00475 
00476 void GridLayout::insert(const LayoutItem *li, int row, int column) {
00477     (*this)(row, column) = li;
00478 }
00479 
00480 void GridLayout::insert(const LayoutItem *li, int row_from,
00481                         int row_to, int column_from, int column_to) {
00482     const int row_span = row_to - row_from + 1;
00483     const int col_span = column_to - column_from + 1;
00484     trace("gridlayout", "Span insertion [%d %d] to [%d %d]\n",
00485           row_from, column_from, row_to, column_to);
00486     for (int r = row_from; r <= row_to; r++) {
00487         for (int c = column_from; c <= column_to; c++) {
00488             MatrixLayoutItemValue value(
00489                 (r == row_from && c == column_from) ?
00490                 MatrixLayoutItemValue::Original :
00491                 MatrixLayoutItemValue::Reference, row_span, col_span);
00492             (*this)(r, c) = MatrixLayoutItemCompound(li, value);
00493         }
00494     }
00495 }
00496 
00497 void GridLayout::addItem(LayoutItem *li) {
00498     // obvious optimization. if exists and it is already in position do nothing
00499     GridLayout::const_iterator it = find(begin(), end(), li);
00500     bool exists = (it != end());
00501 
00502     if (exists && !cellSpanQueue.empty()) {
00503         const CellSpan& cs = cellSpanQueue.front();
00504         const MatrixLayoutItemValue& value = *it;
00505         if (it.row() == cs.rowFrom() && it.column() == cs.columnFrom() &&
00506                 value.rowSpan() == cs.rowSpan() && value.columnSpan() == cs.columnSpan()) {
00507             cellSpanQueue.pop();
00508             return;
00509         }
00510     }
00511 
00512     // remove if already exists. This is used for cell reassigning
00513     if (exists) {
00514         removeItem(li);
00515     }
00516     trace("gridlayout", "GridLayout::addItem layoutItem %p\n", li);
00517     Layout::addItem(li);
00518 
00519     if (!cellSpanQueue.empty()) {
00520         const CellSpan& cs = cellSpanQueue.front();
00521         if (cs.layoutItem() == li) {
00522             insert(cs.layoutItem(), cs.rowFrom(), cs.rowTo(),
00523                    cs.columnFrom(), cs.columnTo());
00524             cellSpanQueue.pop();
00525             return;
00526         }
00527     }
00528     push_back(li);
00529 }
00530 
00531 } // namespace

Generated Fri Jul 28 19:23:00 2006.
Copyright © 1998-2003 by the respective authors.

This document is licensed under the terms of the GNU Free Documentation License and may be freely distributed under the conditions given by this license.