1/* 2 * Copyright (c) 2008 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * The contents of this file constitute Original Code as defined in and 7 * are subject to the Apple Public Source License Version 1.1 (the 8 * "License"). You may not use this file except in compliance with the 9 * License. Please obtain a copy of the License at 10 * http://www.apple.com/publicsource and read it before using this file. 11 * 12 * This Original Code and all software distributed under the License are 13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER 14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the 17 * License for the specific language governing rights and limitations 18 * under the License. 19 * 20 * @APPLE_LICENSE_HEADER_END@ 21 */ 22 23#include <stdlib.h> 24#include <assert.h> 25#include <math.h> 26#include "layout.h" 27#include "top.h" 28#include "log.h" 29 30enum { COLUMN_PADDING = 1 }; 31 32struct enlarge_data { 33 int add; 34 int maxy; 35 int count; 36}; 37 38static bool enlarge_iterator(struct statistic *s, void *ptr) { 39 struct enlarge_data *data = ptr; 40 int size; 41 42 if(s->request_size.width > s->minimum_size.width) { 43 size = s->request_size.width; 44 } else { 45 size = s->minimum_size.width; 46 } 47 48 if(data->count > 0) 49 size += COLUMN_PADDING; 50 51 s->actual_size.width = size; 52 53 s->actual_size.width += data->add; 54 s->actual_size.height = data->maxy; 55 56 data->count += 1; 57 58 /* Return true for more iterations (if possible). */ 59 return true; 60} 61 62/* This assumes that toadd is >= 1.0, so the caller must verify that. */ 63static void enlarge(struct statistics_controller *c, 64 int maxy, int toadd) { 65 struct enlarge_data data; 66 67 data.add = toadd; 68 data.maxy = maxy; 69 data.count = 0; 70 71 c->iterate(c, enlarge_iterator, &data); 72} 73 74 75struct do_layout_data { 76 int x, y; 77 bool error; 78}; 79 80static bool do_layout_iterator(struct statistic *s, void *ptr) { 81 struct do_layout_data *data = ptr; 82 struct statistic_size firstsize = {.width = 1, .height = 1}; 83 84 if(s->callbacks.resize_cells(s, &firstsize)) { 85 data->error = true; 86 return false; 87 } 88 89 if(s->callbacks.move_cells(s, data->x, data->y)) { 90 top_log("error: moving cells for %s!\n", s->header); 91 top_log("error info: data->x %d data->y %d\n", 92 data->x, data->y); 93 94 data->error = true; 95 /*stop iterating*/ 96 return false; 97 } 98 99 if(s->callbacks.resize_cells(s, &s->actual_size)) { 100 top_log("error: resizing cells for %s!\n", s->header); 101 top_log("error info: data->x %d s->actual_size.width %d " 102 "s->actual_size.height %d\n", data->x, s->actual_size.width, 103 s->actual_size.height); 104 105 data->error = true; 106 /*stop iterating*/ 107 return false; 108 } 109 110 data->x += s->actual_size.width; 111 112 /*continue iterating*/ 113 return true; 114} 115 116static bool do_layout(struct statistics_controller *c, int y) { 117 struct do_layout_data data; 118 119 data.x = 0; 120 data.y = y; 121 data.error = false; 122 123 c->iterate(c, do_layout_iterator, &data); 124 125 return data.error; 126} 127 128struct get_size_data { 129 struct statistic_size *reqsize; 130 struct statistic_size *minsize; 131 int count; 132}; 133 134/* Return true if this iterator wants more data. */ 135static bool get_size_iterator(struct statistic *s, void *ptr) { 136 struct get_size_data *data = ptr; 137 int minwidth, reqwidth; 138 139 s->callbacks.get_request_size(s); 140 s->callbacks.get_minimum_size(s); 141 142 minwidth = s->minimum_size.width; 143 reqwidth = s->request_size.width; 144 145 /* Skip padding the first column's left side. */ 146 if(data->count > 0) { 147 minwidth += COLUMN_PADDING; 148 reqwidth += COLUMN_PADDING; 149 } 150 151 data->minsize->width += minwidth; 152 153 if(s->minimum_size.height > data->minsize->height) 154 data->minsize->height = s->minimum_size.height; 155 156 if(reqwidth > minwidth) { 157 data->reqsize->width += reqwidth; 158 } else { 159 data->reqsize->width += minwidth; 160 } 161 162 if(s->request_size.height > data->reqsize->height) 163 data->reqsize->height = s->request_size.height; 164 165 data->count += 1; 166 167 return true; 168} 169 170static void get_size(struct statistics_controller *c, 171 struct statistic_size *reqsize, 172 struct statistic_size *minsize) { 173 struct get_size_data data; 174 175 reqsize->width = 0; 176 reqsize->height = 0; 177 minsize->width = 0; 178 minsize->height = 0; 179 180 data.reqsize = reqsize; 181 data.minsize = minsize; 182 data.count = 0; 183 184 c->iterate(c, get_size_iterator, &data); 185} 186 187struct minsize_fit_data { 188 int maxy; 189 int count; 190}; 191 192static bool minsize_fit_iterator(struct statistic *s, void *ptr) { 193 struct minsize_fit_data *data = ptr; 194 195 s->actual_size.width = s->minimum_size.width; 196 197 /* Don't pad the first column's left side. */ 198 if(data->count > 0) 199 s->actual_size.width += COLUMN_PADDING; 200 201 s->actual_size.height = data->maxy; 202 203 data->count += 1; 204 205 /*more iterations*/ 206 return true; 207} 208 209static void minsize_fit(struct statistics_controller *c, int maxy) { 210 struct minsize_fit_data data; 211 212 data.maxy = maxy; 213 data.count = 0; 214 215 c->iterate(c, minsize_fit_iterator, &data); 216} 217 218/* 219 * The layout algorithm: 220 * We first attempt to fit as many statistics as possible using the 221 * minimum size of each. If we are at a point where the width of the 222 * terminal is such that the total statistics is equal to the total 223 * possible statistics, then we attempt to use the request size. 224 * If the request size exceeds the terminal size, then we fall back 225 * to the minimum size. If the request size fits, then we try to 226 * enlarge proportionately every column, as long as the enlargement 227 * is > 1 for each column. 228 */ 229 230/* Return true if an error occurred. */ 231/* The caller will probably want to schedule another layout after a failure. */ 232bool layout_statistics(struct statistics_controller *c, int maxx, int maxy, 233 int y) { 234 struct statistic_size reqsize, minsize; 235 int total_stats = 0; 236 int inserts = 0, removes = 0; 237 238 if(maxy <= 0) 239 return false; 240 241 while(1) { 242 get_size(c, &reqsize, &minsize); 243 total_stats = c->get_total_active(c); 244 245 if(minsize.width == maxx) { 246 minsize_fit(c, maxy); 247 break; 248 } else if(minsize.width > maxx) { 249 /* The minsize is greater than the total window width. */ 250 251 if(total_stats > 1) { 252 /* Mark that we removed to prevent an endless loop. */ 253 ++removes; 254 //werase(c->parent); 255 c->remove_tail(c); 256 //werase(c->parent); 257 continue; 258 } 259 260 minsize_fit(c, maxy); 261 break; 262 } else { 263 if(0 == removes && total_stats < c->get_total_possible(c)) { 264 ++inserts; 265 c->insert_tail(c); 266 continue; 267 } 268 269 minsize_fit(c, maxy); 270 break; 271 } 272 } 273 274 //werase(c->parent); 275 276 total_stats = c->get_total_active(c); 277 if(total_stats >= c->get_total_possible(c)) { 278 /* 279 * We have all of the possible statistics. 280 * Now we attempt to enlarge to the request size or beyond. 281 */ 282 get_size(c, &reqsize, &minsize); 283 284 if(reqsize.width == maxx) { 285 /* The request size fit exactly. */ 286 enlarge(c, maxy, /*add*/ 0); 287 } else if(reqsize.width > maxx) { 288 /* Fallback to the minsize. */ 289 minsize_fit(c, maxy); 290 } else { 291 /* reqsize.width < maxx */ 292 /* 293 * We can probably enlarge some if not all of the stats. 294 */ 295 int xdelta = maxx - reqsize.width; 296 int enlargement = xdelta / c->get_total_possible(c); 297 298 if(enlargement > 0) { 299 enlarge(c, maxy, enlargement); 300 } else { 301 enlarge(c, maxy, 0); 302 } 303 } 304 } 305 306 top_log("c->get_total_active(c) is %d\n", c->get_total_active(c)); 307 308 if(do_layout(c, y)) 309 return true; 310 311 return false; 312} 313