// @(#)root/gui:$Name: $:$Id: TGTableLayout.cxx,v 1.8 2005/09/05 07:33:37 rdm Exp $
// Author: Brett Viren 04/15/2001
/*************************************************************************
* Copyright (C) 2001, Brett Viren *
* All rights reserved. *
* *
* For the licensing terms see $ROOTSYS/LICENSE. *
* For the list of contributors see $ROOTSYS/README/CREDITS. *
*************************************************************************/
/**************************************************************************
This source is based on Xclass95, a Win95-looking GUI toolkit.
Copyright (C) 1996, 1997 David Barth, Ricky Ralston, Hector Peraza.
Xclass95 is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
**************************************************************************/
/**************************************************************************
The Layout algorithm was largely translated from GTK's gtktable.c
source. That source is also distributed under LGPL.
**************************************************************************/
//////////////////////////////////////////////////////////////////////////
// //
// TGTableLayout //
// //
// A LayoutManager which places child frames in a table. This uses //
// TGTableLayoutHints (not TGLayoutHints). See TGTableLayoutHints //
// for how to use these. This manager works like TGMatrixLayout with //
// the addition that: //
// - Child frames can span more than one column/row. //
// - Child frames can resize with the frame. //
// - Column and row sizes are not fixed nor (optionally) homogeneous. //
// - The number of columns and rows must be fully specified. //
// //
//////////////////////////////////////////////////////////////////////////
#include "TGTableLayout.h"
#include "TGFrame.h"
#include "TList.h"
#include "TMath.h"
#include "Rtypes.h"
#include "Riostream.h"
ClassImp(TGTableLayout)
ClassImp(TGTableLayoutHints)
//______________________________________________________________________________
TGTableLayout::TGTableLayout(TGCompositeFrame *main, UInt_t nrows, UInt_t ncols,
Bool_t homogeneous, Int_t sep, Int_t hints)
{
// TGTableLayout constructor.
// Note:
// - Number of rows first, number of Columns second
// - homogeneous == true means all table cells are the same size,
// set by the widest and the highest child frame.
// - s gives the amount of separation in pixels between cells
// - h are the hints, see TGTableLayoutHints.
fMain = main;
fList = fMain->GetList();
fSep = sep;
fHints = hints;
fNrows = nrows;
fNcols = ncols;
fRow = 0;
fCol = 0;
fHomogeneous = homogeneous;
}
//______________________________________________________________________________
TGTableLayout::~TGTableLayout()
{
// TGTableLayout constructor.
if (fRow) delete [] fRow;
if (fCol) delete [] fCol;
}
//______________________________________________________________________________
void TGTableLayout::FindRowColSizes()
{
// Find the sizes of rows and columns needed to statisfy
// children's layout policies.
// This is equiv to GTK's requisition stage
FindRowColSizesInit();
FindRowColSizesSinglyAttached();
FindRowColSizesHomogeneous();
FindRowColSizesMultiplyAttached();
FindRowColSizesHomogeneous();
}
//______________________________________________________________________________
void TGTableLayout::FindRowColSizesInit()
{
// Initialize values needed to determine the size of rows and columns.
if (fRow) delete [] fRow;
if (fCol) delete [] fCol;
fRow = new TableData_t[fNrows];
fCol = new TableData_t[fNcols];
// Find max of each row and column
UInt_t i;
for (i = 0; i < fNrows; ++i) fRow[i].fDefSize = 0;
for (i = 0; i < fNcols; ++i) fCol[i].fDefSize = 0;
}
//______________________________________________________________________________
void TGTableLayout::FindRowColSizesSinglyAttached()
{
// Determine the size of rows/cols needed for singly attached children.
TIter next(fList);
TGFrameElement *ptr;
while ((ptr = (TGFrameElement *) next())) {
TGTableLayoutHints *layout =
dynamic_cast<TGTableLayoutHints*>(ptr->fLayout);
if (!layout) {
Error("FindRowColSizesSinglyAttached", "didn't get TGTableLayoutHints from %s, layout = 0x%lx",
ptr->fFrame->GetName(), ptr->fLayout);
return;
}
UInt_t col = layout->GetAttachLeft();
if (col == (layout->GetAttachRight() - 1))
fCol[col].fDefSize = TMath::Max(fCol[col].fDefSize,
ptr->fFrame->GetDefaultWidth() +
layout->GetPadLeft() +
layout->GetPadRight());
UInt_t row = layout->GetAttachTop();
if (row == (layout->GetAttachBottom() - 1))
fRow[row].fDefSize = TMath::Max(fRow[row].fDefSize,
ptr->fFrame->GetDefaultHeight() +
layout->GetPadTop() +
layout->GetPadBottom());
}
}
//______________________________________________________________________________
void TGTableLayout::FindRowColSizesHomogeneous()
{
// If the table is homogeneous make sure all col/rows are same
// size as biggest col/row.
if (!fHomogeneous) return;
UInt_t max_width = 0, max_height = 0, col, row;
// find max
for (col = 0; col < fNcols; ++col)
max_width = TMath::Max(max_width,fCol[col].fDefSize);
for (row = 0; row < fNrows; ++row)
max_height = TMath::Max(max_height,fRow[row].fDefSize);
// set max
for (col = 0; col < fNcols; ++col) fCol[col].fDefSize = max_width;
for (row = 0; row < fNrows; ++row) fRow[row].fDefSize = max_height;
}
//______________________________________________________________________________
void TGTableLayout::FindRowColSizesMultiplyAttached()
{
// Checks any children which span multiple col/rows.
TIter next(fList);
TGFrameElement *ptr;
while ((ptr = (TGFrameElement *) next())) {
TGTableLayoutHints *layout =
dynamic_cast<TGTableLayoutHints*>(ptr->fLayout);
if (!layout) {
Error("FindRowColSizesMultiplyAttached", "didn't get TGTableLayoutHints");
return;
}
UInt_t left = layout->GetAttachLeft();
UInt_t right = layout->GetAttachRight();
if (left != right-1) { // child spans multi-columns
UInt_t width = 0, col;
for (col = left; col < right; ++col) width += fCol[col].fDefSize;
// If more space needed, divide space evenly among the columns
UInt_t child_width = ptr->fFrame->GetDefaultWidth() +
layout->GetPadLeft() + layout->GetPadRight();
if (width < child_width) {
width = child_width - width;
for (col = left; col < right; ++col) {
UInt_t extra = width / (right - col);
fCol[col].fDefSize += extra;
width -= extra;
}
}
}
UInt_t top = layout->GetAttachTop();
UInt_t bottom = layout->GetAttachBottom();
if (top != bottom-1) { // child spans multi-rows
UInt_t height = 0, row;
for (row = top; row < bottom; ++row) height += fRow[row].fDefSize;
// If more space needed, divide space evenly among the rows
UInt_t child_height = ptr->fFrame->GetDefaultHeight() +
layout->GetPadTop() + layout->GetPadBottom();
if (height < child_height) {
height = child_height - height;
for (row = top; row < bottom; ++row) {
UInt_t extra = height / (bottom - row);
fRow[row].fDefSize += extra;
height -= extra;
}
}
}
}
}
//______________________________________________________________________________
void TGTableLayout::SetRowColResize(UInt_t real_size, UInt_t nthings,
TableData_t *thing, Bool_t homogeneous)
{
// If main frame is bigger or smaller than all children,
// expand/shrink to fill. This is symmetric under row<-->col
// switching so it is abstracted out to a normal function to save typing.
if (homogeneous) {
UInt_t ind, nexpand = 0, cur_size = 0;
for (ind = 0; ind < nthings; ++ind)
cur_size += thing[ind].fDefSize;
if (cur_size < real_size) {
for (ind = 0; ind < nthings; ++ind)
if (thing[ind].fExpand) { ++ nexpand; break; }
if (nexpand > 0) {
UInt_t size = real_size;
for (ind = 0; ind < nthings; ++ ind) {
UInt_t extra = size / (nthings - ind);
thing[ind].fRealSize = TMath::Max(1U, extra);
size -= extra;
}
}
}
} else {
UInt_t ind, nshrink=0, nexpand=0, size=0;
for (ind = 0; ind < nthings; ++ind) {
size += thing[ind].fDefSize;
if (thing[ind].fExpand) ++ nexpand;
if (thing[ind].fShrink) ++ nshrink;
}
// Did main frame expand?
if ((size < real_size) && (nexpand >= 1)) {
size = real_size - size;
for (ind = 0; ind < nthings; ++ind) {
if (thing[ind].fExpand) {
UInt_t extra = size / nexpand;
thing[ind].fRealSize += extra;
size -= extra;
--nexpand;
}
}
}
// Did main frame shrink?
if (size > real_size) {
UInt_t total_nshrink = nshrink;
UInt_t extra = size - real_size;
while (total_nshrink > 0 && extra > 0) {
nshrink = total_nshrink;
for (ind = 0; ind < nthings; ++ind)
if (thing[ind].fShrink) {
UInt_t size = thing[ind].fRealSize;
thing[ind].fRealSize = TMath::Max(1U,thing[ind].fRealSize - extra / nshrink);
extra -= size - thing[ind].fRealSize;
--nshrink;
if (thing[ind].fRealSize < 2) {
total_nshrink -= 1;
thing[ind].fShrink = kFALSE;
}
}
}
}
} // not homogeneous
}
//______________________________________________________________________________
void TGTableLayout::SetRowColSizes()
{
// This gets the new sizes needed to fit the table to the parent
// frame. To be called after FindRowColSizes.
SetRowColSizesInit();
UInt_t border_width = fMain->GetBorderWidth();
SetRowColResize(fMain->GetWidth() - (fNcols-1)*fSep - 2*border_width,
fNcols, fCol, fHomogeneous);
SetRowColResize(fMain->GetHeight() - (fNrows-1)*fSep - 2*border_width,
fNrows, fRow, fHomogeneous);
}
//______________________________________________________________________________
void TGTableLayout::SetRowColSizesInit()
{
// Initialize rows/cols. By default they do not expand and they
// do shrink. What the children want determine what the rows/cols do.
UInt_t col;
for (col = 0; col < fNcols; ++col) {
fCol[col].fRealSize = fCol[col].fDefSize;
fCol[col].fNeedExpand = kFALSE;
fCol[col].fNeedShrink = kTRUE;
fCol[col].fExpand = kFALSE;
fCol[col].fShrink = kTRUE;
fCol[col].fEmpty = kTRUE;
}
UInt_t row;
for (row = 0; row < fNrows; ++row) {
fRow[row].fRealSize = fRow[row].fDefSize;
fRow[row].fNeedExpand = kFALSE;
fRow[row].fNeedShrink = kTRUE;
fRow[row].fExpand = kFALSE;
fRow[row].fShrink = kTRUE;
fRow[row].fEmpty = kTRUE;
}
// Check single row/col children for expand/shrink-ability
TIter next(fList);
TGFrameElement *ptr;
while ((ptr = (TGFrameElement*) next())) {
TGTableLayoutHints *layout =
dynamic_cast<TGTableLayoutHints*>(ptr->fLayout);
if (!layout) {
Error("SetRowColSizesInit", "didn't get TGTableLayoutHints");
return;
}
ULong_t hints = layout->GetLayoutHints();
// columns
if (layout->GetAttachLeft() == layout->GetAttachRight()-1) {
if (hints & kLHintsExpandX)
fCol[layout->GetAttachLeft()].fExpand = kTRUE;
if (!(hints & kLHintsShrinkX))
fCol[layout->GetAttachLeft()].fShrink = kFALSE;
fCol[layout->GetAttachLeft()].fEmpty = kFALSE;
}
// rows
if (layout->GetAttachTop() == layout->GetAttachBottom()-1) {
if (hints & kLHintsExpandY)
fRow[layout->GetAttachTop()].fExpand = kTRUE;
if (!(hints & kLHintsShrinkY))
fRow[layout->GetAttachTop()].fShrink = kFALSE;
fRow[layout->GetAttachTop()].fEmpty = kFALSE;
}
}
// Do same for children of spanning multiple col/rows
next.Reset();
while ((ptr = (TGFrameElement*) next())) {
TGTableLayoutHints *layout =
dynamic_cast<TGTableLayoutHints*>(ptr->fLayout);
if (!layout) {
Error("SetRowColSizesInit", "didn't get TGTableLayoutHints");
return;
}
ULong_t hints = layout->GetLayoutHints();
// columns
UInt_t left = layout->GetAttachLeft();
UInt_t right = layout->GetAttachRight();
if (left != right - 1) {
for (col = left; col < right; ++col) fCol[col].fEmpty = kFALSE;
Bool_t has_expand=kFALSE, has_shrink=kTRUE;
if (hints & kLHintsExpandX) {
for (col = left; col < right; ++col)
if (fCol[col].fExpand) { has_expand = kTRUE; break; }
if (!has_expand)
for (col = left; col < right; ++col)
fCol[col].fNeedExpand = kTRUE;
}
if (!(hints & kLHintsShrinkX)) {
for (col = left; col < right; ++col)
if (!fCol[col].fShrink) { has_shrink = kFALSE; break;}
if (has_shrink)
for (col = left; col < right; ++col)
fCol[col].fNeedShrink = kFALSE;
}
}
// rows
UInt_t top = layout->GetAttachTop();
UInt_t bottom = layout->GetAttachBottom();
if (top != bottom - 1) {
for (row = top; row < bottom; ++row) fRow[row].fEmpty = kFALSE;
Bool_t has_expand=kFALSE, has_shrink=kTRUE;
if (hints & kLHintsExpandY) {
for (row = top; row < bottom; ++row)
if (fRow[row].fExpand) { has_expand = kTRUE; break; }
if (!has_expand)
for (row = top; row < bottom; ++row)
fRow[row].fNeedExpand = kTRUE;
}
if (!(hints & kLHintsShrinkY)) {
for (row = top; row < bottom; ++row)
if (!fRow[row].fShrink) { has_shrink = kFALSE; break;}
if (has_shrink)
for (row = top; row < bottom; ++row)
fRow[row].fNeedShrink = kFALSE;
}
}
}
// Set expand/shrink flags
for (col = 0; col < fNcols; ++col) {
if (fCol[col].fEmpty) {
fCol[col].fExpand = kFALSE;
fCol[col].fShrink = kFALSE;
} else {
if (fCol[col].fNeedExpand) fCol[col].fExpand = kTRUE;
if (!fCol[col].fNeedShrink) fCol[col].fShrink = kFALSE;
}
}
for (row = 0; row < fNrows; ++row) {
if (fRow[row].fEmpty) {
fRow[row].fExpand = kFALSE;
fRow[row].fShrink = kFALSE;
} else {
if (fRow[row].fNeedExpand) fRow[row].fExpand = kTRUE;
if (!fRow[row].fNeedShrink) fRow[row].fShrink = kFALSE;
}
}
}
//______________________________________________________________________________
void TGTableLayout::CheckSanity()
{
// Sanity check various values.
TIter next(fList);
TGFrameElement *ptr;
UInt_t nerrors = 0;
while ((ptr = (TGFrameElement*) next())) {
TGTableLayoutHints *layout =
dynamic_cast<TGTableLayoutHints*>(ptr->fLayout);
if (!layout) {
Error("CheckSanity", "didn't get TGTableLayoutHints");
return;
}
UInt_t right = layout->GetAttachRight();
UInt_t left = layout->GetAttachLeft();
UInt_t top = layout->GetAttachTop();
UInt_t bottom = layout->GetAttachBottom();
if (left == right) {
++nerrors;
Error("CheckSanity", "AttachLeft == AttachRight");
}
if (left > right) {
++nerrors;
Error("CheckSanity", "AttachLeft > AttachRight");
}
if (left > fNcols-1) {
++nerrors;
Error("CheckSanity", "AttachLeft illegal value: %u", left);
}
if (right < 1 || right > fNcols) {
++nerrors;
Error("CheckSanity", "AttachRight illegal value: %u", right);
}
if (top == bottom) {
++nerrors;
Error("CheckSanity", "AttachTop == AttachBottom");
}
if (top > bottom) {
++nerrors;
Error("CheckSanity", "AttachTop > AttachBottom");
}
if (top > fNrows-1) {
++nerrors;
Error("CheckSanity", "AttachTop illegal value: %u", top);
}
if (bottom < 1 || bottom > fNrows) {
++nerrors;
Error("CheckSanity", "AttachBottom illegal value: %u", bottom);
}
}
if (nerrors) {
Error("CheckSanity", "errors in %u x %u table", fNcols, fNrows);
}
}
//______________________________________________________________________________
void TGTableLayout::Layout()
{
// Make a table layout of all frames in the list.
CheckSanity();
FindRowColSizes();
SetRowColSizes();
// Do the layout
TIter next(fList);
TGFrameElement *ptr;
UInt_t border_width = fMain->GetBorderWidth();
while ((ptr = (TGFrameElement*) next())) {
TGTableLayoutHints *layout = (TGTableLayoutHints*)ptr->fLayout;
ULong_t hints = layout->GetLayoutHints();
TGDimension size = ptr->fFrame->GetDefaultSize();
UInt_t right = layout->GetAttachRight();
UInt_t left = layout->GetAttachLeft();
UInt_t top = layout->GetAttachTop();
UInt_t bottom = layout->GetAttachBottom();
// Find location and size of cell in which to fit the child frame.
UInt_t col, cell_x = border_width + left*fSep;
for (col = 0; col < left; ++col) cell_x += fCol[col].fRealSize;
UInt_t row, cell_y = border_width + top*fSep;
for (row = 0; row < top; ++row) cell_y += fRow[row].fRealSize;
UInt_t cell_width = (right-left-1)*fSep;
for (col=left; col < right; ++col)
cell_width += fCol[col].fRealSize;
UInt_t cell_height = (bottom-top-1)*fSep;
for (row=top; row < bottom; ++row)
cell_height += fRow[row].fRealSize;
UInt_t pad_left = layout->GetPadLeft();
UInt_t pad_right = layout->GetPadRight();
UInt_t pad_bottom = layout->GetPadBottom();
UInt_t pad_top = layout->GetPadTop();
// find size of child frame
UInt_t ww,hh;
if (hints & kLHintsFillX)
ww = cell_width - pad_left - pad_right;
else
ww = size.fWidth;
if (hints & kLHintsFillY)
hh = cell_height - pad_top - pad_bottom;
else
hh = size.fHeight;
// Find location of child frame
UInt_t xx;
if (hints & kLHintsFillX) // Fill beats right/center/left hints
xx = cell_x + pad_left;
else if (hints & kLHintsRight)
xx = cell_x + cell_width - pad_right - ww;
else if (hints & kLHintsCenterX)
xx = cell_x + cell_width/2 - ww/2; // padding?
else // defaults to kLHintsLeft
xx = cell_x + pad_left;
UInt_t yy;
if (hints & kLHintsFillY) // Fill beats top/center/bottom hings
yy = cell_y + pad_top;
else if (hints & kLHintsBottom)
yy = cell_y + cell_height - pad_bottom - hh;
else if (hints & kLHintsCenterY)
yy = cell_y + cell_height/2 - hh/2; // padding?
else // defaults to kLHintsTop
yy = cell_y + pad_top;
ptr->fFrame->MoveResize(xx,yy,ww,hh);
ptr->fFrame->Layout();
}
}
//______________________________________________________________________________
TGDimension TGTableLayout::GetDefaultSize() const
{
// Return default dimension of the table layout.
Int_t border_width = fMain->GetBorderWidth();
TGDimension size(2*border_width + (fNcols-1)*fSep,
2*border_width + (fNrows-1)*fSep);
UInt_t col, row;
if (fCol)
for (col = 0; col < fNcols; ++col) size.fWidth += fCol[col].fDefSize;
if (fRow)
for (row = 0; row < fNrows; ++row) size.fHeight += fRow[row].fDefSize;
return size;
}
// ________________________________________________________________________
void TGTableLayoutHints::SavePrimitive(ofstream &out, Option_t *)
{
// Save table layout hints as a C++ statement(s) on output stream out.
TString hints;
UInt_t pad = GetPadLeft()+GetPadRight()+GetPadTop()+GetPadBottom();
if (!GetLayoutHints()) return;
if ((fLayoutHints == kLHintsNormal) && (pad == 0)) return;
if (fLayoutHints & kLHintsLeft) {
if (hints.Length() == 0) hints = "kLHintsLeft";
else hints += " | kLHintsLeft";
}
if (fLayoutHints & kLHintsCenterX) {
if (hints.Length() == 0) hints = "kLHintsCenterX";
else hints += " | kLHintsCenterX";
}
if (fLayoutHints & kLHintsRight) {
if (hints.Length() == 0) hints = "kLHintsRight";
else hints += " | kLHintsRight";
}
if (fLayoutHints & kLHintsTop) {
if (hints.Length() == 0) hints = "kLHintsTop";
else hints += " | kLHintsTop";
}
if (fLayoutHints & kLHintsCenterY) {
if (hints.Length() == 0) hints = "kLHintsCenterY";
else hints += " | kLHintsCenterY";
}
if (fLayoutHints & kLHintsBottom) {
if (hints.Length() == 0) hints = "kLHintsBottom";
else hints += " | kLHintsBottom";
}
if (fLayoutHints & kLHintsExpandX) {
if (hints.Length() == 0) hints = "kLHintsExpandX";
else hints += " | kLHintsExpandX";
}
if (fLayoutHints & kLHintsExpandY) {
if (hints.Length() == 0) hints = "kLHintsExpandY";
else hints += " | kLHintsExpandY";
}
if (fLayoutHints & kLHintsShrinkX) {
if (hints.Length() == 0) hints = "kLHintsShrinkX";
else hints += " | kLHintsShrinkX";
}
if (fLayoutHints & kLHintsShrinkY) {
if (hints.Length() == 0) hints = "kLHintsShrinkY";
else hints += " | kLHintsShrinkY";
}
if (fLayoutHints & kLHintsFillX) {
if (hints.Length() == 0) hints = "kLHintsFillX";
else hints += " | kLHintsFillX";
}
if (fLayoutHints & kLHintsFillY) {
if (hints.Length() == 0) hints = "kLHintsFillY";
else hints += " | kLHintsFillY";
}
out << ", new TGTableLayoutHints(" << GetAttachLeft() << "," << GetAttachRight()
<< "," << GetAttachTop() << "," << GetAttachBottom()
<< "," << hints;
if (pad) {
out << "," << GetPadLeft() << "," << GetPadRight()
<< "," << GetPadTop() << "," << GetPadBottom();
}
out << ")";
}
// __________________________________________________________________________
void TGTableLayout::SavePrimitive(ofstream &out, Option_t *)
{
// Save table layout as a C++ statement(s) on output stream.
out << " new TGTableLayout(" << fMain->GetName() << "," << fNrows << "," << fNcols;
if (fSep) {
if (fHomogeneous == kTRUE)
out << ", kTRUE";
else
out << ", kFALSE";
out << fSep;
}
out << ")";
// hints parameter is not used/saved currently
}
ROOT page - Class index - Class Hierarchy - Top of the page
This page has been automatically generated. If you have any comments or suggestions about the page layout send a mail to ROOT support, or contact the developers with any questions or problems regarding ROOT.