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 <stdbool.h>
24#include <stdlib.h>
25#include <string.h>
26#include <assert.h>
27#include <curses.h>
28#include "libtop.h"
29#include "generic.h"
30#include "log.h"
31#include "top.h"
32
33enum { HEADER_SIZE = 1 };
34
35int generic_draw_header(struct statistic *s, int x, int y, int anchor) {
36    if(s->header) {
37	generic_draw_extended(s, x, y, anchor, s->header, strlen(s->header));
38	y += HEADER_SIZE;
39    }
40
41    return y;
42}
43
44void generic_draw_aligned(struct statistic *s, int x) {
45    int y = 0;
46    struct generic_cells *cells;
47    int peaklength = 0;
48    int maxy;
49    size_t i;
50
51    cells = s->cells;
52
53    if(NULL == cells)
54	return;
55
56    /* This is needed for panels. */
57    werase(s->window);
58
59    y = generic_draw_header(s, x, y, GENERIC_DRAW_LEFT);
60
61    maxy = s->actual_size.height;
62
63    peaklength = s->actual_size.width;
64
65    for(i = 0; i < cells->length && y < maxy; ++i, ++y) {
66	int len = cells->array[i].length;
67     	int localx;
68
69	localx = peaklength - len;
70
71	if(len <= 0)
72	    continue;
73
74	if(ERR == mvwaddnstr(s->window, y, localx,
75			     cells->array[i].string, len)) {
76	    int erry, errx;
77	    getmaxyx(s->window, erry, errx);
78
79	    top_log("mvwaddnstr error in %s\n", __func__);
80	    top_log("error info: s->header %s y %d x %d n %d string: %s\n"
81		    "s->window width %d height %d\n",
82		    s->header, y, localx + x, len,
83		    cells->array[i].string,
84		    errx, erry);
85	}
86    }
87}
88
89void generic_draw(struct statistic *s, int x) {
90    int y = 0;
91    size_t i;
92    struct generic_cells *cells;
93    int maxy;
94
95    cells = s->cells;
96
97    if(NULL == cells)
98	return;
99
100    /* This is needed for panels. */
101    werase(s->window);
102
103    y = generic_draw_header(s, x, y, GENERIC_DRAW_LEFT);
104
105    maxy = s->actual_size.height;
106
107    for(i = 0; i < cells->length && y < maxy; ++i, ++y) {
108	generic_draw_extended(s, x, y, GENERIC_DRAW_LEFT,
109			      cells->array[i].string,
110			      cells->array[i].length);
111    }
112}
113
114/*
115 * This is meant to be called by a statistic draw function.
116 * It's not directly for use in a callback.
117 */
118void generic_draw_extended(struct statistic *s, int xoffset, int yoffset, int anchor, const char *string, int slen) {
119    int yheight, xwidth, xpos;
120    int n;
121
122    xwidth = s->actual_size.width;
123    yheight = s->actual_size.height;
124
125    switch(anchor) {
126    case GENERIC_DRAW_LEFT:
127	n = xwidth - xoffset;
128	if(n > slen)
129	    n = slen;
130
131	mvwaddnstr(s->window, yoffset, xoffset, string, n);
132	break;
133
134    case GENERIC_DRAW_CENTERED:
135	/* Find the middle, and then subtract half of the string. */
136	xpos = ((xwidth - xoffset) / 2) - (slen / 2);
137
138	if(xpos < 0) {
139      	    /*
140	     * The string resulted in an offset less than the requested
141	     * offset.  We may need to truncate it later.
142	     */
143	    xpos = 0;
144	}
145
146	n = slen;
147
148	if((slen + xpos) >= xwidth) {
149	    /* The n chars is too large for this stat width. */
150	    n = xwidth - xpos - xoffset;
151	}
152
153	if(n < 0) {
154	    xpos = 0;
155	    n = 0;
156	}
157
158	mvwaddnstr(s->window, yoffset, xpos + xoffset, string, n);
159	break;
160
161    case GENERIC_DRAW_RIGHT:
162	xpos = xwidth - slen - xoffset;
163
164	if(xpos < xoffset) {
165	    /* The string is too big to fit. */
166	    xpos = xoffset;
167	}
168
169	n = xwidth - xpos;
170	if(n > slen)
171	    n = slen;
172
173	mvwaddnstr(s->window, yoffset, xpos + xoffset, string, n);
174	break;
175    }
176
177    wsyncup(s->window);
178}
179
180void generic_draw_centered(struct statistic *s, int x) {
181    struct generic_cells *cells;
182    size_t i;
183    int y = 0;
184
185    cells = s->cells;
186
187    if(NULL == cells)
188        return;
189
190    y = generic_draw_header(s, x, y, GENERIC_DRAW_CENTERED);
191
192    for(i = 0; i < cells->length; ++i) {
193        generic_draw_extended(s, x, y, GENERIC_DRAW_CENTERED,
194                              cells->array[i].string,
195			      cells->array[i].length);
196        ++y;
197    }
198}
199
200void generic_draw_right(struct statistic *s, int x) {
201    struct generic_cells *cells;
202    size_t i;
203    int y = 0;
204
205    cells = s->cells;
206
207    if(NULL == cells)
208        return;
209
210    y = generic_draw_header(s, x, y, GENERIC_DRAW_RIGHT);
211
212    for(i = 0; i < cells->length; ++i) {
213        generic_draw_extended(s, x, y, GENERIC_DRAW_RIGHT,
214                              cells->array[i].string,
215			      cells->array[i].length);
216        ++y;
217    }
218}
219
220bool generic_resize_cells(struct statistic *s, struct statistic_size *size) {
221    if(ERR == wresize(s->window, size->height, size->width))
222        return true;
223
224    return false;
225}
226
227bool generic_move_cells(struct statistic *s, int x, int y) {
228    if(ERR == move_panel(s->panel, y, x))
229        return true;
230
231    return false;
232}
233
234void generic_get_request_size(struct statistic *s) {
235    struct generic_cells *cells;
236
237    cells = s->cells;
238
239    if(NULL == cells)
240	return;
241
242    s->request_size.width = cells->max_width;
243    s->request_size.height = cells->length;
244}
245
246static struct generic_cells *alloc_generic_cells(void) {
247    size_t i, length = 10;
248    struct generic_cells *cells;
249
250    cells = malloc(sizeof *cells);
251    if(NULL == cells)
252	return NULL;
253
254    cells->array = malloc(sizeof(*(cells->array)) * length);
255    if(NULL == cells->array) {
256	free(cells);
257	return NULL;
258    }
259
260    for(i = 0; i < length; ++i) {
261	cells->array[i].string = NULL;
262	cells->array[i].length = 0;
263	cells->array[i].allocated_length = 0;
264    }
265
266    cells->max_width = 0;
267    cells->length = 0;
268    cells->length_allocated = length;
269
270    return cells;
271}
272
273static void free_generic_cells(struct generic_cells *cells) {
274    size_t i;
275
276    for(i = 0; i < cells->length_allocated; ++i) {
277	if(cells->array[i].string) {
278	    free(cells->array[i].string);
279	}
280    }
281
282    free(cells->array);
283    free(cells);
284}
285
286static void generic_cell_destructor(struct statistic *s, void *ptr) {
287    free_generic_cells(s->cells);
288}
289
290/* Return true if an error occurred. */
291bool generic_insert_cell(struct statistic *s, const char *sample) {
292    struct generic_cells *cells = s->cells;
293    size_t offset;
294    int sample_length = (int)strlen(sample);
295    int sample_z_length = sample_length + 1;
296
297#if 0
298    top_log("%s %s\n", __func__, sample);
299#endif
300
301    if(NULL == cells) {
302	cells = s->cells = alloc_generic_cells();
303
304	if(NULL == cells)
305	    return true;
306
307	if(create_statistic_destructor(s, generic_cell_destructor, NULL))
308	    return true;
309    }
310
311
312    /* Update the max_width if the sample_length is greater. */
313    if(sample_length > cells->max_width) {
314	cells->max_width = sample_length;
315
316	if(cells->max_width > s->actual_size.width) {
317	    /* This requests a top_layout at a later time. */
318	    top_relayout(s->controller, s->type, cells->max_width);
319	}
320    }
321
322    offset = cells->length;
323
324    cells->length += 1;
325
326    if(cells->length >= cells->length_allocated) {
327	/* Resize the array to store the length and more. */
328
329	size_t newlength = cells->length_allocated * 2;
330	size_t i;
331	cells->array = realloc(cells->array,
332			       sizeof(*(cells->array)) * newlength);
333
334	if(NULL == cells->array)
335	    return true;
336
337	for(i = cells->length_allocated; i < newlength; ++i) {
338	    cells->array[i].string = NULL;
339	    cells->array[i].length = 0;
340	    cells->array[i].allocated_length = 0;
341	}
342
343	cells->length_allocated = newlength;
344    }
345
346    if(0 == sample_length) {
347	cells->array[offset].length = 0;
348	return false;
349    }
350
351    if(cells->array[offset].string
352       && cells->array[offset].allocated_length >= sample_z_length) {
353	/* We have an existing buffer that should fit. */
354	memcpy(cells->array[offset].string, sample, sample_z_length);
355	cells->array[offset].length = sample_length;
356    } else {
357	/* There wasn't enough space or the string was NULL. */
358	free(cells->array[offset].string);
359
360	cells->array[offset].string = malloc(cells->max_width + 1);
361
362	if(NULL == cells->array[offset].string) {
363	    cells->array[offset].length = 0;
364	    return true;
365	}
366
367	cells->array[offset].length = sample_length;
368	cells->array[offset].allocated_length = cells->max_width + 1;
369	memcpy(cells->array[offset].string, sample, sample_z_length);
370    }
371
372    return false;
373}
374
375void generic_reset_insertion(struct statistic *s) {
376    struct generic_cells *cells;
377
378    cells = s->cells;
379    if(NULL == cells)
380	return;
381
382    cells->length = 0;
383}
384
385void generic_get_minimum_size(struct statistic *s) {
386    struct generic_cells *cells;
387
388    cells = s->cells;
389
390    if(NULL == cells)
391	return;
392
393
394    s->minimum_size.width = cells->max_width;
395    s->minimum_size.height = cells->length;
396
397    if(s->minimum_size.width < 4)
398	s->minimum_size.width = 4;
399}
400