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
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.