Guido van Rossum | c636014 | 1990-10-13 19:23:40 +0000 | [diff] [blame] | 1 | # Module 'tablewin' |
| 2 | |
| 3 | # Display a table, with per-item actions: |
| 4 | |
| 5 | # A1 | A2 | A3 | .... | AN |
| 6 | # B1 | B2 | B3 | .... | BN |
| 7 | # C1 | C2 | C3 | .... | CN |
| 8 | # .. | .. | .. | .... | .. |
| 9 | # Z1 | Z2 | Z3 | .... | ZN |
| 10 | |
| 11 | # Not all columns need to have the same length. |
| 12 | # The data structure is a list of columns; |
| 13 | # each column is a list of items. |
| 14 | # Each item is a pair of a string and an action procedure. |
| 15 | # The first item may be a column title. |
| 16 | |
| 17 | import stdwin |
| 18 | import gwin |
| 19 | |
| 20 | def open(title, data): # Public function to open a table window |
| 21 | # |
| 22 | # Set geometry parameters (one day, these may be changeable) |
| 23 | # |
| 24 | margin = stdwin.textwidth(' ') |
| 25 | lineheight = stdwin.lineheight() |
| 26 | # |
| 27 | # Geometry calculations |
| 28 | # |
| 29 | colstarts = [0] |
| 30 | totwidth = 0 |
| 31 | maxrows = 0 |
| 32 | for coldata in data: |
| 33 | # Height calculations |
| 34 | rows = len(coldata) |
| 35 | if rows > maxrows: maxrows = rows |
| 36 | # Width calculations |
| 37 | width = colwidth(coldata) + margin |
| 38 | totwidth = totwidth + width |
| 39 | colstarts.append(totwidth) |
| 40 | # |
| 41 | # Calculate document and window height |
| 42 | # |
| 43 | docwidth, docheight = totwidth, maxrows*lineheight |
| 44 | winwidth, winheight = docwidth, docheight |
| 45 | if winwidth > stdwin.textwidth('n')*100: winwidth = 0 |
| 46 | if winheight > stdwin.lineheight()*30: winheight = 0 |
| 47 | # |
| 48 | # Create the window |
| 49 | # |
| 50 | stdwin.setdefwinsize(winwidth, winheight) |
| 51 | w = gwin.open(title) |
| 52 | # |
| 53 | # Set properties and override methods |
| 54 | # |
| 55 | w.data = data |
| 56 | w.margin = margin |
| 57 | w.lineheight = lineheight |
| 58 | w.colstarts = colstarts |
| 59 | w.totwidth = totwidth |
| 60 | w.maxrows = maxrows |
| 61 | w.selection = (-1, -1) |
| 62 | w.lastselection = (-1, -1) |
| 63 | w.selshown = 0 |
| 64 | w.setdocsize(docwidth, docheight) |
| 65 | w.draw = draw |
| 66 | w.mup = mup |
| 67 | w.arrow = arrow |
| 68 | # |
| 69 | # Return |
| 70 | # |
| 71 | return w |
| 72 | |
| 73 | def update(w, data): # Change the data |
| 74 | # |
| 75 | # Hide selection |
| 76 | # |
| 77 | hidesel(w, w.begindrawing()) |
| 78 | # |
| 79 | # Get old geometry parameters |
| 80 | # |
| 81 | margin = w.margin |
| 82 | lineheight = w.lineheight |
| 83 | # |
| 84 | # Geometry calculations |
| 85 | # |
| 86 | colstarts = [0] |
| 87 | totwidth = 0 |
| 88 | maxrows = 0 |
| 89 | for coldata in data: |
| 90 | # Height calculations |
| 91 | rows = len(coldata) |
| 92 | if rows > maxrows: maxrows = rows |
| 93 | # Width calculations |
| 94 | width = colwidth(coldata) + margin |
| 95 | totwidth = totwidth + width |
| 96 | colstarts.append(totwidth) |
| 97 | # |
| 98 | # Calculate document and window height |
| 99 | # |
| 100 | docwidth, docheight = totwidth, maxrows*lineheight |
| 101 | # |
| 102 | # Set changed properties and change window size |
| 103 | # |
| 104 | w.data = data |
| 105 | w.colstarts = colstarts |
| 106 | w.totwidth = totwidth |
| 107 | w.maxrows = maxrows |
| 108 | w.change((0, 0), (10000, 10000)) |
| 109 | w.setdocsize(docwidth, docheight) |
| 110 | w.change((0, 0), (docwidth, docheight)) |
| 111 | # |
| 112 | # Show selection, or forget it if out of range |
| 113 | # |
| 114 | showsel(w, w.begindrawing()) |
| 115 | if not w.selshown: w.selection = (-1, -1) |
| 116 | |
| 117 | def colwidth(coldata): # Subroutine to calculate column width |
| 118 | maxwidth = 0 |
| 119 | for string, action in coldata: |
| 120 | width = stdwin.textwidth(string) |
| 121 | if width > maxwidth: maxwidth = width |
| 122 | return maxwidth |
| 123 | |
| 124 | def draw(w, ((left, top), (right, bottom))): # Draw method |
| 125 | ileft = whichcol(w, left) |
| 126 | iright = whichcol(w, right-1) + 1 |
| 127 | if iright > len(w.data): iright = len(w.data) |
| 128 | itop = divmod(top, w.lineheight)[0] |
| 129 | if itop < 0: itop = 0 |
| 130 | ibottom, remainder = divmod(bottom, w.lineheight) |
| 131 | if remainder: ibottom = ibottom + 1 |
| 132 | d = w.begindrawing() |
| 133 | if ileft <= w.selection[0] < iright: |
| 134 | if itop <= w.selection[1] < ibottom: |
| 135 | hidesel(w, d) |
| 136 | d.erase((left, top), (right, bottom)) |
| 137 | for i in range(ileft, iright): |
| 138 | col = w.data[i] |
| 139 | jbottom = len(col) |
| 140 | if ibottom < jbottom: jbottom = ibottom |
| 141 | h = w.colstarts[i] |
| 142 | v = itop * w.lineheight |
| 143 | for j in range(itop, jbottom): |
| 144 | string, action = col[j] |
| 145 | d.text((h, v), string) |
| 146 | v = v + w.lineheight |
| 147 | showsel(w, d) |
| 148 | |
| 149 | def mup(w, detail): # Mouse up method |
| 150 | (h, v), nclicks, button, mask = detail |
| 151 | icol = whichcol(w, h) |
| 152 | if 0 <= icol < len(w.data): |
| 153 | irow = divmod(v, w.lineheight)[0] |
| 154 | col = w.data[icol] |
| 155 | if 0 <= irow < len(col): |
| 156 | string, action = col[irow] |
| 157 | action(w, string, (icol, irow), detail) |
| 158 | |
| 159 | def whichcol(w, h): # Return column number (may be >= len(w.data)) |
| 160 | for icol in range(0, len(w.data)): |
| 161 | if h < w.colstarts[icol+1]: |
| 162 | return icol |
| 163 | return len(w.data) |
| 164 | |
| 165 | def arrow(w, type): |
| 166 | import stdwinsupport |
| 167 | S = stdwinsupport |
| 168 | if type = S.wc_left: |
| 169 | incr = -1, 0 |
| 170 | elif type = S.wc_up: |
| 171 | incr = 0, -1 |
| 172 | elif type = S.wc_right: |
| 173 | incr = 1, 0 |
| 174 | elif type = S.wc_down: |
| 175 | incr = 0, 1 |
| 176 | else: |
| 177 | return |
| 178 | icol, irow = w.lastselection |
| 179 | icol = icol + incr[0] |
| 180 | if icol < 0: icol = len(w.data)-1 |
| 181 | if icol >= len(w.data): icol = 0 |
| 182 | if 0 <= icol < len(w.data): |
| 183 | irow = irow + incr[1] |
| 184 | if irow < 0: irow = len(w.data[icol]) - 1 |
| 185 | if irow >= len(w.data[icol]): irow = 0 |
| 186 | else: |
| 187 | irow = 0 |
| 188 | if 0 <= icol < len(w.data) and 0 <= irow < len(w.data[icol]): |
| 189 | w.lastselection = icol, irow |
| 190 | string, action = w.data[icol][irow] |
| 191 | detail = (0, 0), 1, 1, 1 |
| 192 | action(w, string, (icol, irow), detail) |
| 193 | |
| 194 | |
| 195 | # Selection management |
| 196 | # TO DO: allow multiple selected entries |
| 197 | |
| 198 | def select(w, selection): # Public function to set the item selection |
| 199 | d = w.begindrawing() |
| 200 | hidesel(w, d) |
| 201 | w.selection = selection |
| 202 | showsel(w, d) |
| 203 | if w.selshown: lastselection = selection |
| 204 | |
| 205 | def hidesel(w, d): # Hide the selection, if shown |
| 206 | if w.selshown: invertsel(w, d) |
| 207 | |
| 208 | def showsel(w, d): # Show the selection, if hidden |
| 209 | if not w.selshown: invertsel(w, d) |
| 210 | |
| 211 | def invertsel(w, d): # Invert the selection, if valid |
| 212 | icol, irow = w.selection |
| 213 | if 0 <= icol < len(w.data) and 0 <= irow < len(w.data[icol]): |
| 214 | left = w.colstarts[icol] |
| 215 | right = w.colstarts[icol+1] |
| 216 | top = irow * w.lineheight |
| 217 | bottom = (irow+1) * w.lineheight |
| 218 | d.invert((left, top), (right, bottom)) |
| 219 | w.selshown = (not w.selshown) |
| 220 | |
| 221 | |
| 222 | # Demonstration |
| 223 | |
| 224 | def demo_action(w, string, (icol, irow), detail): # Action function for demo |
| 225 | select(w, (irow, icol)) |
| 226 | |
| 227 | def demo(): # Demonstration |
| 228 | da = demo_action # shorthand |
| 229 | col0 = [('a1', da), ('bbb1', da), ('c1', da)] |
| 230 | col1 = [('a2', da), ('bbb2', da)] |
| 231 | col2 = [('a3', da), ('b3', da), ('c3', da), ('d4', da), ('d5', da)] |
| 232 | col3 = [] |
| 233 | for i in range(1, 31): col3.append('xxx' + `i`, da) |
| 234 | data = [col0, col1, col2, col3] |
| 235 | w = open('tablewin.demo', data) |
| 236 | gwin.mainloop() |
| 237 | return w |