eudora-mac/table.c

1 line
34 KiB
C
Executable File

/* Copyright (c) 2017, Computer History Museum
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted (subject to
the limitations in the disclaimer below) provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of Computer History Museum nor the names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE
COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE. */
#include "table.h"
#define FILE_NUM 102
/* Copyright (c) 2000 by QUALCOMM Incorporated */
#pragma segment Table
static Boolean EditingTable(MyWindowPtr win);
static OSErr InsertTable(MyWindowPtr win,TIHandle table);
static void MakeTable(MyWindowPtr win);
static void TableDisplay(PETEHandle pte,TIHandle table,long offset);
static void SizeTable(PETEHandle pte,TIHandle tableInfo,short maxWidth);
static short **GetColWidths(PETEHandle pte,TIHandle tableInfo,short *sumWidths,short tableWidth,Boolean tableWidthSpecified);
static short **GetRowHeights(PETEHandle pte,TIHandle tableInfo,short *sumHeights,short **hColWidths);
static void SetCellBounds(PETEHandle pte,TableHandle table,short **hColWidths,short **hRowHeights);
static short GetCellWidth(PETEHandle pte,TIHandle tableInfo,short cellIdx);
static short GetCellHeight(PETEHandle pte,TIHandle tableInfo,short cellIdx,short colWidth);
static OSErr SetCellText(PETEHandle pte,TIHandle tableInfo,short cellIdx,short cellWidth);
static OSErr TableClone(TableHandle *table);
static void CloneHandle(Handle *pHand,OSErr *pErr);
RgnHandle SavePeteWinUpdateRgn(PETEHandle pte);
void RestorePeteWinUpdateRgn(PETEHandle pte,RgnHandle rgn);
static OSErr CreateSparePETE(MyWindowPtr win,PETEHandle *pte);
static OSErr ResetCellStyle(TIHandle tableInfo, short cellIdx);
/************************************************************************
* MakeTableGraphic - make a table, insert as graphic
************************************************************************/
OSErr MakeTableGraphic(PETEHandle pte,long offset,TableHandle table,PETEStyleEntryPtr pse)
{
TIHandle hTableInfo;
OSErr err = noErr;
PETEHandle pteSpare;
MyWindowPtr win = (*PeteExtra(pte))->win;
if (!(hTableInfo = NuHandleClear(sizeof(TableInfo))))
return MemError();
(*hTableInfo)->pgi.itemProc = TableGraphic;
(*hTableInfo)->pgi.wantsEvents = true;
(*hTableInfo)->table = table;
(*hTableInfo)->offset = offset;
// make a PETEHandle to measure, draw cells
if (err = CreateSparePETE(win,&pteSpare)) return err;
(*hTableInfo)->pteSpare = pteSpare;
SizeTable(pte,hTableInfo,RectWi(win->contR));
Zero(*pse);
pse->psGraphic = 1;
pse->psStyle.graphicStyle.tsFont = kPETEDefaultFont;
pse->psStyle.graphicStyle.tsSize = kPETERelativeSizeBase;
pse->psStyle.graphicStyle.graphicInfo = (PETEGraphicInfoHandle)hTableInfo;
return(err);
}
/**********************************************************************
* TableGraphic - editor callback to handle tables
**********************************************************************/
pascal OSErr TableGraphic(PETEHandle pte,TIHandle table,long offset,PETEGraphicMessage message,void *data)
{
OSErr err = noErr;
short oldResFile = CurResFile();
EventRecord *event;
MyWindowPtr win;
Boolean keyEvent;
Rect r;
short maxWidth;
PushGWorld();
(*table)->offset = offset;
switch (message)
{
case peGraphicDraw: /* data is (Point *) with penLoc at baseline left as a point */
TableDisplay(pte,table,offset);
break;
case peGraphicClone: /* data is (PETEGraphicInfoHandle *) into which to put duplicate */
// When user does copy or drag and drop, make copy of table
if (!(err = MyHandToHand(&table)))
{
TableHandle tempTable;
// make clone
tempTable = (*table)->table;
err = TableClone(&tempTable);
if (err) ZapHandle(table);
else
{
(*table)->table = tempTable;
(*table)->pteSpare = nil;
}
}
*(TIHandle*)data = table;
break;
case peGraphicInsert: /* data is nil. Graphic has been inserted into a new document. */
err = InsertTable((*PeteExtra(pte))->win,table);
break;
case peGraphicTest: /* data is (Point *) from top left; return errOffsetIsOutsideOfView if not hit */
// Can cancel hit by returning errOffsetIsOutsideOfView
break;
case peGraphicHit: /* data is (EventRecord *) if mouse down, nil if time to turn off */
break;
case peGraphicRemove: /* data is nil; just dispose of table info */
TableDispose((*table)->table);
if((*table)->pteSpare)
PeteDispose((*PeteExtra((*table)->pteSpare))->win,(*table)->pteSpare);
ZapHandle(table);
break;
case peGraphicResize: /* data is a (short *) of the max width */
maxWidth = *(short*)data;
// don't bother calculating size if the maxWidth hasn't changed
if (maxWidth != (*table)->maxWidth)
{
(*table)->maxWidth = maxWidth;
SizeTable(pte,table,maxWidth);
}
break;
case peGraphicRegion: /* data is a RgnHandle which is to be changed to the graphic's region */
// Used mostly for changing the cursor
PeteGraphicRect(&r,pte,(PETEGraphicInfoHandle)table,offset);
RectRgn((RgnHandle)data,&r);
break;
case peGraphicEvent: /* data is (EventRecord *) */
event = (EventRecord *)data;
win = (*PeteExtra(pte))->win;
keyEvent = event->what==keyDown || event->what==autoKey;
break;
}
PopGWorld();
UseResFile(oldResFile);
return(err);
}
/**********************************************************************
* TableMenuChoice - handle a selection from the table submenu
**********************************************************************/
void TableMenuChoice(MyWindowPtr win, short item)
{
//Styled Text - don't allow user to compose or send styled text
if (!HasFeature (featureExtendedStyles))
return;
switch (item)
{
case tblInsert:
if (EditingTable(win))
{
// Insert columns or rows
}
else
{
// Insert or make table
if (PeteLen(win->pte))
MakeTable(win);
else
InsertTable(win,nil);
}
break;
case tblDelete:
break;
case tblGridLine:
break;
case tblBorder:
break;
case tblRowHt:
break;
case tblColWd:
break;
}
}
/**********************************************************************
* CheckTableItems - check items in Table submenu
**********************************************************************/
void CheckTableItems(MyWindowPtr win,Boolean allMenu)
{
Boolean inTable;
//Styled Text - don't allow user to compose or send styled text
if (!HasFeature (featureExtendedStyles))
return;
inTable = EditingTable(win);
#if 0
if (inTable)
{
MenuHandle mh = GetMHandle(TEXT_TABLE_HIER_MENU);
TableHandle hTable = win->table;
SetItemMark(mh,tblGridLine,(*hTable)->gridline ? checkMark : noMark);
SetItemMark(mh,tblBorder,(*hTable)->borders ? checkMark : noMark);
}
#endif
}
/**********************************************************************
* EnableTableMenu - enable/disable/rename items in Table submenu
**********************************************************************/
void EnableTableMenu(MyWindowPtr win,Boolean all)
{
Boolean inTable;
#if 0
MenuHandle mh;
#endif
//Styled Text - don't allow user to compose or send styled text
if (!HasFeature (featureExtendedStyles))
return;
inTable = EditingTable(win);
#if 0
mh = GetMHandle(TEXT_TABLE_HIER_MENU);
if (mh)
{
EnableIf(mh,tblInsert,GetWindowKind(GetMyWindowWindowPtr(win))==COMP_WIN);
EnableIf(mh,tblDelete,inTable);
EnableIf(mh,tblGridLine,inTable);
EnableIf(mh,tblBorder,inTable);
EnableIf(mh,tblRowHt,inTable);
EnableIf(mh,tblColWd,inTable);
}
#endif
}
/**********************************************************************
* EditingTable - are we editing a table?
**********************************************************************/
static Boolean EditingTable(MyWindowPtr win)
{
return win && IsMyWindow(GetMyWindowWindowPtr(win)) && win->table && (*win->table)->pteEdit && (*win->table)->pteEdit == win->pte;
}
/**********************************************************************
* MakeTable - make a table from existing text
**********************************************************************/
static void MakeTable(MyWindowPtr win)
{
}
/**********************************************************************
* InsertTable - insert a new table
**********************************************************************/
static OSErr InsertTable(MyWindowPtr win,TIHandle table)
{
PETEHandle pteSpare;
OSErr err = noErr;
if (!(*table)->pteSpare && !(err = CreateSparePETE(win,&pteSpare)))
(*table)->pteSpare = pteSpare;
return err;
}
/**********************************************************************
* InsertTable - insert a new table
**********************************************************************/
static void TableDisplay(PETEHandle pte,TIHandle tableInfo,long offset)
{
Rect rTable;
TableHandle table = (*tableInfo)->table;
TabCellHandle hCells = (*table)->cellData;
PETEHandle pteSpare = (*tableInfo)->pteSpare;
short border,cellIdx;
RgnHandle clipRgn,saveUpdate = SavePeteWinUpdateRgn(pte);
PeteGraphicRect(&rTable,pte,(PETEGraphicInfoHandle)tableInfo,offset);
if (border = (*table)->border)
{
// draw border
SetForeGrey(0x8000);
PenSize(border,border);
FrameRect(&rTable);
PenNormal();
SetForeGrey(0);
}
// draw each cell if in clip region
if(clipRgn=NewRgn()) GetClip(clipRgn);
for(cellIdx=0;cellIdx<(*table)->cells;cellIdx++)
{
Rect rCell = (*hCells)[cellIdx].bounds;
OffsetRect(&rCell,rTable.left,rTable.top);
if (RectInRgn(&rCell,clipRgn))
{
short align,top,left;
PETEDocInfo info;
ClipRect(&rCell);
if (!SetCellText(pte,tableInfo,cellIdx,RectWi(rCell)))
{
top = rCell.top;
left = rCell.left;
if ((align = (*hCells)[cellIdx].align.vAlign) != htmlTopAlign)
if (!PETEGetDocInfo(PETE,pteSpare,&info))
{
// vertical alignment
switch (align)
{
case htmlBottomAlign: top = rCell.bottom-info.docHeight; break;
case htmlMiddleAlign: top = (rCell.top+rCell.bottom-info.docHeight)/2; break;
}
}
PETESizeDoc(PETE,pteSpare,RectWi(rCell),RectHi(rCell));
PETEMoveDoc(PETE,pteSpare,left,top);
PETEDraw(PETE,pteSpare);
ResetCellStyle(tableInfo, cellIdx);
}
if (border)
{
short inset = -(*table)->cellPadding-1;
SetForeGrey(0x8000);
InsetRect(&rCell,inset,inset);
ClipRect(&rCell);
FrameRect(&rCell);
SetForeGrey(0);
}
}
}
if(clipRgn) SetClip(clipRgn);
RestorePeteWinUpdateRgn(pte,saveUpdate);
}
/**********************************************************************
* TableColIndex - column to index
**********************************************************************/
short TableColIndex(TabColHandle columnData, short c)
{
if(columnData)
{
short i;
i = GetHandleSize(columnData) / sizeof(TabColData);
while(--i >= 0)
{
if(((*columnData)[i].column >= c) && ((*columnData)[i].column + (*columnData)[i].span < c))
return i;
}
}
return -1;
}
/**********************************************************************
* TableColGroupIndex - column group to index
**********************************************************************/
short TableColGroupIndex(TabColHandle columnGroupData, short c)
{
if(columnGroupData)
{
short i;
i = GetHandleSize(columnGroupData) / sizeof(TabColData);
while(--i >= 0)
{
if((((*columnGroupData)[i].span == 0) && ((*columnGroupData)[i].column < c)) ||
(((*columnGroupData)[i].column >= c) && ((*columnGroupData)[i].column + (*columnGroupData)[i].span < c)))
return i;
}
}
return -1;
}
/**********************************************************************
* TableRowIndex - table row to index
**********************************************************************/
short TableRowIndex(TabRowHandle rowData, short r)
{
if(rowData)
{
short i;
i = GetHandleSize(rowData) / sizeof(TabRowData);
while(--i >= 0)
{
if((*rowData)[i].row == r)
return i;
}
}
return -1;
}
/**********************************************************************
* TableRowGroupIndex - table group to index
**********************************************************************/
short TableRowGroupIndex(TabRowGroupHandle rowGroupData, short r)
{
if(rowGroupData)
{
short i;
i = GetHandleSize(rowGroupData) / sizeof(TabRowGroupData);
while(--i >= 0)
{
if((*rowGroupData)[i].row < r)
return i;
}
}
return -1;
}
/**********************************************************************
* GetCellHAlign - get horizontal alignment for cell
**********************************************************************/
void GetCellHAlign(TableHandle table, short cell, short *hAlign, UniChar *alignChar, short *charOff)
{
TabAlignData align;
short r, c, i;
r = (*(**table).cellData)[cell].row;
c = (*(**table).cellData)[cell].column;
align = (*(**table).cellData)[cell].align;
if(!align.hAlign)
{
i = TableColIndex((**table).columnData, c);
if(i >= 0)
{
align = (*(**table).columnData)[i].align;
}
if(!align.hAlign)
{
i = TableColIndex((**table).columnGroupData, c);
if(i >= 0)
{
align = (*(**table).columnGroupData)[i].align;
}
if(!align.hAlign)
{
i = TableRowIndex((**table).rowData, r);
if(i >= 0)
{
align = (*(**table).rowData)[i].align;
}
if(!align.hAlign)
{
i = TableRowGroupIndex((**table).rowGroupData, r);
if(i >= 0)
{
align = (*(**table).rowGroupData)[i].align;
}
if(!align.hAlign)
{
align.hAlign = (**table).align;
}
}
}
}
}
if(hAlign)
*hAlign = align.hAlign;
if(alignChar)
*alignChar = align.alignChar;
if(charOff)
*charOff = align.charOff;
}
/**********************************************************************
* GetCellVAlign - get vertical alignment for cell
**********************************************************************/
void GetCellVAlign(TableHandle table, short cell, short *vAlign)
{
short r, c, i, v;
r = (*(**table).cellData)[cell].row;
c = (*(**table).cellData)[cell].column;
v = (*(**table).cellData)[cell].align.vAlign;
if(!v)
{
i = TableRowIndex((**table).rowData, r);
if(i >= 0)
{
v = (*(**table).rowData)[i].align.vAlign;
}
if(!v)
{
i = TableRowGroupIndex((**table).rowGroupData, r);
if(i >= 0)
{
v = (*(**table).rowGroupData)[i].align.vAlign;
}
if(!v)
{
i = TableColIndex((**table).columnData, c);
if(i >= 0)
{
v = (*(**table).columnData)[i].align.vAlign;
}
if(!v)
{
i = TableColIndex((**table).columnGroupData, c);
if(i >= 0)
{
v = (*(**table).columnGroupData)[i].align.vAlign;
}
}
}
}
}
if(vAlign)
*vAlign = v;
}
/**********************************************************************
* GetCellDirection - get cell direction
**********************************************************************/
void GetCellDirection(TableHandle table, short cell, short *direction)
{
short r, c, i, d;
r = (*(**table).cellData)[cell].row;
c = (*(**table).cellData)[cell].column;
d = (*(**table).cellData)[cell].align.direction;
if(d == kHilite)
{
i = TableColIndex((**table).columnData, c);
if(i >= 0)
{
d = (*(**table).columnData)[i].align.direction;
}
if(d == kHilite)
{
i = TableColIndex((**table).columnGroupData, c);
if(i >= 0)
{
d = (*(**table).columnGroupData)[i].align.direction;
}
if(d == kHilite)
{
i = TableRowIndex((**table).rowData, r);
if(i >= 0)
{
d = (*(**table).rowData)[i].align.direction;
}
if(d == kHilite)
{
i = TableRowGroupIndex((**table).rowGroupData, r);
if(i >= 0)
{
d = (*(**table).rowGroupData)[i].align.direction;
}
if(d == kHilite)
d = (**table).direction;
}
}
}
}
if(direction)
*direction = d;
}
/**********************************************************************
* TableDispose - dispose of table data
**********************************************************************/
void TableDispose(TableHandle table)
{
TabCellHandle hCells = (*table)->cellData;
short cellIdx;
if(hCells)
for(cellIdx=0;cellIdx<(*table)->cells;cellIdx++)
{
DisposeHandle((*hCells)[cellIdx].abbr);
PETEDisposeStyleScrap(PETE, (*hCells)[cellIdx].styleInfo);
PETEDisposeParaScrap(PETE, (*hCells)[cellIdx].paraInfo);
}
DisposeHandle((*table)->rowGroupData);
DisposeHandle((*table)->rowData);
DisposeHandle((*table)->columnData);
DisposeHandle((*table)->columnGroupData);
DisposeHandle((*table)->cellData);
DisposeHandle(table);
}
/**********************************************************************
* TableClone - make a copy of table data
**********************************************************************/
static OSErr TableClone(TableHandle *table)
{
TableData tableData;
OSErr err;
long cellIdx;
if((err = MyHandToHand(table)) != noErr)
return err;
tableData = ***table;
(**table)->rowGroupData = nil;
(**table)->rowData = nil;
(**table)->columnData = nil;
(**table)->columnGroupData = nil;
(**table)->cellData = nil;
if(tableData.cellData && !(err = MyHandToHand(&tableData.cellData)))
{
(**table)->cellData = tableData.cellData;
for(cellIdx=0;cellIdx<(**table)->cells;cellIdx++)
{
Handle abbr;
PETEParaScrapHandle paraInfo= nil;
PETEStyleListHandle styleInfo = nil;
abbr = (*tableData.cellData)[cellIdx].abbr;
if(!abbr || !(err = MyHandToHand(&abbr)))
{
paraInfo = (*tableData.cellData)[cellIdx].paraInfo;
if(!paraInfo || !(err = MyHandToHand(&paraInfo)))
{
styleInfo = (*tableData.cellData)[cellIdx].styleInfo;
if(styleInfo && (err = PETEDuplicateStyleScrap(PETE, &styleInfo)) != noErr)
styleInfo = nil;
}
else
paraInfo = nil;
}
else
abbr = nil;
(*tableData.cellData)[cellIdx].abbr = abbr;
(*tableData.cellData)[cellIdx].paraInfo = paraInfo;
(*tableData.cellData)[cellIdx].styleInfo = styleInfo;
if(err)
{
while(cellIdx >= 0)
{
DisposeHandle((*tableData.cellData)[cellIdx].abbr);
PETEDisposeStyleScrap(PETE, (*tableData.cellData)[cellIdx].styleInfo);
PETEDisposeParaScrap(PETE, (*tableData.cellData)[cellIdx].paraInfo);
--cellIdx;
}
break;
}
}
}
if(!err)
{
if(!tableData.rowGroupData || !(err = MyHandToHand(&tableData.rowGroupData)))
{
(**table)->rowGroupData = tableData.rowGroupData;
if(!tableData.rowData || !(err = MyHandToHand(&tableData.rowData)))
{
(**table)->rowData = tableData.rowData;
if(!tableData.columnData || !(err = MyHandToHand(&tableData.columnData)))
{
(**table)->columnData = tableData.columnData;
if(!tableData.columnGroupData || !(err = MyHandToHand(&tableData.columnGroupData)))
{
(**table)->columnGroupData = tableData.columnGroupData;
}
}
}
}
}
if(err)
TableDispose(*table);
return err;
}
/**********************************************************************
* DuplicateHandle - duplicate a handle
**********************************************************************/
static void CloneHandle(Handle *pHand,OSErr *pErr)
{
if (!*pHand || *pErr) return;
*pErr = HandToHand(pHand);
}
/**********************************************************************
* GetColWidths - get column widths
**********************************************************************/
static short **GetColWidths(PETEHandle pte,TIHandle tableInfo,short *sumWidths,short tableWidth,Boolean tableWidthSpecified)
{
short **hWidths,colCount;
TabColHandle hColData;
short propCount = 0,cellIdx;
Str255 colStat;
enum { kNoWidth,kFixedWidth,kPropWidth };
short column;
TableHandle table = (*tableInfo)->table;
short width,span,i;
short leftoverWidth,noSpecCount;
colCount = (*table)->columns;
if (!(hWidths = NuHandleClear(sizeof(short)*colCount))) return nil;
Zero(colStat);
if ((hColData = (*table)->columnData) || (hColData = (*table)->columnGroupData))
{
short colDataCount,width;
TabColPtr pColData;
colDataCount = HandleCount(hColData);
for(colDataCount=HandleCount(hColData),pColData=*hColData;colDataCount--;)
{
span = pColData->span ? pColData->span : 1;
if ((width = pColData->width) < 0)
width = tableWidth*(-width)/100; // width is %
if (pColData->column + span <= colCount)
{
for (column = pColData->column;span--;column++)
{
(*hWidths)[column] = width;
if (pColData->propWidth)
{
propCount += width;
colStat[column] = kPropWidth;
}
else
colStat[column] = kFixedWidth;
}
}
}
// do we have any columns that we still don't know the width of?
for(column=0;column<colCount;column++)
if (!colStat[column])
{
hColData = 0; // signal we need to do some column calculations
break;
}
}
if (!hColData)
{
// we need to calculate column sizes based on cell contents
TabCellHandle hCells = (*table)->cellData;
short sumWidth,overCount,aveWidth;
Boolean doSpan=false;
for(cellIdx=0;cellIdx<(*table)->cells;cellIdx++)
{
column = (*hCells)[cellIdx].column;
if (!colStat[column])
{
if ((*hCells)[cellIdx].colSpan<2)
{
// need to calculate this column
width = GetCellWidth(pte,tableInfo,cellIdx);
if (width > (*hWidths)[column]) (*hWidths)[column] = width;
}
else
doSpan=true;
}
}
if (doSpan)
{
// there are column spans we need to check
for(cellIdx=0;cellIdx<(*table)->cells;cellIdx++)
{
span = (*hCells)[cellIdx].colSpan;
column = (*hCells)[cellIdx].column;
if (!colStat[column] && span>1)
{
// calculate this span
short sum=0;
width = GetCellWidth(pte,tableInfo,cellIdx);
// get sum of widths in span
for(i=column;i<column+span;i++)
sum += (*hWidths)[i];
sum += (span-1)*((*table)->cellSpacing+2*((*table)->cellPadding+(*table)->border?1:0));
if (width > sum)
{
// cell is too wide for span
// divide excess up equally between each column
short addThis = (width-sum+span-1)/span;
for(i=column;i<column+span;i++)
(*hWidths)[i] += addThis;
}
}
}
}
// make sure none of the columns are too wide
sumWidth = 0;
leftoverWidth = 0;
overCount = 0;
aveWidth = tableWidth/colCount;
for(column=0;column<colCount;column++)
{
if (colStat[column] != kPropWidth)
{
short thisWidth = (*hWidths)[column];
sumWidth += thisWidth;
if (thisWidth <= aveWidth) leftoverWidth += thisWidth;
else overCount++;
}
}
if (sumWidth > tableWidth)
{
// too wide, reduce width of wide ones
short bigWidth = (tableWidth+leftoverWidth)/colCount;
for(column=0;column<colCount;column++)
if (colStat[column] != kPropWidth && (*hWidths)[column]>aveWidth)
(*hWidths)[column] = bigWidth;
}
}
if (propCount)
{
// calculate size of proportional columns
leftoverWidth = tableWidth;
// how much table width do we have left over?
for(column=0;column<colCount;column++)
{
if (colStat[column] != kPropWidth)
leftoverWidth -= (*hWidths)[column];
}
if (leftoverWidth < 0) leftoverWidth = 0; // this should never happen
for(column=0;column<colCount;column++)
{
if (colStat[column] == kPropWidth)
(*hWidths)[column] = (*hWidths)[column]*leftoverWidth/propCount;
}
}
if (tableWidthSpecified)
{
// Any extra remaining width should be divided up among
// cells with no specified width
leftoverWidth = tableWidth;
noSpecCount = 0;
for(column=0;column<colCount;column++)
{
leftoverWidth -= (*hWidths)[column];
if (!colStat[column])
noSpecCount++;
}
if (leftoverWidth)
{
short lastNoSpec=0;
for(column=0;column<colCount;column++)
if (!colStat[column])
{
(*hWidths)[column] += leftoverWidth/noSpecCount;
lastNoSpec = column;
}
(*hWidths)[lastNoSpec] += leftoverWidth%noSpecCount; // Fill up to very end
}
}
// calculate sum of widths
*sumWidths = 0;
for(column=0;column<colCount;column++)
*sumWidths += (*hWidths)[column];
return hWidths;
}
/**********************************************************************
* SetCellText - put cell text in edit pte
**********************************************************************/
static OSErr SetCellText(PETEHandle pte,TIHandle tableInfo,short cellIdx,short cellWidth)
{
TableHandle table = (*tableInfo)->table;
PETEHandle pteSpare = (*tableInfo)->pteSpare;
UHandle text = nil;
TabCellHandle hCells = (*table)->cellData;
OSErr err;
Boolean saveDirty;
MyWindowPtr win;
err = PETESetRecalcState(PETE,pteSpare,false);
if (err) return err;
win = (*PeteExtra(pteSpare))->win;
saveDirty = win->isDirty;
err = PeteDelete(pteSpare,0,0x7fffffff);
if (!err)
{
err = PETEGetText(PETE, pte, (*hCells)[cellIdx].textOffset + (*tableInfo)->offset, (*hCells)[cellIdx].textOffset + (*tableInfo)->offset + (*hCells)[cellIdx].textLength, &text);
if(!err)
{
err = PETEChangeDocWidth(PETE,pteSpare,cellWidth,true);
if(!err)
{
err = PETEInsertTextScrap(PETE,pteSpare,0,text,(*hCells)[cellIdx].styleInfo,(*hCells)[cellIdx].paraInfo,false);
if(!err)
ZapHandle((*hCells)[cellIdx].styleInfo);
}
ZapHandle(text);
}
}
win->isDirty = saveDirty;
PETESetRecalcState(PETE,pteSpare,true);
return err;
}
/**********************************************************************
* GetCellWidth - get width of cell contents
**********************************************************************/
static short GetCellWidth(PETEHandle pte,TIHandle tableInfo,short cellIdx)
{
TableHandle table = (*tableInfo)->table;
TabCellHandle hCells = (*table)->cellData;
Point startPt,endPt;
LHElement lineHeight;
short width;
width = 0;
if ((*hCells)[cellIdx].width)
width = (*hCells)[cellIdx].width;
else if (!SetCellText(pte,tableInfo,cellIdx,REAL_BIG))
{
if (!PETEOffsetToPosition(PETE,(*tableInfo)->pteSpare,0,&startPt,&lineHeight))
if (!PETEOffsetToPosition(PETE,(*tableInfo)->pteSpare,(*hCells)[cellIdx].textLength,&endPt,&lineHeight))
width = endPt.h - startPt.h + 9;
ResetCellStyle(tableInfo, cellIdx);
}
return width;
}
/**********************************************************************
* GetRowHeights - get row heights
**********************************************************************/
static short **GetRowHeights(PETEHandle pte,TIHandle tableInfo,short *sumHeights,short **hColWidths)
{
short **hHeights,rowCount;
short cellIdx, row, height, column, span, cellWidth, i;
TableHandle table = (*tableInfo)->table;
TabCellHandle hCells = (*table)->cellData;
rowCount = (*table)->rows;
if (!(hHeights = NuHandleClear(sizeof(short)*rowCount))) return nil;
for(cellIdx=0;cellIdx<(*table)->cells;cellIdx++)
{
row = (*hCells)[cellIdx].row;
span = (*hCells)[cellIdx].colSpan;
column = (*hCells)[cellIdx].column;
cellWidth = (*hColWidths)[column];
if (span>1)
{
for(i=column+1;i<column+span;i++)
cellWidth += (*hColWidths)[i];
cellWidth += (span-1)*((*table)->cellSpacing+2*((*table)->cellPadding+(*table)->border?1:0));
}
height = GetCellHeight(pte,tableInfo,cellIdx,cellWidth);
if (height > (*hHeights)[row]) (*hHeights)[row] = height;
}
// calculate sum of heights
*sumHeights = 0;
for(row=0;row<rowCount;row++)
*sumHeights += (*hHeights)[row];
return hHeights;
}
/**********************************************************************
* GetCellHeight - get height of cell contents
**********************************************************************/
static short GetCellHeight(PETEHandle pte,TIHandle tableInfo,short cellIdx,short colWidth)
{
TableHandle table = (*tableInfo)->table;
PETEHandle pteEdit = (*table)->pteEdit;
TabCellHandle hCells = (*table)->cellData;
short height;
height = 0;
if ((*hCells)[cellIdx].height)
height = (*hCells)[cellIdx].height;
else if (!SetCellText(pte,tableInfo,cellIdx,colWidth))
{
PETEDocInfo info;
if (!PETEGetDocInfo(PETE,(*tableInfo)->pteSpare,&info))
height = info.docHeight;
ResetCellStyle(tableInfo, cellIdx);
}
return height;
}
/**********************************************************************
* SetCellBounds - set the size and position of each cell
**********************************************************************/
static void SetCellBounds(PETEHandle pte,TableHandle table,short **hColWidths,short **hRowHeights)
{
TabCellHandle hCells = (*table)->cellData;
short lastRow=0,colIdx,cellCount;
Rect r;
TabCellPtr pCell;
short border = (*table)->border;
short cellPadding = (*table)->cellPadding;
short cellSpacing = (*table)->cellSpacing;
short cellBorder = border?1:0;
short row,column,span;
if (border && !(*table)->cellSpacingSpecified)
cellSpacing = 1; // if we're drawing cell borders, make sure there is at least some cell spacing
r.top = border+cellSpacing+cellBorder+cellPadding;
for (cellCount=(*table)->cells,pCell=*hCells;cellCount--;pCell++)
{
while (pCell->row>lastRow)
// new row, might have skipped some with row span
r.top += (*hRowHeights)[lastRow++]+cellSpacing+2*(cellPadding+cellBorder);
r.left = border+cellSpacing+cellBorder+cellPadding;
for(colIdx=0;colIdx<pCell->column;colIdx++)
r.left += (*hColWidths)[colIdx]+cellSpacing+2*(cellPadding+cellBorder);
row = pCell->row;
column = pCell->column;
r.bottom = r.top + (*hRowHeights)[row++];
r.right = r.left + (*hColWidths)[column++];
for (span = pCell->colSpan?pCell->colSpan-1:0;span-- && column < (*table)->columns;)
{
// column span
r.right += (*hColWidths)[column++] + cellSpacing+2*(cellPadding+cellBorder);
}
for (span = pCell->rowSpan?pCell->rowSpan-1:0;span-- && row < (*table)->rows;)
{
// row span
r.bottom += (*hRowHeights)[row++] + cellSpacing+2*(cellPadding+cellBorder);
}
pCell->bounds = r;
}
}
/**********************************************************************
* SizeTable - set the size of the table
**********************************************************************/
static void SizeTable(PETEHandle pte,TIHandle tableInfo,short maxWidth)
{
short width, height, colWidths;
short **hColWidths=nil,**hRowHeights=nil;
short widthPadding,heightPadding,widthAvail,columns,rows;
TableHandle table = (*tableInfo)->table;
short cellSpacing;
RgnHandle saveClip = SavePeteWinUpdateRgn(pte);
height = 0;
width = (*table)->width;
if (width < 0) width = maxWidth*(-width)/100; // width is %
// determine extra padding
columns = (*table)->columns;
rows = (*table)->rows;
cellSpacing = (*table)->cellSpacing;
if ((*table)->border && !(*table)->cellSpacingSpecified)
cellSpacing = 1; // if we're drawing cell borders, make sure there is at least some cell spacing
widthPadding = 2*(*table)->border + 2*columns*(*table)->cellPadding
+ (columns+1)*cellSpacing;
heightPadding = 2*(*table)->border + 2*rows*(*table)->cellPadding
+ (rows+1)*cellSpacing;
widthAvail = width?width:maxWidth;
if (widthPadding >= widthAvail) widthPadding = 0;
else widthAvail -= widthPadding;
if (hColWidths = GetColWidths(pte,tableInfo,&colWidths,widthAvail,width!=0))
if (hRowHeights = GetRowHeights(pte,tableInfo,&height,hColWidths))
{
if (!width)
width = colWidths;
SetCellBounds(pte,table,hColWidths,hRowHeights);
}
ZapHandle(hColWidths);
ZapHandle(hRowHeights);
// if we're measuring the width of a cell that contains a table
// this may be really large. Reduce it down a bit to avoid short int overflow
width = MIN(width,16000);
width += widthPadding;
(*tableInfo)->pgi.width = MIN(width,maxWidth);
(*tableInfo)->pgi.height = height + heightPadding;
(*tableInfo)->pgi.descent = 3;
RestorePeteWinUpdateRgn(pte,saveClip);
}
/**********************************************************************
* SavePeteWinUpdateRgn - save the updatergn of the window containing a pete handle
**********************************************************************/
RgnHandle SavePeteWinUpdateRgn(PETEHandle pte)
{
RgnHandle rgn;
if (rgn = NewRgn())
GetWindowUpdateRgn(GetMyWindowWindowPtr((*PeteExtra(pte))->win),rgn);
return rgn;
}
/**********************************************************************
* RestorePeteWinUpdateRgn - restore the updatergn of the window containing a pete handle
**********************************************************************/
void RestorePeteWinUpdateRgn(PETEHandle pte,RgnHandle rgn)
{
if (rgn)
{
LocalizeRgn(rgn);
InvalWindowRgn(GetMyWindowWindowPtr((*PeteExtra(pte))->win),rgn);
DisposeRgn(rgn);
}
}
/**********************************************************************
* CreateSparePTE - create a spare PETE handle
**********************************************************************/
static OSErr CreateSparePETE(MyWindowPtr win,PETEHandle *pte)
{
PETEDocInitInfo pdi;
OSErr err;
DefaultPII(win,false,peEmbedded,&pdi);
pdi.inParaInfo.paraFlags = 0;
pdi.inParaInfo.startMargin = 0;
pdi.inParaInfo.endMargin = WindowWi(GetMyWindowWindowPtr(win))+1;
err = PeteCreate(win,pte,peEmbedded,&pdi);
CleanPII(&pdi);
if (err) return err;
// remove from win's pteList, we don't want anyone to know about this pte
win->pteList = PeteRemove(win->pteList,*pte);
// get rid of progress callback function
// it can cause re-entrancy problems
PETESetCallback(PETE,*pte,nil,peProgressLoop);
return noErr;
}
static OSErr ResetCellStyle(TIHandle tableInfo, short cellIdx)
{
PETEStyleListHandle styleInfo;
TabCellHandle hCells = (*(*tableInfo)->table)->cellData;
OSErr err;
if(!(err = PeteGetTextStyleScrap((*tableInfo)->pteSpare, 0, LONG_MAX, nil, &styleInfo, nil)))
{
(*hCells)[cellIdx].styleInfo = styleInfo;
}
return err;
}