1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
|
# -*- coding: utf-8 -*-
""" This module holds the TableLayout and TableCell classes
They are used to intelligent layout widgets in cells in a table...
"""
# Copyright 2002, 2003 St James Software
#
# This file is part of jToolkit.
#
# jToolkit is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# jToolkit is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with jToolkit; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
from jToolkit.widgets import widgets
class TableCell(widgets.ContentWidget):
def __init__(self, contents, newattribs={}, rowspan=1, colspan=1):
widgets.ContentWidget.__init__(self, 'td', contents)
self.attribs = {'type':'td', 'rowspan':rowspan, 'colspan':colspan}
self.overrideattribs(newattribs)
self.hidden = 0
self.width = 0
self.isempty = 0
def gethtml(self):
if self.hidden:
return ""
else:
return widgets.ContentWidget.gethtml(self)
class TableLayout(widgets.Widget):
# maintains a dictionary of cells in rows, indexed by row number and column number
def __init__(self, newattribs = {}):
widgets.Widget.__init__(self, "TABLE")
self.attribs = {'cellspacing':'1', 'cellpadding':'1'}
self.overrideattribs(newattribs)
self.rowsdict = {}
def hasrow(self, rownum):
return rownum in self.rowsdict
def setrow(self, rownum):
self.rowsdict[rownum] = {}
def getrow(self, rownum):
return self.rowsdict.get(rownum, {})
def hascell(self, rownum, colnum):
return self.hasrow(rownum) and colnum in self.getrow(rownum)
def setcell(self, rownum, colnum, value):
"""sets the cell in rownum, colnum position to value"""
# add the row if it doesn't yet exist
if not self.hasrow(rownum):
self.setrow(rownum)
self.getrow(rownum)[colnum] = value
def delcell(self, rownum, colnum):
"""removes the cell in rownum, colnum position"""
if not self.hasrow(rownum):
raise KeyError, "row %d does not exist" % rownum
else:
del self.getrow(rownum)[colnum]
def getcell(self, rownum, colnum):
"""returns the cell in position rownum, colnum"""
cell = self.getrow(rownum).get(colnum, None)
if cell is None:
return self.newemptycell()
else:
return cell
def newemptycell(self):
emptycell = TableCell('')
emptycell.width = 0
emptycell.isempty = 1
return emptycell
def maxrowspan(self, row):
if len(row) == 0: return 0
return max([cell.attribs['rowspan'] for cell in row.itervalues()])
def rowmaxcolnum(self, row):
if len(row) == 0: return 0
return max([col + cell.attribs['colspan']-1 for col, cell in row.iteritems()])
def minrownum(self):
if len(self.rowsdict) == 0: return 0
return min(self.rowsdict.iterkeys())
def maxrownum(self):
if len(self.rowsdict) == 0: return 0
return max([rownum + self.maxrowspan(row)-1 for rownum, row in self.rowsdict.iteritems()])
def rowrange(self):
return range(self.minrownum(), self.maxrownum()+1)
def mincolnum(self):
if len(self.rowsdict) == 0: return 0
return min([min(row.iterkeys()) for row in self.rowsdict.itervalues()])
def maxcolnum(self):
if len(self.rowsdict) == 0: return 0
return max([self.rowmaxcolnum(row) for row in self.rowsdict.itervalues()])
def colrange(self):
return range(self.mincolnum(), self.maxcolnum()+1)
def getrowcontents(self, rownum):
contentslist = [self.getcell(rownum, colnum).gethtml() for colnum in self.colrange()]
# TODO: investigate ways to neaten this up while still preventing ASCII decoding error...
typelist = [type(p) for p in contentslist]
if unicode in typelist and str in typelist:
for n in range(len(contentslist)):
if isinstance(contentslist[n], str):
contentslist[n] = contentslist[n].decode('utf8')
return "<tr>" + "".join(contentslist) + "</tr>\r"
def getcontents(self):
contentslist = [self.getrowcontents(rownum) for rownum in self.rowrange()]
# TODO: investigate ways to neaten this up while still preventing ASCII decoding error...
typelist = [type(p) for p in contentslist]
if unicode in typelist and str in typelist:
for n in range(len(contentslist)):
if isinstance(contentslist[n], str):
contentslist[n] = contentslist[n].decode('utf8')
return "".join(contentslist)
def rowempty(self, rownum):
"""returns whether this row is empty (contains no non-empty cells)"""
for colnum in self.colrange():
if self.hascell(rownum, colnum) and not self.getcell(rownum, colnum).isempty:
return 0
return 1
def colempty(self, colnum):
"""returns whether this column is empty (contains no non-empty cells)"""
for rownum in self.rowrange():
if self.hascell(rownum, colnum) and not self.getcell(rownum, colnum).isempty:
return 0
return 1
def adjustrowspan(self, rownum, deletedrownum):
"""adjusts all the rowspans for this rownum to reflect the fact that deletedrownum is gone"""
for colnum in self.colrange():
if self.hascell(rownum, colnum):
cell = self.getcell(rownum, colnum)
if cell.attribs['rowspan'] > deletedrownum - rownum:
cell.attribs['rowspan'] -= 1
def adjustcolspan(self, colnum, deletedcolnum):
"""adjusts all the colspans for this colnum to reflect the dact that deletedcolnum is gone"""
for rownum in self.rowrange():
if self.hascell(rownum, colnum):
cell = self.getcell(rownum, colnum)
if cell.attribs['colspan'] > deletedcolnum - colnum:
cell.attribs['colspan'] -= 1
def removerow(self, rownum):
"""removes the row from the table, shrinking other rownums, and adjusting rowspans..."""
if rownum in self.rowsdict:
del self.rowsdict[rownum]
for otherrownum in self.rowrange():
# adjust rowspans for rows before this one
if not otherrownum in self.rowsdict:
continue
if otherrownum < rownum:
# do a general check to see if this is neccessary at all for this row
if self.maxrowspan(self.rowsdict[otherrownum]) > rownum - otherrownum:
self.adjustrowspan(otherrownum, rownum)
elif otherrownum > rownum:
# shift row one up...
self.rowsdict[otherrownum-1] = self.rowsdict[otherrownum]
del self.rowsdict[otherrownum]
def removecol(self, colnum):
"""removes the column from the table, shrinking other colnums, and adjusting colspans..."""
for rownum in self.rowrange():
if self.hascell(rownum, colnum):
self.delcell(rownum, colnum)
for othercolnum in self.colrange():
# adjust colspans for cols before this one
if othercolnum < colnum:
self.adjustcolspan(othercolnum, colnum)
elif othercolnum > colnum:
# shift col one up...
for rownum in self.rowrange():
if self.hascell(rownum, othercolnum):
cell = self.getcell(rownum, othercolnum)
self.delcell(rownum, othercolnum)
self.setcell(rownum, othercolnum-1, cell)
def shrinkrange(self):
"""removes all the empty rows and columns, reducing the range..."""
if len(self.rowsdict) == 0: return
# detect which rows are empty
emptyrows = []
for rownum in self.rowrange():
if self.rowempty(rownum):
emptyrows.append(rownum)
# remove the rows, adjusting for the rows that have already been removed...
adjustment = 0
for rownum in emptyrows:
self.removerow(rownum-adjustment)
adjustment += 1
# detect which columns are empty
emptycols = []
for colnum in self.colrange():
if self.colempty(colnum):
emptycols.append(colnum)
# remove the colums, adjusting for the colums that have already been removed...
adjustment = 0
for colnum in emptycols:
self.removecol(colnum-adjustment)
adjustment += 1
def fillemptycells(self, emptycellmaker=None):
if emptycellmaker is None:
emptycellmaker = self.newemptycell
for rownum in self.rowrange():
for colnum in self.colrange():
if not self.hascell(rownum, colnum):
self.setcell(rownum, colnum, emptycellmaker())
def mergeduplicates(self, excluderows=[], emptycellmaker=None):
"""merge cells that are the same object..."""
if emptycellmaker is None:
emptycellmaker = self.newemptycell
for colnum in self.colrange():
lastcell = None
for rownum in self.rowrange():
if rownum in excluderows:
# allows us to skip the title etc
continue
cell = self.getcell(rownum, colnum)
if cell == lastcell and lastcell is not None:
lastcell.attribs['rowspan'] += 1
self.setcell(rownum, colnum, emptycellmaker())
else:
lastcell = cell
def hidecoveredcells(self):
"""hide blank cells that are underneath another cell with rowspan/colspan"""
self.fillemptycells()
for rownum in self.rowrange():
for colnum in self.colrange():
cell = self.getcell(rownum,colnum)
if not cell.hidden:
for blankrowoffset in range(0,cell.attribs['rowspan']):
for blankcoloffset in range(0,cell.attribs['colspan']):
# don't overwrite the current cell!
if blankrowoffset != 0 or blankcoloffset != 0:
self.hidecell(rownum+blankrowoffset, colnum+blankcoloffset)
def hidecell(self, rownum, colnum):
cell = self.getcell(rownum, colnum)
cell.hidden = 1
def rowwidth(self, rownum):
width = 0
for cell in self.getrow(rownum).itervalues():
width += getattr(cell, 'width', 0)
return width
def maxwidth(self):
return max([self.rowwidth(rownum) for rownum in self.rowrange()])
def calcweights(self):
# calculate column widths for all the cells
maxwidth = self.maxwidth()
for rownum in self.rowrange():
for colnum in self.colrange():
cell = self.getcell(rownum, colnum)
if cell.width != 0:
width=100.0*cell.width/maxwidth
styleattribs = {'width':'%d%%' % int(width)}
cell.overrideattribs({'style':styleattribs})
|