/* 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(¶Info))) { 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;columncellData; 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;icellSpacing+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 tableWidth) { // too wide, reduce width of wide ones short bigWidth = (tableWidth+leftoverWidth)/colCount; for(column=0;columnaveWidth) (*hWidths)[column] = bigWidth; } } if (propCount) { // calculate size of proportional columns leftoverWidth = tableWidth; // how much table width do we have left over? for(column=0;columntable; 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;icellSpacing+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;rowtable; 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;colIdxcolumn;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; }