1 /* GKrellM
2 |  Copyright (C) 1999-2019 Bill Wilson
3 |
4 |  Author:  Bill Wilson    billw@gkrellm.net
5 |  Latest versions might be found at:  http://gkrellm.net
6 |
7 |
8 |  GKrellM is free software: you can redistribute it and/or modify it
9 |  under the terms of the GNU General Public License as published by
10 |  the Free Software Foundation, either version 3 of the License, or
11 |  (at your option) any later version.
12 |
13 |  GKrellM is distributed in the hope that it will be useful, but WITHOUT
14 |  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 |  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
16 |  License for more details.
17 |
18 |  You should have received a copy of the GNU General Public License
19 |  along with this program. If not, see http://www.gnu.org/licenses/
20 |
21 |
22 |  Additional permission under GNU GPL version 3 section 7
23 |
24 |  If you modify this program, or any covered work, by linking or
25 |  combining it with the OpenSSL project's OpenSSL library (or a
26 |  modified version of that library), containing parts covered by
27 |  the terms of the OpenSSL or SSLeay licenses, you are granted
28 |  additional permission to convey the resulting work.
29 |  Corresponding Source for a non-source form of such a combination
30 |  shall include the source code for the parts of OpenSSL used as well
31 |  as that of the covered work.
32 */
33 
34 #include "gkrellm.h"
35 #include "gkrellm-private.h"
36 
37 #include <math.h>
38 
39 
40 static void	set_grid_resolution_spin_button(GkrellmChart *, gint);
41 
42 
43   /* For grid images of height 2 pixels, make room at bottom of chartdata
44   |  window so both pixel lines will show.
45   */
46 #define GRID_HEIGHT_Y_OFFSET_ADJUST(cp) \
47 		(( gdk_pixbuf_get_height((cp)->bg_grid_piximage->pixbuf) < 2) ? 0 : 1)
48 
49   /* Map internal y values with origin at lower left to X screen coordinates
50   |  which have origin at upper left.
51   */
52 #define Y_SCREEN_MAP(cp,y)	((cp)->h - (y) - 1)
53 
54 
55 #define	MIN_CHARTHEIGHT	5
56 #define	MAX_CHARTHEIGHT	200
57 
58 static GList	*chart_list;
59 
60 // #define DEBUG1
61 // #define DEBUG2
62 // #define DEBUG3
63 
64 /* ----------------------------------------------------------------------- */
65 
66 static gint
67 computed_index(GkrellmChart *cp, gint i)
68 	{
69 	gint	x;
70 
71 	x = (cp->position + i + 1) % cp->w;
72 	return x;
73 	}
74 
75 GList *
76 gkrellm_get_chart_list()
77 	{
78 	return chart_list;
79 	}
80 
81 gint
82 gkrellm_get_chart_scalemax(GkrellmChart *cp)
83 	{
84 	if (!cp)
85 		return 0;
86 	return cp->scale_max;
87 	}
88 
89 gint
90 gkrellm_get_current_chartdata(GkrellmChartdata *cd)
91 	{
92 	if (!cd)
93 		return 0;
94 	return cd->data[cd->chart->position];
95 	}
96 
97 gint
98 gkrellm_get_chartdata_data(GkrellmChartdata *cd, gint index)
99 	{
100 	gint	x;
101 
102 	if (!cd)
103 		return 0;
104 	x = computed_index(cd->chart, index);
105 	return cd->data[x];
106 	}
107 
108 void
109 gkrellm_clear_chart(GkrellmChart *cp)
110 	{
111 	if (!cp)
112 		return;
113 	gdk_draw_drawable(cp->pixmap, _GK.draw1_GC, cp->bg_src_pixmap,
114 						0, 0,	0, 0,   cp->w, cp->h);
115 	gdk_draw_drawable(cp->bg_pixmap, _GK.draw1_GC, cp->bg_src_pixmap,
116 						0, 0,	0, 0,   cp->w, cp->h);
117 	if (cp->drawing_area->window)
118 		gdk_draw_drawable(cp->drawing_area->window, _GK.draw1_GC, cp->pixmap,
119 						0, 0,	0, 0,   cp->w, cp->h);
120 	}
121 
122 void
123 gkrellm_clear_chart_pixmap(GkrellmChart *cp)
124 	{
125 	if (!cp)
126 		return;
127 	gdk_draw_drawable(cp->pixmap, _GK.draw1_GC, cp->bg_src_pixmap,
128 						0, 0,	0, 0,   cp->w, cp->h);
129 	gdk_draw_drawable(cp->bg_pixmap, _GK.draw1_GC, cp->bg_src_pixmap,
130 						0, 0,	0, 0,   cp->w, cp->h);
131 	}
132 
133 void
134 gkrellm_clean_bg_src_pixmap(GkrellmChart *cp)
135 	{
136 	if (!cp)
137 		return;
138 	if (!gkrellm_winop_draw_rootpixmap_onto_transparent_chart(cp))
139 		gdk_draw_drawable(cp->bg_src_pixmap, _GK.draw1_GC,
140 				cp->bg_clean_pixmap, 0, 0, 0, 0, cp->w, cp->h);
141 	cp->bg_sequence_id += 1;
142 	}
143 
144 void
145 gkrellm_draw_chart_grid_line(GkrellmChart *cp, GdkPixmap *pixmap, gint y)
146 	{
147 	gint	h;
148 
149 	if (!cp)
150 		return;
151 	gdk_drawable_get_size(cp->bg_grid_pixmap, NULL, &h);
152 	gdk_draw_drawable(pixmap, _GK.draw1_GC,
153 				cp->bg_grid_pixmap, 0, 0, cp->x, y, cp->w, h);
154 	}
155 
156 void
157 gkrellm_draw_chart_to_screen(GkrellmChart *cp)
158 	{
159 	/* Draw the expose pixmap onto the screen.
160 	*/
161 	if (cp && cp->drawing_area->window)
162 		gdk_draw_drawable(cp->drawing_area->window, _GK.draw1_GC, cp->pixmap,
163 						0, 0,	0, 0,   cp->w, cp->h);
164 	}
165 
166 static void
167 default_draw_chart_function(GkrellmChart *cp)
168 	{
169 	if (!cp)
170 		return;
171 	gkrellm_draw_chartdata(cp);
172 	gkrellm_draw_chart_to_screen(cp);
173 	}
174 
175 void
176 gkrellm_set_draw_chart_function(GkrellmChart *cp, void (*func)(), gpointer data)
177 	{
178 	if (!cp)
179 		return;
180 	cp->draw_chart = func;
181 	cp->draw_chart_data = data;
182 	}
183 
184 void
185 gkrellm_scale_chartdata(GkrellmChartdata *cd, gint multiplier, gint divisor)
186 	{
187 	gint	i;
188 
189 	if (!cd || !cd->data || divisor < 1)
190 		return;
191 	for (i = 0; i < cd->chart->w; ++i)
192 		cd->data[i] = cd->data[i] * multiplier / divisor;
193 	cd->previous = cd->previous * multiplier / divisor;
194 	}
195 
196 void
197 gkrellm_offset_chartdata(GkrellmChartdata *cd, gint offset)
198 	{
199 	gint	i;
200 
201 	if (!cd || !cd->data)
202 		return;
203 	for (i = 0; i < cd->chart->w; ++i)
204 		cd->data[i] = cd->data[i] + offset;
205 	cd->previous = cd->previous + offset;
206 	}
207 
208 void
209 gkrellm_reset_chart(GkrellmChart *cp)
210 	{
211 	GList			*list;
212 	GkrellmChartdata *cd;
213 	gint			i;
214 
215 	cp->scale_max = 0;
216 	cp->maxval = 0;
217 	cp->redraw_all = TRUE;
218 	cp->position = cp->w - 1;
219 	cp->primed = FALSE;
220 
221 	for (list = cp->cd_list; list; list = list->next)
222 		{
223 		cd = (GkrellmChartdata *) list->data;
224 		cd->maxval = 0;
225 		cd->previous = 0;
226 		if (cd->data)
227 			for (i = 0; i < cp->w; ++i)
228 				cd->data[i] = 0;
229 		}
230 	}
231 
232 void
233 gkrellm_reset_and_draw_chart(GkrellmChart *cp)
234 	{
235 	if (!cp)
236 		return;
237 	gkrellm_reset_chart(cp);
238 	if (cp->draw_chart)
239 		(*(cp->draw_chart))(cp->draw_chart_data);
240 	}
241 
242 void
243 gkrellm_monotonic_chartdata(GkrellmChartdata *cd, gboolean value)
244 	{
245 	if (cd)
246 		cd->monotonic = value;
247 	}
248 
249 void
250 gkrellm_set_chartdata_draw_style(GkrellmChartdata *cd, gint dstyle)
251 	{
252 	if (cd)
253 		cd->draw_style = dstyle;
254 	}
255 
256 void
257 gkrellm_set_chartdata_draw_style_default(GkrellmChartdata *cd, gint dstyle)
258 	{
259 	if (cd && cd->chart->config && !cd->chart->config->config_loaded)
260 		cd->draw_style = dstyle;
261 	}
262 
263 void
264 gkrellm_set_chartdata_flags(GkrellmChartdata *cd, gint flags)
265 	{
266 	if (cd)
267 		cd->flags = flags;
268 	}
269 
270 static void
271 gk_draw_vertical_line(GdkDrawable *drawable, GdkGC *gc, gint x, gint y1, gint y2)
272 	{
273 	if (y1 == y2)
274 		gdk_draw_point(drawable, gc, x, y1);
275 	else
276 		gdk_draw_line(drawable, gc, x, y1, x, y2);
277 	}
278 
279 
280 static gint
281 chartdata_ycoord(GkrellmChart *cp, GkrellmChartdata *cd, gint yd)
282 	{
283 	glong	y;
284 	guint64	Y;
285 
286 	if (cp->scale_max <= 0)
287 		cp->scale_max = 1;
288 
289 	if (yd > 2000000000 / MAX_CHARTHEIGHT)
290 		{
291 		Y = (guint64) yd * (guint64) cd->h;
292 		y = Y / cp->scale_max;
293 		}
294 	else
295 		y = ((glong) yd * cd->h / cp->scale_max);
296 
297 	if (y < 0)
298 		y = 0;
299 	if (y >= cd->h)
300 		y = cd->h - 1;
301 	if (cd->inverted)
302 		y = cd->h - y - 1;
303 	y += cd->y;
304 	return Y_SCREEN_MAP(cp, y);
305 	}
306 
307 static void
308 draw_layer_grid_lines(GkrellmChart *cp)
309 	{
310 	GList			*list;
311 	GkrellmChartdata *cd;
312 	gint			y, y0, h, grid_res, lines;
313 	gint			active_split, current_split;
314 	gboolean		do_next_split, done_once_per_split, tmp;
315 
316 	gdk_draw_drawable(cp->bg_pixmap, _GK.draw1_GC,
317 				cp->bg_src_pixmap, 0, 0,  0, 0,  cp->w, cp->h);
318 	do_next_split = TRUE;
319 	for (active_split = 0; do_next_split; ++active_split)
320 		{
321 		do_next_split = FALSE;
322 		done_once_per_split = FALSE;
323 		current_split = 0;
324 		for (list = cp->cd_list; list; list = list->next)
325 			{
326 			cd = (GkrellmChartdata *) list->data;
327 			if (cd->hide)
328 				continue;
329 			current_split += cd->split_chart;
330 			if (active_split != current_split)
331 				{
332 				if (current_split > active_split)
333 					do_next_split = TRUE;
334 				continue;
335 				}
336 			gdk_draw_drawable(cd->layer.pixmap, _GK.draw1_GC,
337 					*(cd->layer.src_pixmap), 0, 0,  0, 0,  cp->w, cp->h);
338 
339 			grid_res = cp->config->grid_resolution;
340 			lines = cp->scale_max / grid_res;
341 			if (lines && cd->h / lines > 2)	/* No grids if h is too small */
342 				{
343 				for (y = 0; y <= cp->scale_max; y += grid_res)
344 					{
345 					if (   _GK.bg_grid_mode == GRID_MODE_RESTRAINED
346 						&& ((y == 0 && cp->y == 0) || y == cp->scale_max)
347 					   )
348 						continue;
349 					tmp = cd->inverted;	  /* Draw grid lines in one direction*/
350 					cd->inverted = FALSE; /* else, may not line up by 1 pixel*/
351 					y0 = chartdata_ycoord(cp, cd, y);
352 					cd->inverted = tmp;
353 					gdk_drawable_get_size(cd->layer.grid_pixmap, NULL, &h);
354 					gdk_draw_drawable(cd->layer.pixmap, _GK.draw1_GC,
355 						cd->layer.grid_pixmap, 0, 0, cp->x, y0, cp->w, h);
356 
357 					if (!done_once_per_split)
358 						{
359 						gdk_drawable_get_size(cp->bg_grid_pixmap, NULL, &h);
360 						gdk_draw_drawable(cp->bg_pixmap, _GK.draw1_GC,
361 							cp->bg_grid_pixmap, 0, 0, cp->x, y0, cp->w, h);
362 						}
363 					}
364 				}
365 			if (current_split > 0 && !done_once_per_split)
366 				{
367 				y = cd->y - 1;		/* Get separator y value */
368 				y -= GRID_HEIGHT_Y_OFFSET_ADJUST(cp);
369 				if (y >= 0)
370 					{
371 					gdk_draw_drawable(cp->bg_pixmap, _GK.draw1_GC,
372 							_GK.bg_separator_pixmap,
373 							0, 0, cp->x, Y_SCREEN_MAP(cp, y),
374 							cp->w, _GK.bg_separator_height);
375 					}
376 				}
377 			done_once_per_split = TRUE;
378 			}
379 		}
380 	}
381 
382   /* Return TRUE as long as there is a next split with impulse data needing
383   |  to be drawn.
384   */
385 static gboolean
386 draw_chartdata_impulses(GkrellmChart *cp, GList *cd_list,
387 			gint i0, gint active_split)
388 	{
389 	GList			*list;
390 	GkrellmChartdata *cd;
391 	gint			n, x, y, y0, y1, yN, yI;
392 	gint			current_split;
393 	gboolean		need_next_split = FALSE;
394 
395 	if (!cd_list)
396 		return FALSE;
397 	for (n = i0; n < cp->w; ++n)
398 		{
399 		x = computed_index(cp, n);
400 		y0 = y1 = -1;
401 		yN = yI = 0;
402 		current_split = 0;
403 		for (list = cp->cd_list; list; list= list->next)
404 			{
405 			cd = (GkrellmChartdata *) list->data;
406 			if (cd->hide)
407 				continue;
408 			current_split += cd->split_chart;
409 			if (   cd->draw_style != CHARTDATA_IMPULSE
410 				|| current_split != active_split
411 			   )
412 				{
413 				if (   current_split > active_split
414 					&& cd->draw_style == CHARTDATA_IMPULSE
415 				   )
416 					need_next_split = TRUE;
417 				continue;
418 				}
419 			if (cd->inverted)
420 				{
421 				if (y1 < 0)
422 					y1 = chartdata_ycoord(cp, cd, 0);
423 				yI += cd->data[x];
424 				y = chartdata_ycoord(cp, cd, yI);
425 				if (cd->data[x] > 0)
426 					gk_draw_vertical_line(cd->data_bitmap, _GK.bit1_GC, n, y1, y);
427 				y1 = y;
428 				}
429 			else
430 				{
431 				if (y0 < 0)
432 					y0 = chartdata_ycoord(cp, cd, 0);
433 				yN += cd->data[x];
434 				y = chartdata_ycoord(cp, cd, yN);
435 				if (cd->data[x] > 0)
436 					gk_draw_vertical_line(cd->data_bitmap, _GK.bit1_GC, n, y0, y);
437 				y0 = y;
438 				}
439 			}
440 		}
441 	/* Push the grided pixmaps through the data bitmaps onto the expose pixmap
442 	*/
443 	current_split = 0;
444 	for (list = cp->cd_list; list; list = list->next)
445 		{
446 		cd = (GkrellmChartdata *) list->data;
447 		if (cd->hide)
448 			continue;
449 		current_split += cd->split_chart;
450 		if (cd->draw_style == CHARTDATA_LINE || current_split != active_split)
451 			continue;
452 		if (cd->maxval > 0)
453 			{
454 			y0 = chartdata_ycoord(cp, cd, 0);
455 			y1 = chartdata_ycoord(cp, cd, cd->maxval);
456 			gdk_gc_set_clip_mask(_GK.draw1_GC, cd->data_bitmap);
457 			if (cd->inverted)
458 				gdk_draw_drawable(cp->pixmap, _GK.draw1_GC, cd->layer.pixmap,
459 						0, y0,  0, y0,  cp->w, y1 - y0 + 1);
460 			else
461 				gdk_draw_drawable(cp->pixmap, _GK.draw1_GC, cd->layer.pixmap,
462 						0, y1,  0, y1,  cp->w, y0 - y1 + 1);
463 			}
464 		}
465 	return need_next_split;
466 	}
467 
468 static gint
469 fix_y(gint yn, gint yp, gint ypp)
470 	{
471 	gint	y;
472 
473 	if (yp > ypp && yn < yp)
474 		{
475 		y = (yp + yn) / 2;
476 		if (y < ypp)
477 			y = ypp;
478 		}
479 	else if (yp < ypp && yn > yp)
480 		{
481 		y = (yp + yn) / 2;
482 		if (y > ypp)
483 			y = ypp;
484 		}
485 	else
486 		y = yp;
487 	return y;
488 	}
489 
490 static void
491 draw_chartdata_lines(GkrellmChart *cp, GkrellmChartdata *cd, gint i0)
492 	{
493 	gint	n, x, xp, y, y0, y1;
494 
495 	y0 = chartdata_ycoord(cp, cd, 0);
496 	for (n = i0; n < cp->w; ++n)
497 		{
498 		x = computed_index(cp, n);
499 		y1 = chartdata_ycoord(cp, cd, cd->data[x]);
500 		if (n == 0)
501 			cd->y_p = y1;
502 		else if (!cd->y_p_valid && i0 == cp->w - 1)
503 			{
504 			if ((xp = x - 1) < 0)
505 				xp = cp->w - 1;
506 			cd->y_p = cd->y_pp = chartdata_ycoord(cp, cd, cd->data[xp]);
507 			}
508 		y = fix_y(y1, cd->y_p, cd->y_pp);
509 		cd->y_pp = y;
510 		cd->y_p = y1;
511 		cd->y_p_valid = FALSE;	/* Need a store_chartdata to make it valid */
512 		if (cd->data[x] > 0 || (cd->inverted ? (y > y0) : (y < y0)))
513 			{
514 			gk_draw_vertical_line(cd->data_bitmap, _GK.bit1_GC, cp->x + n, y, y1);
515 			}
516 		}
517 	/* Push the grided pixmap through the data bitmap onto the expose pixmap
518 	*/
519 	if (cd->maxval > 0)
520 		{
521 		y0 = chartdata_ycoord(cp, cd, 0);
522 		y1 = chartdata_ycoord(cp, cd, cd->maxval);
523 		gdk_gc_set_clip_mask(_GK.draw1_GC, cd->data_bitmap);
524 		if (cd->inverted)
525 			gdk_draw_drawable(cp->pixmap, _GK.draw1_GC, cd->layer.pixmap,
526 					0, y0,  0, y0,  cp->w, y1 - y0 + 1);
527 		else
528 			gdk_draw_drawable(cp->pixmap, _GK.draw1_GC, cd->layer.pixmap,
529 					0, y1,  0, y1,  cp->w, y0 - y1 + 1);
530 		}
531 	}
532 
533   /* See the README for description of auto grid resolution behavior.
534   */
535 static void
536 set_auto_grid_resolution(GkrellmChart *cp, gint maxval)
537 	{
538 	GkrellmChartconfig	*cf = cp->config;
539 	gint				grids, grid_res, maxval_base;
540 
541 	if (maxval <= cp->maxval_auto_base)
542 		maxval = cp->maxval_auto_base;
543 	else
544 		{
545 		if (maxval > cp->maxval_peak)
546 			cp->maxval_peak = maxval;
547 		maxval_base = maxval / FULL_SCALE_GRIDS;
548 		if (maxval_base > cp->maxval_auto_base)
549 			cp->maxval_auto_base = maxval_base;
550 		}
551 	cp->maxval_auto = maxval;
552 
553 	grids = cf->fixed_grids;
554 	if (grids == 0)		/* Auto grids mode */
555 		grid_res = gkrellm_125_sequence(cp->maxval_auto_base, cf->sequence_125,
556 					cf->low, cf->high, TRUE, FALSE);
557 	else
558 		{
559 		if (cf->auto_resolution_stick)
560 			maxval = cp->maxval_peak;
561 		grid_res = gkrellm_125_sequence(maxval / grids, cf->sequence_125,
562 					cf->low, cf->high, TRUE, TRUE);
563 		}
564 	if (grid_res != cf->grid_resolution)
565 		{
566 		cf->grid_resolution = grid_res;
567 		set_grid_resolution_spin_button(cp, grid_res);
568 		if (cf->cb_grid_resolution && !cf->cb_block)
569 			(*cf->cb_grid_resolution)(cf, cf->cb_grid_resolution_data);
570 		cp->redraw_all = TRUE;
571 		}
572 	cp->auto_recalibrate_delay = 0;
573 	}
574 
575 static gboolean
576 auto_recalibrate(GkrellmChart *cp)
577 	{
578 	if (++cp->auto_recalibrate_delay < 10)
579 		return FALSE;
580 	cp->maxval_peak = 0;
581 	cp->maxval_auto_base = 0;
582 	return TRUE;
583 	}
584 
585 static gint
586 setup_chart_scalemax(GkrellmChart *cp)
587 	{
588 	GkrellmChartconfig *cf = cp->config;
589 	glong			scalemax;
590 	gint			grid_res, i0;
591 
592 	/* maxval may change at any gkrellm_store_chartdata(), so at each chart
593 	|  draw compute a scalemax and compare to last cp->scale_max.
594 	|  Redraw grided background if different.
595 	*/
596 	if (cf->auto_grid_resolution)
597 		{
598 		if (   cp->maxval != cp->maxval_auto
599 			&& (   cp->maxval > cp->maxval_auto
600 				|| cp->maxval_auto != cp->maxval_auto_base
601 			   )
602 		   )
603 			set_auto_grid_resolution(cp, cp->maxval);
604 		else if (   !cf->auto_resolution_stick
605 				 && cp->maxval < cp->maxval_auto_base / FULL_SCALE_GRIDS
606 				)
607 			{
608 			if (auto_recalibrate(cp))
609 				set_auto_grid_resolution(cp, cp->maxval);
610 			}
611 		else
612 			cp->auto_recalibrate_delay = 0;
613 		}
614 	grid_res = cf->grid_resolution;
615 	if (cf->fixed_grids)
616 		scalemax = grid_res * cf->fixed_grids;
617 	else	/* Auto scale to cp->maxval */
618 		{
619 		if (cp->maxval == 0)
620 			scalemax = grid_res;
621 		else
622 			scalemax = ((cp->maxval - 1) / grid_res + 1) * grid_res;
623 		if (cp->previous_total && scalemax > grid_res * FULL_SCALE_GRIDS)
624 			scalemax = grid_res * FULL_SCALE_GRIDS;
625 		}
626 	if (scalemax != cp->scale_max || cp->redraw_all)
627 		{
628 		cp->redraw_all = FALSE;
629 		i0 = 0;				/* Will draw all data on chart */
630 		cp->scale_max = scalemax;
631 		draw_layer_grid_lines(cp);
632 		}
633 	else
634 		i0 = cp->w - 1;		/* Will draw the last data point only */
635 	return i0;
636 	}
637 
638 void
639 gkrellm_draw_chartdata(GkrellmChart *cp)
640 	{
641 	GList			*list;
642 	GkrellmChartdata *cd;
643 	gint			i0, active_split, current_split;
644 	gboolean		have_impulse_splits = FALSE;
645 
646 	if (!cp)
647 		return;
648 	i0 = setup_chart_scalemax(cp);
649 	gdk_draw_drawable(cp->pixmap, _GK.draw1_GC, cp->bg_pixmap,
650 						0, 0,	0, 0,   cp->w, cp->h);
651 
652 	current_split = active_split = 0;
653 	for (list = cp->cd_list; list; list = list->next)
654 		{
655 		cd = (GkrellmChartdata *) list->data;
656 		if (cd->hide)
657 			continue;
658 		current_split += cd->split_chart;
659 		if (!have_impulse_splits && cd->draw_style == CHARTDATA_IMPULSE)
660 			{
661 			have_impulse_splits = TRUE;
662 			active_split = current_split;
663 			}
664 		/* Clear the area of the data bitmaps that data will be drawn into
665 		*/
666 		gdk_draw_rectangle(cd->data_bitmap, _GK.bit0_GC, TRUE,
667 					i0, 0, cd->w - i0, cp->h);
668 		}
669 
670 	for (  ; have_impulse_splits; ++active_split)
671 		have_impulse_splits = draw_chartdata_impulses(cp, cp->cd_list,
672 					i0, active_split);
673 
674 	for (list = cp->cd_list; list; list = list->next)
675 		{
676 		cd = (GkrellmChartdata *) list->data;
677 		if (cd->draw_style == CHARTDATA_LINE && !cd->hide)
678 			draw_chartdata_lines(cp, cd, i0);
679 		}
680 	gdk_gc_set_clip_mask(_GK.draw1_GC, NULL);
681 	}
682 
683 void
684 gkrellm_alloc_chartdata(GkrellmChart *cp)
685 	{
686 	GList			*list;
687 	GkrellmChartdata *cd;
688 	size_t			w;
689 
690 	if (!cp)
691 		return;
692 	w = (size_t) cp->w;
693 	for (list = cp->cd_list; list; list = list->next)
694 		{
695 		cd = (GkrellmChartdata *) list->data;
696 		if (cd->w == w && cd->data)
697 			continue;
698 		cd->w = w;
699 		if (cd->data)
700 			g_free(cd->data);
701 		cd->data = (gulong *) g_new0(gulong, w);
702 		cd->maxval = 0;
703 		cp->position = cp->w - 1;
704 		cp->tail = cp->position;
705 		}
706 	cp->alloc_width = w;
707 	cp->maxval = 0;
708 	cp->scale_max = 0;
709 	cp->redraw_all = TRUE;
710 	}
711 
712 static void
713 scroll_chartdata_bitmaps(GkrellmChart *cp)
714 	{
715 	GList			*list;
716 	GkrellmChartdata *cd;
717 
718 	for (list = cp->cd_list; list; list = list->next)
719 		{
720 		cd = (GkrellmChartdata *) list->data;
721 		if (cd->hide)
722 			continue;
723 		gdk_draw_drawable(cd->data_bitmap, _GK.bit1_GC, cd->data_bitmap,
724 						1, 0, 0, 0, cp->w - 1, cp->h);
725 		}
726 	}
727 
728 static gboolean
729 scan_for_impulse_maxval(GkrellmChart *cp, gint active_split)
730 	{
731 	GList			*list;
732 	GkrellmChartdata *cd;
733 	gint			N, I;
734 	gint			i, current_split;
735 	gboolean		need_next_split = FALSE;
736 
737 	for (i = 0; i < cp->w; ++i)
738 		{
739 		/* N is normal and I inverted cumulative impulse data/split
740 		*/
741 		N = I = 0;
742 		current_split = 0;
743 		for (list = cp->cd_list; list; list = list->next)
744 			{
745 			cd = (GkrellmChartdata *) list->data;
746 			if (cd->hide)
747 				continue;
748 			current_split += cd->split_chart;
749 			if (   cd->draw_style != CHARTDATA_IMPULSE
750 				|| current_split != active_split
751 			   )
752 				{
753 				if (   current_split > active_split
754 					&& cd->draw_style == CHARTDATA_IMPULSE
755 				   )
756 					need_next_split = TRUE;
757 				continue;
758 				}
759 			if (cd->inverted)
760 				{
761 				I += cd->data[i];
762 				if (I > cd->maxval)
763 					cd->maxval = I;
764 				}
765 			else
766 				{
767 				N += cd->data[i];
768 				if (N > cd->maxval)
769 					cd->maxval = N;
770 				}
771 			if (N + I > cp->maxval)
772 				cp->maxval = N + I;
773 			}
774 		}
775 	return need_next_split;
776 	}
777 
778 static void
779 scan_for_maxval(GkrellmChart *cp)
780 	{
781 	GList			*list;
782 	GkrellmChartdata *cd;
783 	gint			i, current_split, active_split;
784 	gboolean		have_impulse_splits = FALSE;
785 
786 	cp->maxval = 0;
787 	current_split = 0;
788 	active_split = 0;
789 	for (list = cp->cd_list; list; list = list->next)
790 		{
791 		cd = (GkrellmChartdata *) list->data;
792 		if (cd->hide)
793 			continue;
794 		cd->maxval = 0;
795 		current_split += cd->split_chart;
796 		if (cd->draw_style == CHARTDATA_LINE)
797 			for (i = 0; i < cp->w; ++i)
798 				{
799 				if (cd->data[i] > cd->maxval)
800 				    cd->maxval = cd->data[i];
801 				if (cd->maxval > cp->maxval)
802 					cp->maxval = cd->maxval;
803 				}
804 		if (!have_impulse_splits && cd->draw_style == CHARTDATA_IMPULSE)
805 			{
806 			have_impulse_splits = TRUE;
807 			active_split = current_split;
808 			}
809 		}
810 	for ( ; have_impulse_splits; ++active_split)
811 		have_impulse_splits = scan_for_impulse_maxval(cp, active_split);
812 	}
813 
814 void
815 gkrellm_store_chartdata(GkrellmChart *cp, gulong total, ...)
816 	{
817 	va_list			args;
818 
819 	if (!cp)
820 		return;
821 	va_start(args, total);
822 	gkrellm_store_chartdatav(cp, total, args);
823 	va_end(args);
824 	}
825 
826 void
827 gkrellm_store_chartdatav(GkrellmChart *cp, gulong total, va_list args)
828 	{
829 	GList			*list;
830 	GkrellmChartdata *cd;
831 	gulong			range, total_diff;
832 	gint			n, N_discard, I_discard, N, I;
833 	gint			active_split, current_split;
834 	gboolean		need_scan = FALSE;
835 
836 	if (!cp)
837 		return;
838 	for (list = cp->cd_list; list; list = list->next)
839 		{
840 		cd = (GkrellmChartdata *) list->data;
841 		//FIXME: missing check for number of passed varargs passed in "args"
842 		cd->current = va_arg(args, gulong);
843 		if (!cd->monotonic)
844 			{
845 			cd->previous = 0;
846 			cp->previous_total = 0;		/* All or none if total is used */
847 			}
848 		/* Prime the pump.  Also handle data wrap around or reset to zero.
849 		*/
850 		cd->wrap = 0;
851 		if (!cp->primed)
852 			cd->previous = cd->current;
853 		else if (cd->current < cd->previous)
854 			{
855 			cd->wrap = (gulong) -1 - cd->previous;
856 			cd->previous = 0;
857 			}
858 		}
859 	if (total < cp->previous_total || !cp->primed)
860 		cp->previous_total = total;	  /* Wrap around, this store won't scale */
861 	total_diff = total - cp->previous_total;
862 	cp->previous_total = total;
863 
864 	/* Increment position in circular buffer and remember the data
865 	|  value to be thrown out.
866 	*/
867 	cp->position = (cp->position + 1) % cp->w;
868 	cp->tail = (cp->tail + 1) % cp->w;
869 	n = cp->position;
870 	active_split = current_split = 0;
871 	N_discard = I_discard = 0;
872 
873 	/* N is normal and I inverted cumulative impulse data/split
874 	*/
875 	N = I = 0;
876 	for (list = cp->cd_list; list; list = list->next)
877 		{
878 		cd = (GkrellmChartdata *) list->data;
879 		cd->discard = cd->data[cp->tail];
880 		cd->data[n] = (gulong)(cd->current - cd->previous) + cd->wrap;
881 		cd->previous = cd->current;
882 
883 		/* If using totals, scale the stored data to range between 0 and the
884 		|  max chart value.  Max chart value is number of grids * grid res.
885 		|  No. of grids is 5 in auto grid mode.  For plotting data as a %.
886 		*/
887 		if (total_diff > 0)
888 			{
889 			range = (cp->config->fixed_grids ? cp->config->fixed_grids
890 							: FULL_SCALE_GRIDS) * cp->config->grid_resolution;
891 			if (range != total_diff)
892 				cd->data[n] = cd->data[n] * range / total_diff;
893 			}
894 		if (cd->hide || need_scan)
895 			continue;
896 
897 		/* Compare discarded data to new data (accounting for stacked impulse
898 		|  data) and decide if a new maxval must be set (new data > maxval)
899 		|  or if a complete rescan of the data is needed to find a new
900 		|  maxval (a discard > a new).
901 		*/
902 		current_split += cd->split_chart;
903 		if (cd->draw_style == CHARTDATA_IMPULSE)
904 			{
905 			if (current_split != active_split)
906 				{
907 				active_split = current_split;
908 				N_discard = I_discard = 0;
909 				N = I = 0;
910 				}
911 			if (cd->inverted)
912 				{
913 				I_discard += cd->discard;
914 				I += cd->data[n];
915 				if (I_discard && I_discard >= cd->maxval)
916 					need_scan = TRUE;
917 				else if (I > cd->maxval)
918 					cd->maxval = I;
919 				}
920 			else
921 				{
922 				N_discard += cd->discard;
923 				N += cd->data[n];
924 				if (N_discard && N_discard >= cd->maxval)
925 					need_scan = TRUE;
926 				else if (N > cd->maxval)
927 					cd->maxval = N;
928 				}
929 			if (N_discard + I_discard >= cd->maxval)
930 				need_scan = TRUE;
931 			else if (N + I > cp->maxval)
932 				cp->maxval = N + I;
933 			}
934 		else if (cd->draw_style == CHARTDATA_LINE)
935 			{
936 			cd->y_p_valid = TRUE;
937 			if (cd->discard && cd->discard >= cd->maxval)
938 				need_scan = TRUE;
939 			else
940 				{
941 				if (cd->data[n] > cd->maxval)
942 					cd->maxval = cd->data[n];
943 				if (cd->maxval > cp->maxval)
944 					cp->maxval = cd->maxval;
945 				}
946 			}
947 		}
948 	cp->primed = TRUE;
949 	if (need_scan || cp->redraw_all)
950 		scan_for_maxval(cp);
951 	scroll_chartdata_bitmaps(cp);
952 	}
953 
954 
955 
956 /* =================================================================== */
957 
958 static void
959 chart_destroy_text_run_list(GkrellmChart *cp)
960 	{
961 	GList			*list;
962 
963 	if (!cp || !cp->text_run_list)
964 		return;
965 	for (list = cp->text_run_list; list; list = list->next)
966 		g_free(((GkrellmTextRun *) list->data)->text);
967 	gkrellm_free_glist_and_data(&cp->text_run_list);
968 	}
969 
970 static gint
971 chartdata_text_y(GkrellmChart *cp, char key, gint height,
972 				gint y_ink, gint shadow)
973 	{
974 	GList				*list;
975 	GkrellmChartdata	*cd;
976 	gint				n, y;
977 
978 	n = key - '0';
979 	y = -100;
980 	if (n >= 0)
981 		{
982 		list = g_list_nth(cp->cd_list, n / 2);
983 		if (list)
984 			{
985 			cd = (GkrellmChartdata *) list->data;
986 			if (!cd->hide)
987 				{
988 				if (n & 1)	/* Justify 2 pixels from top of ChartData view */
989 					{
990 					y = cd->y + cd->h + y_ink - 3;
991 					}
992 				else		/* Justify to bottom of ChartData view */
993 					{
994 					y = cd->y + height + y_ink + shadow - 1;
995 					}
996 				y = Y_SCREEN_MAP(cp, y);
997 				}
998 			}
999 		}
1000 	return y;
1001 	}
1002 
1003 void
1004 gkrellm_chart_reuse_text_format(GkrellmChart *cp)
1005 	{
1006 	cp->text_format_reuse = TRUE;
1007 	}
1008 
1009 void
1010 gkrellm_draw_chart_text(GkrellmChart *cp, gint style_id, gchar *str)
1011 	{
1012 	GList			*list;
1013 	GkrellmTextRun	*tr;
1014 	GkrellmTextstyle *ts, *ts_default, *ts_alt;
1015 	gchar			c, *s, *t;
1016 	gint			h, text_length, field_width, fw;
1017 	gint			offset;
1018 	gint			xx, x, y, center, shadow;
1019 	gboolean		right, set_fw, fw_right;
1020 
1021 	if (!cp || !str)
1022 		return;
1023 
1024 	/* Assume text_format will be changed at each call unless caller has said
1025 	|  we can reuse it or the whole string compares equal.
1026 	*/
1027 	if (   !cp->text_format_reuse
1028 		&& !gkrellm_dup_string(&cp->text_format_string, str)
1029 	   )
1030 		cp->text_format_reuse = TRUE;
1031 
1032 	if (_GK.debug_level & DEBUG_CHART_TEXT)
1033 		{
1034 		g_debug("\n");
1035 		if (!cp->text_format_reuse)
1036 			g_debug("draw_chart_text: [%s]\n", str);
1037 		}
1038 
1039 	if (   !cp->text_format_reuse
1040 		|| cp->text_run_sequence_id != cp->bg_sequence_id
1041 	   )
1042 		chart_destroy_text_run_list(cp);
1043 	cp->text_run_sequence_id = cp->bg_sequence_id;
1044 	cp->text_format_reuse = FALSE;
1045 
1046 	ts_default = gkrellm_chart_textstyle(style_id);
1047 	ts_alt = gkrellm_chart_alt_textstyle(style_id);
1048 
1049 	x = xx = 2;
1050 	if (!cp->text_run_list)
1051 		gkrellm_text_extents(ts_default->font, _("Ag8"), 3, NULL,
1052 					&cp->h_text, &cp->baseline_ref, &cp->y_ink);
1053 
1054 	y = 2 - cp->y_ink;
1055 	h = fw = 0;
1056 	tr = NULL;
1057 	for (list = cp->text_run_list, s = str; *s; s += text_length)
1058 		{
1059 		if (!list)
1060 			{
1061 			tr = g_new0(GkrellmTextRun, 1);
1062 			cp->text_run_list = g_list_append(cp->text_run_list, tr);
1063 			}
1064 		else
1065 			{
1066 			tr = (GkrellmTextRun *) list->data;
1067 			list = list->next;
1068 			tr->cache_valid = TRUE;
1069 			}
1070 		c = '\0';
1071 		center = 0;
1072 		right = FALSE;
1073 		set_fw = FALSE;
1074 		fw_right = FALSE;
1075 		ts = ts_default;
1076 		field_width = 0;
1077 		shadow = ts_default->effect ? 1 : 0;
1078 		while (*s == '\\')
1079 			{
1080 			if ((c = *(++s)) != '\0')
1081 				++s;
1082 			if (c == 'n')
1083 				{
1084 				y += cp->h_text + 1;
1085 				x = xx;
1086 				}
1087 			else if (c == 'c')
1088 				center = 1;
1089 			else if (c == 'C')
1090 				center = 2;
1091 			else if (c == 'N')
1092 				{
1093 				x = xx;		/* A conditional newline.  If previous string */
1094 				if (h > 2)	/* was spaces, no nl.  A space has nonzero a  */
1095 					y += cp->h_text + 1;
1096 				}
1097 			else if (c == 'b')
1098 				{
1099 				y = cp->h - cp->h_text - cp->y_ink - 1;
1100 				x = xx;
1101 				}
1102 			else if (c == 't')
1103 				{
1104 				y = 2 - cp->y_ink;
1105 				x = xx;
1106 				}
1107 			else if (c == 'r')
1108 				right = TRUE;
1109 			else if (c == 'p')
1110 				{
1111 				y -= cp->h_text + 1;
1112 				x = xx;
1113 				}
1114 			else if (c == 'w')
1115 				set_fw = TRUE;
1116 			else if (c == 'a')
1117 				field_width = fw;
1118 			else if (c == 'e')
1119 				{
1120 				field_width = fw;
1121 				fw_right = TRUE;
1122 				}
1123 			else if (c == 'f')
1124 				{
1125 				ts = ts_alt;
1126 				shadow = ts_alt->effect ? 1 : 0;
1127 				}
1128 			else if (c == 'x' && isdigit((unsigned char)*s))
1129 				xx = *s++ - '0';
1130 			else if (c == 'y' && isdigit((unsigned char)*s))
1131 				y = *s++ - '0';
1132 			else if (c == 'D')
1133 				{
1134 				y = chartdata_text_y(cp, *s++, cp->h_text, cp->y_ink, shadow);
1135 				x = xx;
1136 				}
1137 			}
1138 		t = strchr(s, (gint) '\\');
1139 		if (t)
1140 			text_length = t - s;
1141 		else
1142 			text_length = strlen(s);
1143 
1144 		if (y == -100)	/* asked for a chartdata that is hidden */
1145 			continue;
1146 
1147 		if (   !tr->cache_valid || !tr->text
1148 			|| strncmp(tr->text, s, text_length)
1149 			|| strlen(tr->text) != text_length
1150 		   )
1151 			{
1152 			gkrellm_text_extents(ts->font, s, text_length,
1153 						&tr->w, &tr->h, &tr->baseline, &tr->y_ink);
1154 			tr->cache_valid = FALSE;
1155 			g_free(tr->text);
1156 			tr->text = g_strndup(s, text_length);
1157 			}
1158 		h = tr->h;
1159 
1160 		if (set_fw)
1161 			{
1162 			fw = tr->w + shadow;
1163 			continue;
1164 			}
1165 		if (center == 1)
1166 			x = (cp->w - tr->w) / 2;
1167 		else if (center == 2)
1168 			x = cp->w / 2;
1169 		else if (fw_right)
1170 			x = x + fw - tr->w - shadow;
1171 		else if (right)
1172 			x = cp->w - tr->w - 2 - shadow;
1173 		if (text_length > 1 || (text_length == 1 && *s != ' '))
1174 			{
1175 			if (x != tr->x || y != tr->y)
1176 				tr->cache_valid = FALSE;
1177 			tr->x = x;
1178 			tr->y = y;
1179 
1180 			if (_GK.debug_level & DEBUG_CHART_TEXT)
1181 				{
1182 				gchar	buf[128];
1183 
1184 				strncpy(buf, s, text_length);
1185 				buf[text_length] = '\0';
1186 				g_debug("draw_chart_text: [%s] ", buf);
1187 				}
1188 
1189 			offset = cp->baseline_ref - tr->baseline;	/* align baselines */
1190 			if (_GK.chart_text_no_fill)
1191 	    		gkrellm_draw_text(cp->pixmap, ts, x, y + offset, s,
1192 						text_length);
1193 			else /* Default text draw effect is fill solid and can use cache */
1194 				{
1195 				if (!tr->cache_valid)
1196 					{
1197 					if (_GK.debug_level & DEBUG_CHART_TEXT)
1198 						g_debug("pango ");
1199 					gdk_draw_drawable(cp->bg_text_pixmap, _GK.draw1_GC,
1200 							cp->bg_pixmap,
1201 							x - 1, y + tr->y_ink + offset - 1,
1202 							x - 1, y + tr->y_ink + offset - 1,
1203 							tr->w + shadow + 2, tr->h + shadow + 1);
1204 		    		gkrellm_draw_text(cp->bg_text_pixmap, ts, x, y + offset, s,
1205 							text_length);
1206 					}
1207 				if (_GK.debug_level & DEBUG_CHART_TEXT)
1208 					g_debug("x=%d y=%d w=%d h=%d\n",
1209 							x - 1, y + tr->y_ink + offset - 1,
1210 							tr->w + shadow + 2, tr->h + shadow + 1);
1211 				gdk_draw_drawable(cp->pixmap, _GK.draw1_GC, cp->bg_text_pixmap,
1212 						x - 1, y + tr->y_ink + offset - 1,
1213 						x - 1, y + tr->y_ink + offset - 1,
1214 						tr->w + shadow + 2, tr->h + shadow + 1);
1215 				}
1216 			}
1217 		if (field_width && !fw_right)
1218 			x += (field_width > tr->w) ? field_width : tr->w;
1219 		else
1220 			x += tr->w + shadow;
1221 		}
1222 	}
1223 
1224 gint
1225 gkrellm_draw_chart_label(GkrellmChart *cp, GkrellmTextstyle *ts,
1226 				gint x, gint y, gchar *s)
1227 	{
1228 	gint	w, h, y_ink;
1229 
1230 	if (!cp || !ts || !s)
1231 		return x;
1232 
1233 	gkrellm_text_extents(ts->font, s, strlen(s), &w, &h, NULL, &y_ink);
1234 
1235 	gdk_draw_drawable(cp->pixmap, _GK.draw1_GC, cp->bg_pixmap,
1236 				x, y, x, y, w + ts->effect, h + ts->effect);
1237     gkrellm_draw_string(cp->pixmap, ts, x, y - y_ink, s);
1238 
1239 	return x + w + ts->effect;
1240 	}
1241 
1242 void
1243 gkrellm_destroy_chartdata_list(GList **cd_list_head)
1244 	{
1245 	GList			*list;
1246 	GkrellmChartdata *cd;
1247 
1248 	if (!cd_list_head)
1249 		return;
1250 	for (list = *cd_list_head; list; list = list->next)
1251 		{
1252 		cd = (GkrellmChartdata *) list->data;
1253 		if (cd->label)
1254 			g_free(cd->label);
1255 		if (cd->data)
1256 			g_free(cd->data);
1257 		if (cd->data_bitmap)
1258 			g_object_unref(G_OBJECT(cd->data_bitmap));
1259 		if (cd->layer.pixmap)
1260 			g_object_unref(G_OBJECT(cd->layer.pixmap));
1261 		/* cd->layer.src_pixmap & cd->layer.grid_pixmap must be handled by
1262 		|  creating monitor.
1263 		*/
1264 		}
1265 	gkrellm_free_glist_and_data(cd_list_head);
1266 	}
1267 
1268 
1269 static void
1270 free_chart_pixmaps(GkrellmChart *cp)
1271 	{
1272 	GList			*list;
1273 	GkrellmChartdata *cd;
1274 
1275 	for (list = cp->cd_list; list; list = list->next)
1276 		{
1277 		cd = (GkrellmChartdata *) list->data;
1278 		gkrellm_free_bitmap(&cd->data_bitmap);
1279 		gkrellm_free_pixmap(&cd->layer.pixmap);
1280 		/* cd->layer.src_pixmap & cd->layer.grid_pixmap must be handled by
1281 		|  creating monitor.
1282 		*/
1283 		}
1284 	/* If new theme or size, the cd_list will not be destroyed so I can
1285 	|  reuse the data arrays.   Pixmaps will be recreated.
1286 	*/
1287 	cp->cd_list_index = 0;
1288 
1289 	gkrellm_free_pixmap(&cp->bg_pixmap);
1290 	gkrellm_free_pixmap(&cp->bg_text_pixmap);
1291 	gkrellm_free_pixmap(&cp->bg_src_pixmap);
1292 	gkrellm_free_pixmap(&cp->bg_grid_pixmap);
1293 
1294 	gkrellm_free_pixmap(&cp->bg_clean_pixmap);
1295 	gkrellm_free_bitmap(&cp->bg_mask);
1296 
1297 	gkrellm_free_pixmap(&cp->pixmap);
1298 
1299 	gkrellm_free_pixmap(&cp->top_spacer.clean_pixmap);
1300 	gkrellm_free_pixmap(&cp->top_spacer.pixmap);
1301 	gkrellm_free_bitmap(&cp->top_spacer.mask);
1302 	gkrellm_free_pixmap(&cp->bottom_spacer.clean_pixmap);
1303 	gkrellm_free_pixmap(&cp->bottom_spacer.pixmap);
1304 	gkrellm_free_bitmap(&cp->bottom_spacer.mask);
1305 	}
1306 
1307 static void
1308 destroy_chart_data(GkrellmChart *cp)
1309 	{
1310 	GList			*list;
1311 	GkrellmChartdata *cd;
1312 
1313 	free_chart_pixmaps(cp);
1314 	chart_destroy_text_run_list(cp);
1315 	for (list = cp->cd_list; list; list = list->next)
1316 		{
1317 		cd = (GkrellmChartdata *) list->data;
1318 		if (cd->label)
1319 			g_free(cd->label);
1320 		if (cd->data)
1321 			g_free(cd->data);
1322 		cd->label = NULL;
1323 		cd->data = NULL;
1324 		}
1325 	/* Don't free the cd_list.  It is in the GkrellmChartconfig struct.
1326 	*/
1327 	}
1328 
1329   /* Destroy everything inside a chart except leave cp->config alone since
1330   |  the config is managed by each monitor.
1331   */
1332 void
1333 gkrellm_chart_destroy(GkrellmChart *cp)
1334 	{
1335 	gint	h_spacers = 0;
1336 
1337 	if (!cp)
1338 		return;
1339 
1340 	if (cp->top_spacer.image)
1341 		h_spacers = cp->top_spacer.height;
1342 	if (cp->bottom_spacer.image)
1343 		h_spacers += cp->bottom_spacer.height;
1344 
1345 	gkrellm_freeze_side_frame_packing();
1346 	if (cp->panel)
1347 		gkrellm_panel_destroy(cp->panel);
1348 	destroy_chart_data(cp);
1349 	gtk_widget_destroy(cp->box);
1350 	chart_list = g_list_remove(chart_list, cp);
1351 	gkrellm_chartconfig_window_destroy(cp);
1352 	if (cp->shown)
1353 		gkrellm_monitor_height_adjust(-(cp->h + h_spacers));
1354 	g_free(cp);
1355 	gkrellm_thaw_side_frame_packing();
1356 	}
1357 
1358 void
1359 gkrellm_chartconfig_destroy(GkrellmChartconfig **cf)
1360 	{
1361 	if (!cf || !*cf)
1362 		return;
1363 	gkrellm_destroy_chartdata_list((*cf)->chart_cd_list);
1364 	g_free(*cf);
1365 	*cf = NULL;
1366 	}
1367 
1368 void
1369 gkrellm_chart_bg_piximage_override(GkrellmChart *cp,
1370 			GkrellmPiximage *bg_piximage, GkrellmPiximage *bg_grid_piximage)
1371 	{
1372 	if (!cp || !bg_piximage || !bg_grid_piximage)
1373 		return;
1374 	cp->bg_piximage = bg_piximage;
1375 	cp->bg_grid_piximage = bg_grid_piximage;
1376 	cp->bg_piximage_override = TRUE;
1377 	}
1378 
1379 
1380 static void
1381 set_chartdata_split_heights(GkrellmChart *cp)
1382 	{
1383 	GList			*list, *list1;
1384 	GkrellmChartdata *cd, *cd1;
1385 	gint			splits;
1386 	gint			i, y0, h_avail, h_free, y_offset;
1387 
1388 	for (i = 0, splits = 0, list = cp->cd_list; list; list = list->next)
1389 		{
1390 		cd = (GkrellmChartdata *) list->data;
1391 		if (cd->hide)
1392 			continue;
1393 		if (++i > 1 && cd->split_chart)	/* Can't split before the first one */
1394 			++splits;
1395 		cd->split_share = 1.0;
1396 		for (list1 = list->next; list1; list1 = list1->next)
1397 			{
1398 			cd1 = (GkrellmChartdata *) list1->data;
1399 			if (!cd1->split_chart || cd1->hide)
1400 				continue;
1401 			cd->split_share = cd1->split_fraction;
1402 			break;
1403 			}
1404 		}
1405 	y_offset = GRID_HEIGHT_Y_OFFSET_ADJUST(cp);
1406 	y0 = cp->y + y_offset;
1407 	h_avail = cp->h - cp->y - ((splits + 1) * y_offset)
1408 				- splits * _GK.bg_separator_height;
1409 	h_free = h_avail;
1410 	for (list = cp->cd_list; list; list = list->next)
1411 		{
1412 		cd = (GkrellmChartdata *) list->data;
1413 		if (cd->hide)
1414 			continue;
1415 		cd->y = y0;
1416 		for (list1 = list->next; list1; list1 = list1->next)
1417 			if (!((GkrellmChartdata *) list1->data)->hide)
1418 				break;
1419 		if (!list1)
1420 			cd->h = h_free;
1421 		else
1422 			cd->h = (gint) (cd->split_share * (gfloat) h_free);
1423 		if (cd->h < 1)
1424 			cd->h = 1;
1425 		if (list1 && ((GkrellmChartdata *) list1->data)->split_chart)
1426 			{
1427 			y0 += cd->h + _GK.bg_separator_height + y_offset;
1428 			h_free -= cd->h;
1429 			}
1430 		}
1431 	}
1432 
1433 GkrellmChartdata *
1434 gkrellm_add_default_chartdata(GkrellmChart *cp, gchar *label)
1435 	{
1436 	GdkPixmap	**src_pixmap, *grid_pixmap;
1437 
1438 	if (!cp)
1439 		return NULL;
1440 	if (cp->cd_list_index & 1)
1441 		{
1442 		src_pixmap = &_GK.data_in_pixmap;
1443 		grid_pixmap = _GK.data_in_grid_pixmap;
1444 		}
1445 	else
1446 		{
1447 		src_pixmap = &_GK.data_out_pixmap;
1448 		grid_pixmap = _GK.data_out_grid_pixmap;
1449 		}
1450 	return gkrellm_add_chartdata(cp, src_pixmap, grid_pixmap, label);
1451 	}
1452 
1453   /* Need a GdkPixmap ** because the src pixmap can change (re-rendered at
1454   |  size or theme changes).
1455   */
1456 GkrellmChartdata *
1457 gkrellm_add_chartdata(GkrellmChart *cp, GdkPixmap **src_pixmap,
1458 			GdkPixmap *grid_pixmap, gchar *label)
1459 	{
1460 	GtkWidget		*top_win	= gkrellm_get_top_window();
1461 	GList			*list;
1462 	GkrellmChartdata *cd;
1463 
1464 	if (!cp || !src_pixmap || !grid_pixmap || !label)
1465 		return NULL;
1466 
1467 	/* To handle theme and vert size changes without losing data, reuse the
1468 	|  GkrellmChartdata structs in the cd_list.
1469 	*/
1470 	list = g_list_nth(cp->cd_list, cp->cd_list_index++);
1471 	if (!list)
1472 		{
1473 		cd = g_new0(GkrellmChartdata, 1);
1474 		cp->cd_list = g_list_append(cp->cd_list, cd);
1475 		cp->config->cd_list = cp->cd_list;
1476 		cd->split_fraction = 0.5;
1477 		}
1478 	else
1479 		cd = (GkrellmChartdata *) list->data;
1480 	cd->chart = cp;
1481 	gkrellm_dup_string(&cd->label, label);
1482 	cd->monotonic = TRUE;
1483 	cd->data_bitmap = gdk_pixmap_new(top_win->window, cp->w, cp->h, 1);
1484 	cd->layer.pixmap = gdk_pixmap_new(top_win->window, cp->w, cp->h, -1);
1485 	cd->layer.src_pixmap = src_pixmap;
1486 	cd->layer.grid_pixmap = grid_pixmap;
1487 
1488 	set_chartdata_split_heights(cp);
1489 	return cd;
1490 	}
1491 
1492 void
1493 gkrellm_render_data_grid_pixmap(GkrellmPiximage *im, GdkPixmap **pixmap,
1494 			GdkColor *color)
1495 	{
1496 	GtkWidget   *top_win = gkrellm_get_top_window();
1497 	gint		w, w_pixmap = 0, h = 0;
1498 
1499 	w = gkrellm_chart_width();
1500 	if (*pixmap)
1501 		gdk_drawable_get_size(*pixmap, &w_pixmap, NULL);
1502 	if (!*pixmap || w != w_pixmap)
1503 		{
1504 		if (im)
1505 			{
1506 			if ((h = gdk_pixbuf_get_height(im->pixbuf)) > 2)
1507 				h = 2;
1508 			gkrellm_scale_piximage_to_pixmap(im, pixmap, NULL, w, h);
1509 			}
1510 		else
1511 			{
1512 			gkrellm_free_pixmap(pixmap);
1513 			*pixmap = gdk_pixmap_new(top_win->window, w, 1, -1);
1514 			if (color)
1515 				gdk_gc_set_foreground(_GK.draw1_GC, color);
1516 			else
1517 				gdk_gc_set_foreground(_GK.draw1_GC, &_GK.in_color_grid);
1518 			gdk_draw_rectangle(*pixmap, _GK.draw1_GC, TRUE, 0, 0, w, 1);
1519 			}
1520 		}
1521 	}
1522 
1523 void
1524 gkrellm_render_data_pixmap(GkrellmPiximage *im, GdkPixmap **pixmap,
1525 			GdkColor *color, gint h)
1526 	{
1527 	GtkWidget   *top_win = gkrellm_get_top_window();
1528 	gint		w, w_pixmap = 0, h_pixmap = 0;
1529 
1530 	w = gkrellm_chart_width();
1531 	if (*pixmap)
1532 		gdk_drawable_get_size(*pixmap, &w_pixmap, &h_pixmap);
1533 
1534 	if (!*pixmap || w != w_pixmap || h != h_pixmap)
1535 		{
1536 		if (!gkrellm_scale_piximage_to_pixmap(im, pixmap, NULL, w, h))
1537 			{
1538 			*pixmap = gdk_pixmap_new(top_win->window, w, h, -1);
1539 			if (color)
1540 				gdk_gc_set_foreground(_GK.draw1_GC, color);
1541 			else
1542 				gdk_gc_set_foreground(_GK.draw1_GC, &_GK.in_color_grid);
1543 			gdk_draw_rectangle(*pixmap, _GK.draw1_GC, TRUE, 0,0,w,h);
1544 			}
1545 		}
1546 	}
1547 
1548 static void
1549 render_default_data_pixmaps(GkrellmChart *cp)
1550 	{
1551 	GList		*list;
1552 	GdkPixmap	*pixmap;
1553 	gint		w, h, w_pixmap = 0;
1554 
1555 	gkrellm_render_data_grid_pixmap(_GK.data_in_grid_piximage,
1556 				&_GK.data_in_grid_pixmap, &_GK.in_color_grid);
1557 	gkrellm_render_data_grid_pixmap(_GK.data_out_grid_piximage,
1558 				&_GK.data_out_grid_pixmap, &_GK.out_color_grid);
1559 
1560 	w = gkrellm_chart_width();
1561 	pixmap = _GK.bg_separator_pixmap;
1562 	if (pixmap)
1563 		gdk_drawable_get_size(pixmap, &w_pixmap, NULL);
1564 	if (!pixmap || w_pixmap != w)
1565 		{
1566 		if ((h = _GK.bg_separator_height) < 1 || h > 5)
1567 			h = 2;
1568 		if (_GK.bg_separator_piximage)
1569 			gkrellm_scale_piximage_to_pixmap(_GK.bg_separator_piximage,
1570 						&_GK.bg_separator_pixmap, NULL, w, h);
1571 		else
1572 			{
1573 			GkrellmPiximage	*im;
1574 
1575 			im = gkrellm_bg_panel_piximage(DEFAULT_STYLE_ID);
1576 			gkrellm_scale_pixbuf_to_pixmap(im->pixbuf, &_GK.bg_separator_pixmap,
1577 					NULL, w, h);
1578 			}
1579 		}
1580 
1581 	h = 2;
1582 	for (list = chart_list; list; list = list->next)
1583 		{
1584 		cp = (GkrellmChart *) list->data;
1585 		if (cp->h > h)
1586 			h = cp->h;
1587 		}
1588 	gkrellm_render_data_pixmap(_GK.data_in_piximage,
1589 				&_GK.data_in_pixmap, &_GK.in_color, h);
1590 	gkrellm_render_data_pixmap(_GK.data_out_piximage,
1591 				&_GK.data_out_pixmap, &_GK.out_color, h);
1592 	}
1593 
1594 void
1595 gkrellm_chart_setup(void)
1596 	{
1597 	gkrellm_free_pixmap(&_GK.data_in_pixmap);
1598 	gkrellm_free_pixmap(&_GK.data_in_grid_pixmap);
1599 	gkrellm_free_pixmap(&_GK.data_out_pixmap);
1600 	gkrellm_free_pixmap(&_GK.data_out_grid_pixmap);
1601 	gkrellm_free_pixmap(&_GK.bg_separator_pixmap);
1602 	}
1603 
1604 #if 0
1605 static gint
1606 compare_chartlist(gconstpointer a, gconstpointer b)
1607     {
1608 	GkrellmChart	*cp_a	= (GkrellmChart *) a;
1609 	GkrellmChart	*cp_b	= (GkrellmChart *) b;
1610 	gint	result;
1611 
1612 	if (cp_a->style_id < cp_b->style_id)
1613 		result = -1;
1614 	else (if cp_a->style_id > cp_b->style_id)
1615 		result = 1;
1616 	else
1617 		result = 0;
1618 	return result;
1619     }
1620 #endif
1621 
1622 static void
1623 insert_in_chartlist(GkrellmChart *cp)
1624 	{
1625 	GList	*list;
1626 	GkrellmChart	*cp_x;
1627 	gint	position = 0;
1628 
1629 	for (list = chart_list; list; list = list->next, ++position)
1630 		{
1631 		cp_x = (GkrellmChart *) list->data;
1632 		if (cp_x->style_id > cp->style_id)
1633 			{
1634 			chart_list = g_list_insert(chart_list, cp, position);
1635 			return;
1636 			}
1637 		}
1638 	chart_list = g_list_append(chart_list, cp);
1639 	}
1640 
1641 void
1642 gkrellm_chart_hide(GkrellmChart *cp, gboolean hide_panel)
1643 	{
1644 	gint	h_spacers = 0;
1645 
1646 	if (!cp || !cp->shown)
1647 		return;
1648 
1649 	if (cp->top_spacer.image)
1650 		h_spacers = cp->top_spacer.height;
1651 	if (cp->bottom_spacer.image)
1652 		h_spacers += cp->bottom_spacer.height;
1653 
1654 	gtk_widget_hide(cp->box);
1655 	gkrellm_freeze_side_frame_packing();
1656 	if (hide_panel)
1657 		gkrellm_panel_hide(cp->panel);
1658 	gkrellm_monitor_height_adjust(- (cp->h + h_spacers));
1659 	cp->shown = FALSE;
1660 	gkrellm_thaw_side_frame_packing();
1661 	}
1662 
1663 void
1664 gkrellm_chart_show(GkrellmChart *cp, gboolean show_panel)
1665 	{
1666 	gint	h_spacers = 0;
1667 
1668 	if (!cp || cp->shown)
1669 		return;
1670 
1671 	if (cp->top_spacer.image)
1672 		h_spacers = cp->top_spacer.height;
1673 	if (cp->bottom_spacer.image)
1674 		h_spacers += cp->bottom_spacer.height;
1675 
1676 	gtk_widget_show(cp->box);
1677 	gkrellm_freeze_side_frame_packing();
1678 	if (show_panel)
1679 		gkrellm_panel_show(cp->panel);
1680 	cp->shown = TRUE;
1681 	gkrellm_monitor_height_adjust(cp->h + h_spacers);
1682 	gkrellm_thaw_side_frame_packing();
1683 	}
1684 
1685 gboolean
1686 gkrellm_is_chart_visible(GkrellmChart *cp)
1687 	{
1688 	if (!cp)
1689 		return FALSE;
1690 	return cp->shown;
1691 	}
1692 
1693 gboolean
1694 gkrellm_chart_enable_visibility(GkrellmChart *cp, gboolean new_vis,
1695 					gboolean *current_vis)
1696 	{
1697 	gboolean	changed = FALSE;
1698 
1699 	if (new_vis  && ! *current_vis)
1700 		{
1701 		gkrellm_chart_show(cp, TRUE);
1702 		*current_vis  = TRUE;
1703 		changed = TRUE;
1704 		}
1705 	if (!new_vis  && *current_vis)
1706 		{
1707 		gkrellm_chart_hide(cp, TRUE);
1708 		*current_vis  = FALSE;
1709 		changed = TRUE;
1710 		}
1711 	return changed;
1712 	}
1713 
1714 void
1715 gkrellm_set_chart_height_default(GkrellmChart *cp, gint h)
1716 	{
1717 	if (!cp || (cp->config && cp->config->config_loaded))
1718 		return;
1719 
1720 	if (h < MIN_CHARTHEIGHT)
1721 		h = MIN_CHARTHEIGHT;
1722 	if (h > MAX_CHARTHEIGHT)
1723 		h = MAX_CHARTHEIGHT;
1724 	cp->h = h;
1725 	}
1726 
1727 static void
1728 render_chart_margin_spacers(GkrellmChart *cp)
1729 	{
1730 	GkrellmMargin	*m;
1731 	GkrellmSpacer	*ts, *bs;
1732 
1733 	ts = &cp->top_spacer;
1734 	bs = &cp->bottom_spacer;
1735 	if (ts->image)
1736 		gtk_container_remove(GTK_CONTAINER(ts->vbox), ts->image);
1737 	if (bs->image)
1738 		gtk_container_remove(GTK_CONTAINER(bs->vbox), bs->image);
1739 	ts->image = bs->image = NULL;
1740 	m = gkrellm_get_style_margins(cp->style);
1741 	ts->piximage = cp->bg_piximage;
1742 	ts->height   = m->top;
1743 	bs->piximage = cp->bg_piximage;
1744 	bs->height   = m->bottom;
1745 
1746 	if (!gkrellm_render_spacer(ts, 0, m->top,
1747 				_GK.frame_left_chart_overlap, _GK.frame_right_chart_overlap))
1748 		gtk_widget_hide(ts->vbox);
1749 
1750 	if (!gkrellm_render_spacer(bs,
1751 				gdk_pixbuf_get_height(cp->bg_piximage->pixbuf) - m->bottom,
1752 				m->bottom,
1753 				_GK.frame_left_chart_overlap, _GK.frame_right_chart_overlap))
1754 		gtk_widget_hide(bs->vbox);
1755 	}
1756 
1757 #if 0
1758 static gboolean
1759 cb_chart_map_event(GtkWidget *widget, GdkEvent *event, GkrellmChart *cp)
1760     {
1761     gdk_window_get_position(cp->drawing_area->window, NULL, &cp->y_mapped);
1762 	if (_GK.frame_left_chart_overlap > 0 || _GK.frame_right_chart_overlap > 0)
1763 		_GK.need_frame_packing = TRUE;
1764     return FALSE;
1765     }
1766 #endif
1767 
1768 static gboolean
1769 cb_chart_size_allocate(GtkWidget *widget, GtkAllocation *size,
1770 				GkrellmChart *cp)
1771     {
1772     gdk_window_get_position(cp->drawing_area->window, NULL, &cp->y_mapped);
1773 	if (_GK.frame_left_chart_overlap > 0 || _GK.frame_right_chart_overlap > 0)
1774 		_GK.need_frame_packing = TRUE;
1775     return FALSE;
1776     }
1777 
1778 static void
1779 render_chart_pixmaps(GkrellmChart *cp)
1780 	{
1781 	GkrellmPiximage	piximage;
1782 	GkrellmMargin	*m;
1783 	gint			h, w;
1784 
1785 	m = gkrellm_get_style_margins(cp->style);
1786 	w = gdk_pixbuf_get_width(cp->bg_piximage->pixbuf)
1787 				- _GK.frame_left_chart_overlap - _GK.frame_right_chart_overlap;
1788 	h = gdk_pixbuf_get_height(cp->bg_piximage->pixbuf) - m->top - m->bottom;
1789 
1790 	if (   (   m->top > 0 || m->bottom > 0
1791 			|| _GK.frame_left_chart_overlap > 0
1792 			|| _GK.frame_right_chart_overlap > 0
1793 		   )
1794 		&& w > 0 && h > 0
1795 	   )
1796 		{
1797 		piximage.pixbuf = gdk_pixbuf_new_subpixbuf(cp->bg_piximage->pixbuf,
1798 				_GK.frame_left_chart_overlap, m->top, w, h);
1799 		piximage.border = cp->bg_piximage->border;
1800 		gkrellm_border_adjust(&piximage.border,
1801 				-_GK.frame_left_chart_overlap, -_GK.frame_right_chart_overlap,
1802 				-m->top, -m->bottom);
1803 		gkrellm_scale_piximage_to_pixmap(&piximage, &cp->bg_clean_pixmap,
1804 					&cp->bg_mask, cp->w, cp->h);
1805 		g_object_unref(G_OBJECT(piximage.pixbuf));
1806 		}
1807 	else
1808 		gkrellm_scale_piximage_to_pixmap(cp->bg_piximage,
1809 					&cp->bg_clean_pixmap, &cp->bg_mask, cp->w, cp->h);
1810 
1811 	gkrellm_clone_pixmap(&cp->pixmap, &cp->bg_clean_pixmap);
1812 	gkrellm_clone_pixmap(&cp->bg_pixmap, &cp->bg_clean_pixmap);
1813 	gkrellm_clone_pixmap(&cp->bg_src_pixmap, &cp->bg_clean_pixmap);
1814 	gkrellm_clone_pixmap(&cp->bg_text_pixmap, &cp->bg_clean_pixmap);
1815 	}
1816 
1817 void
1818 gkrellm_chart_create(GtkWidget *vbox, GkrellmMonitor *mon, GkrellmChart *cp,
1819 			GkrellmChartconfig **config)
1820 	{
1821 	GkrellmMargin	*m;
1822 	GkrellmChartconfig *cf;
1823 	gint			h, style_id;
1824 
1825 	if (!vbox || !mon || !cp || !config)
1826 		return;
1827 	if (mon->privat->style_type == CHART_PANEL_TYPE)
1828 		style_id = mon->privat->style_id;
1829 	else
1830 		style_id = DEFAULT_STYLE_ID;
1831 	cp->style = gkrellm_chart_style(style_id);
1832 	m = gkrellm_get_style_margins(cp->style);
1833 	cp->monitor = (gpointer) mon;
1834 	if (!cp->bg_piximage_override)
1835 		{
1836 		cp->bg_piximage = gkrellm_bg_chart_piximage(style_id);
1837 		cp->bg_grid_piximage = gkrellm_bg_grid_piximage(style_id);
1838 		}
1839 	cp->bg_piximage_override = FALSE;
1840 	cp->x = 0;
1841 /*	cp->y = 0; */
1842 	cp->w = _GK.chart_width;
1843 	if (!*config)
1844 		{
1845 		*config = gkrellm_chartconfig_new0();
1846 		(*config)->auto_grid_resolution = TRUE;		/* the default */
1847 		(*config)->h = cp->h;					/* In case default */
1848 		}
1849 	cf = cp->config = *config;
1850 	if (cf->h < 5)
1851 		cf->h = 40;
1852 	cp->h = cf->h;
1853 	if (cf->grid_resolution < 1)
1854 		cf->grid_resolution = 1;
1855 	cp->cd_list = cp->config->cd_list;
1856 	cp->config->chart_cd_list = &cp->cd_list;
1857 
1858 	if (!cp->box)
1859 		{
1860 		cp->box = gtk_vbox_new(FALSE, 0);	/* not a hbox anymore !! */
1861 		gtk_box_pack_start(GTK_BOX(vbox), cp->box, FALSE, FALSE, 0);
1862 
1863 		cp->top_spacer.vbox = gtk_vbox_new(FALSE, 0);
1864 		gtk_box_pack_start(GTK_BOX(cp->box), cp->top_spacer.vbox,
1865 				FALSE, FALSE, 0);
1866 		cp->drawing_area = gtk_drawing_area_new();
1867 		gtk_widget_set_events(cp->drawing_area, GDK_EXPOSURE_MASK
1868 				| GDK_LEAVE_NOTIFY_MASK | GDK_ENTER_NOTIFY_MASK
1869 				| GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK
1870 				| GDK_POINTER_MOTION_MASK);
1871 		gtk_box_pack_start(GTK_BOX(cp->box), cp->drawing_area,
1872 				FALSE, FALSE, 0);
1873 		cp->bottom_spacer.vbox = gtk_vbox_new(FALSE, 0);
1874 		gtk_box_pack_start(GTK_BOX(cp->box), cp->bottom_spacer.vbox,
1875 				FALSE, FALSE, 0);
1876 
1877 		gtk_widget_show(cp->drawing_area);
1878 		gtk_widget_show(cp->box);
1879 		cp->shown = TRUE;
1880 		gtk_widget_realize(cp->box);
1881 		gtk_widget_realize(cp->drawing_area);
1882 		cp->style_id = style_id;
1883 		insert_in_chartlist(cp);
1884 		cp->y_mapped = -1;
1885 //		g_signal_connect(G_OBJECT (cp->drawing_area), "map_event",
1886 //					G_CALLBACK(cb_chart_map_event), cp);
1887 		g_signal_connect(G_OBJECT (cp->drawing_area), "size_allocate",
1888 					G_CALLBACK(cb_chart_size_allocate), cp);
1889 		}
1890 	else
1891 		free_chart_pixmaps(cp);
1892 
1893 	gtk_widget_set_size_request(cp->drawing_area, cp->w, cp->h);
1894 
1895 	cp->bg_sequence_id += 1;
1896 
1897 	render_chart_pixmaps(cp);
1898 	render_chart_margin_spacers(cp);
1899 
1900 	h = gdk_pixbuf_get_height(cp->bg_grid_piximage->pixbuf);
1901 	if (h > 2)
1902 		h = 2;
1903 	gkrellm_scale_piximage_to_pixmap(cp->bg_grid_piximage,
1904 				&cp->bg_grid_pixmap, NULL, cp->w, h);
1905 
1906 	cp->transparency = cp->style->transparency;
1907 	_GK.any_transparency |= cp->transparency;
1908 
1909 	gkrellm_set_draw_chart_function(cp, default_draw_chart_function, cp);
1910 	cp->redraw_all = TRUE;
1911 	render_default_data_pixmaps(cp);
1912 	if (cp->shown)
1913 		{
1914 		gkrellm_monitor_height_adjust(cp->h + m->top + m->bottom);
1915 		gkrellm_pack_side_frames();
1916 		}
1917 	}
1918 
1919 void
1920 gkrellm_refresh_chart(GkrellmChart *cp)
1921 	{
1922 	if (!cp)
1923 		return;
1924 	cp->redraw_all = TRUE;
1925 	cp->maxval_auto = -1;
1926 	if (cp->draw_chart)
1927 		(*(cp->draw_chart))(cp->draw_chart_data);
1928 	}
1929 
1930 void
1931 gkrellm_rescale_chart(GkrellmChart *cp)
1932 	{
1933 	if (!cp)
1934 		return;
1935 	scan_for_maxval(cp);
1936 	gkrellm_refresh_chart(cp);
1937 	}
1938 
1939 /* =================================================================== */
1940 
1941 
1942 static gint		map_125_table[] =
1943 	{
1944 	1, 2, 5,
1945 	10, 20, 50,
1946 	100, 200, 500,
1947 	1000, 2000, 5000,
1948 	10000, 20000, 50000,
1949 	100000, 200000, 500000,
1950 	1000000, 2000000, 5000000,
1951 	10000000, 20000000, 50000000,
1952 	100000000, 200000000, 500000000
1953 	};
1954 
1955 static gint		map_12357_table[] =
1956 	{
1957 	1, 2, 3, 5, 7,
1958 	10, 15, 20, 30, 50, 70,
1959 	100, 150, 200, 300, 500, 700,
1960 	1000, 1500, 2000, 3000, 5000, 7000,
1961 	10000, 15000, 20000, 30000, 50000, 70000,
1962 	100000, 150000, 200000, 300000, 500000, 700000,
1963 	1000000, 1500000, 2000000, 3000000, 5000000, 7000000,
1964 	10000000, 15000000, 20000000, 30000000, 50000000, 70000000,
1965 	100000000, 150000000, 200000000, 300000000, 500000000, 700000000
1966 	};
1967 
1968 gint
1969 gkrellm_125_sequence(gint value, gboolean use125,
1970 			gint low, gint high,
1971 			gboolean snap_to_table, gboolean roundup)
1972 	{
1973 	gint    i, table_size;
1974 	gint	*table;
1975 
1976 	if (use125)
1977 		{
1978 		table = map_125_table;
1979 		table_size = sizeof(map_125_table) / sizeof(gint);
1980 		}
1981 	else
1982 		{
1983 		table = map_12357_table;
1984 		table_size = sizeof(map_12357_table) / sizeof(gint);
1985 		}
1986 	if (value < low)
1987 		value = low;
1988 	if (value > high)
1989 		value = high;
1990 	if (value < table[0])
1991 		return table[0];
1992 	if (value > table[table_size - 1])
1993 		return table[table_size - 1];
1994 	if (!snap_to_table && !roundup)
1995 		{
1996 		for (i = 0; i < table_size; ++i)
1997 			{
1998 /*g_debug("  mapping[%d] value=%d table=%d\n", i, value, table[i]); */
1999 			if (value == table[i])
2000 				return table[i];
2001 			else if (value == table[i] - 1)
2002 				return table[i - 1];
2003 			else if (value == table[i] + 1)
2004 				return table[i + 1];
2005 			}
2006 		}
2007 	else if (snap_to_table && !roundup)
2008 		{
2009 		for (i = table_size - 1; i >= 0; --i)
2010 			{
2011 			if (value >= table[i])
2012 				{
2013 				value = table[i];
2014 				break;
2015 				}
2016 			}
2017 		}
2018 	else if (snap_to_table && roundup)
2019 		{
2020 		for (i = 0; i < table_size; ++i)
2021 			{
2022 			if (value <= table[i])
2023 				{
2024 				value = table[i];
2025 				break;
2026 				}
2027 			}
2028 		}
2029 	return value;
2030 	}
2031 
2032 static void
2033 set_grid_resolution_spin_button(GkrellmChart *cp, gint res)
2034 	{
2035 	GtkSpinButton	*spin;
2036 
2037 	if (!cp || !cp->config_window || !cp->config->grid_resolution_spin_button)
2038 		return;
2039 	spin = GTK_SPIN_BUTTON(cp->config->grid_resolution_spin_button);
2040 	gtk_spin_button_set_value(spin, (gfloat) res);
2041 	}
2042 
2043 static void
2044 cb_chart_grid_resolution(GtkWidget *adjustment, GkrellmChart *cp)
2045 	{
2046 	GtkSpinButton	*spin;
2047 	GkrellmChartconfig		*cf;
2048 	gint			res;
2049 	gfloat			fres;
2050 
2051 	_GK.config_modified = TRUE;
2052 	cf = cp->config;
2053 	spin = GTK_SPIN_BUTTON(cf->grid_resolution_spin_button);
2054 	if (cf->map_sequence)
2055 		{
2056 		res = gtk_spin_button_get_value_as_int(spin);
2057 		if (res != cf->grid_resolution)
2058 			{
2059 			res = gkrellm_125_sequence(res, cf->sequence_125,
2060 						cf->low, cf->high, FALSE, FALSE);
2061 			cf->grid_resolution = res;
2062 			gtk_spin_button_set_value(spin, (gfloat) res);
2063 			if (cf->cb_grid_resolution && !cf->cb_block)
2064 				(*cf->cb_grid_resolution)(cf, cf->cb_grid_resolution_data);
2065 			gkrellm_refresh_chart(cp);
2066 			}
2067 		}
2068 	else
2069 		{
2070 		fres = gtk_spin_button_get_value(spin);
2071 		if (cf->spin_factor > 0.0)
2072 			fres *= cf->spin_factor;
2073 		cf->grid_resolution = (gint) fres;
2074 		if (cf->grid_resolution < 1)
2075 			cf->grid_resolution = 1;
2076 		if (cf->cb_grid_resolution && !cf->cb_block)
2077 			(*cf->cb_grid_resolution)(cf, cf->cb_grid_resolution_data);
2078 		gkrellm_refresh_chart(cp);
2079 		}
2080 	}
2081 
2082 
2083 /* ---- GkrellmChartconfig functions ---- */
2084 void
2085 gkrellm_chartconfig_callback_block(GkrellmChartconfig *cf, gboolean block)
2086 	{
2087 	if (!cf)
2088 		return;
2089 	cf->cb_block = block;
2090 	}
2091 
2092 void
2093 gkrellm_set_chartconfig_grid_resolution(GkrellmChartconfig *cf, gint res)
2094 	{
2095 	if (!cf || res <= 0)
2096 		return;
2097 	cf->grid_resolution = res;
2098 	}
2099 
2100 gint
2101 gkrellm_get_chartconfig_grid_resolution(GkrellmChartconfig *cf)
2102 	{
2103 	if (!cf)
2104 		return 0;
2105 	return cf->grid_resolution;
2106 	}
2107 
2108 void
2109 gkrellm_chartconfig_grid_resolution_connect(GkrellmChartconfig *cf,
2110 			void (*func)(gpointer), gpointer data)
2111 	{
2112 	if (!cf)
2113 		return;
2114 	cf->cb_grid_resolution = func;
2115 	cf->cb_grid_resolution_data = data;
2116 	}
2117 
2118 void
2119 gkrellm_set_chartconfig_flags(GkrellmChartconfig *cf, gint flags)
2120 	{
2121 	if (!cf)
2122 		return;
2123 	cf->flags = flags;
2124 	}
2125 
2126 void
2127 gkrellm_chartconfig_grid_resolution_adjustment(GkrellmChartconfig *cf,
2128 			gboolean map_sequence, gfloat spin_factor,
2129 			gfloat low, gfloat high, gfloat step0, gfloat step1, gint digits,
2130 			gint width)
2131 	{
2132 	if (!cf)
2133 		return;
2134 	cf->map_sequence = map_sequence;
2135 	if ((cf->width = width) == 0)
2136 		cf->width = 70 + log(high / 100000) * 5;
2137 	if (map_sequence)
2138 		{
2139 		cf->low = 1;
2140 		cf->low = (gfloat) gkrellm_125_sequence((gint) low, cf->sequence_125,
2141 							low, high, TRUE, FALSE);
2142 		cf->high = (gfloat) gkrellm_125_sequence((gint) high,
2143 							cf->sequence_125, low, high, TRUE, TRUE);
2144 		cf->step0 = 1.0;
2145 		cf->step1 = 1.0;
2146 		cf->digits = 0;
2147 		}
2148 	else
2149 		{
2150 		cf->low = low;
2151 		cf->high = high;
2152 		cf->step0 = step0;
2153 		cf->step1 = step1;
2154 		cf->digits = digits;
2155 		cf->spin_factor = spin_factor;
2156 		}
2157 	if (cf->spin_factor < 1.0)
2158 		cf->spin_factor = 1.0;
2159 	cf->adjustment_is_set = TRUE;
2160 	}
2161 
2162 void
2163 gkrellm_chartconfig_grid_resolution_label(GkrellmChartconfig *cf, gchar *label)
2164 	{
2165 	if (!cf)
2166 		return;
2167 	gkrellm_dup_string(&cf->grid_resolution_label, label);
2168 	}
2169 
2170 void
2171 gkrellm_set_chartconfig_auto_grid_resolution(GkrellmChartconfig *cf, gboolean ato)
2172 	{
2173 	if (cf)
2174 		cf->auto_grid_resolution = ato;
2175 	}
2176 
2177 void
2178 gkrellm_set_chartconfig_auto_resolution_stick(GkrellmChartconfig *cf, gboolean stick)
2179 	{
2180 	if (cf)
2181 		cf->auto_resolution_stick = stick;
2182 	}
2183 
2184 void
2185 gkrellm_set_chartconfig_sequence_125(GkrellmChartconfig *cf, gboolean seq)
2186 	{
2187 	if (cf)
2188 		cf->sequence_125 = seq;
2189 	}
2190 
2191 void
2192 gkrellm_set_chartconfig_fixed_grids(GkrellmChartconfig *cf, gint fixed_grids)
2193 	{
2194 	if (!cf || fixed_grids < 0 || fixed_grids > 5)
2195 		return;
2196 	cf->fixed_grids = fixed_grids;
2197 	}
2198 
2199 gint
2200 gkrellm_get_chartconfig_fixed_grids(GkrellmChartconfig *cf)
2201 	{
2202 	if (!cf)
2203 		return 0;
2204 	return cf->fixed_grids;
2205 	}
2206 
2207 void
2208 gkrellm_chartconfig_fixed_grids_connect(GkrellmChartconfig *cf,
2209 			void (*func)(gpointer), gpointer data)
2210 	{
2211 	if (!cf)
2212 		return;
2213 	cf->cb_fixed_grids = func;
2214 	cf->cb_fixed_grids_data = data;
2215 	}
2216 
2217 gint
2218 gkrellm_get_chartconfig_height(GkrellmChartconfig *cf)
2219 	{
2220 	if (!cf)
2221 		return 0;
2222 	return cf->h;
2223 	}
2224 
2225 void
2226 gkrellm_chartconfig_height_connect(GkrellmChartconfig *cf,
2227 			void (*func)(gpointer), gpointer data)
2228 	{
2229 	if (!cf)
2230 		return;
2231 	cf->cb_height = func;
2232 	cf->cb_height_data = data;
2233 	}
2234 
2235 void
2236 gkrellm_set_chart_height(GkrellmChart *cp, gint h)
2237 	{
2238 	GtkWidget			*top_win = gkrellm_get_top_window();
2239 	GtkSpinButton		*spin;
2240 	GList				*list;
2241 	GkrellmChartdata	*cd;
2242 	GkrellmChartconfig	*cf;
2243 	gint				dy;
2244 
2245 	if (!cp || cp->h == h)
2246 		return;
2247 	dy = h - cp->h;
2248 	cp->h = h;
2249 	cp->config->h = h;
2250 	render_default_data_pixmaps(cp);
2251 	for (list = cp->cd_list; list; list = list->next)
2252 		{
2253 		cd = (GkrellmChartdata *) list->data;
2254 		g_object_unref(G_OBJECT(cd->data_bitmap));
2255 		g_object_unref(G_OBJECT(cd->layer.pixmap));
2256 		cd->data_bitmap = gdk_pixmap_new(top_win->window, cp->w, cp->h, 1);
2257 		cd->layer.pixmap = gdk_pixmap_new(top_win->window, cp->w, cp->h, -1);
2258 		}
2259 	cp->bg_sequence_id += 1;
2260 	render_chart_pixmaps(cp);
2261 
2262 	cf = cp->config;
2263 	if (cf->cb_height && !cf->cb_block)
2264 		(*cf->cb_height)(cf, cf->cb_height_data);
2265 	gtk_widget_set_size_request(cp->drawing_area, cp->w, cp->h);
2266 	set_chartdata_split_heights(cp);
2267 	if (cp->shown)
2268 		{
2269 		gkrellm_monitor_height_adjust(dy);
2270 		gkrellm_pack_side_frames();
2271 		gkrellm_refresh_chart(cp);
2272 		}
2273 	if (cp->config_window)
2274 		{
2275 		spin = GTK_SPIN_BUTTON(cf->height_spin_button);
2276 		if (h != gtk_spin_button_get_value_as_int(spin))
2277 			gtk_spin_button_set_value(spin, (gfloat) h);
2278 		}
2279 	}
2280 
2281 gboolean
2282 gkrellm_get_chartdata_hide(GkrellmChartdata *cd)
2283 	{
2284 	if (cd && cd->hide)
2285 		return TRUE;
2286 	return FALSE;
2287 	}
2288 
2289 
2290 /* =================================================================== */
2291 
2292 static void
2293 chart_config_window_close(GtkWidget *widget, GkrellmChart *cp)
2294 	{
2295 	if (cp->config_window)
2296 		gtk_widget_destroy(cp->config_window);
2297 	cp->config_window = NULL;
2298 	}
2299 
2300 void
2301 gkrellm_chartconfig_window_destroy(GkrellmChart *cp)
2302 	{
2303 	chart_config_window_close(NULL, cp);
2304 	}
2305 
2306 static gint
2307 chart_config_window_delete_event(GtkWidget *widget, GdkEvent *ev,
2308 			gpointer data)
2309 	{
2310 	chart_config_window_close(widget, data);
2311 	return FALSE;
2312 	}
2313 
2314 static void
2315 set_resolution_menubar_items_sensitivity(GkrellmChartconfig *cf)
2316 	{
2317 	GtkWidget	*w;
2318 
2319 	if (!cf->auto_resolution_ui_manager)
2320 		return;
2321 
2322 	w = gtk_ui_manager_get_widget(cf->auto_resolution_ui_manager,
2323                                       "/menubar/Control/AutoModeStickPeak");
2324 	GTK_CHECK_MENU_ITEM(w)->active = cf->auto_resolution_stick;
2325 
2326 	w = gtk_ui_manager_get_widget(cf->auto_resolution_ui_manager,
2327                                       "/menubar/Control/AutoModeRecalibrate");
2328 	if (cf->auto_grid_resolution)
2329 		gtk_widget_set_sensitive(w, TRUE);
2330 	else
2331 		gtk_widget_set_sensitive(w, FALSE);
2332 	}
2333 
2334 static void
2335 cb_chart_height(GtkWidget *adjustment, GkrellmChart *cp)
2336 	{
2337 	GtkSpinButton	*spin;
2338 	gint			h;
2339 
2340 	_GK.config_modified = TRUE;
2341 	spin = GTK_SPIN_BUTTON(cp->config->height_spin_button);
2342 	h = gtk_spin_button_get_value_as_int(spin);
2343 	gkrellm_set_chart_height(cp, h);
2344 	}
2345 
2346 static void
2347 cb_chart_fixed_grids(GtkWidget *adjustment, GkrellmChart *cp)
2348 	{
2349 	GtkSpinButton		*spin;
2350 	GkrellmChartconfig	*cf = cp->config;
2351 
2352 	_GK.config_modified = TRUE;
2353 	spin = GTK_SPIN_BUTTON(cf->fixed_grids_spin_button);
2354 	cf->fixed_grids = gtk_spin_button_get_value_as_int(spin);
2355 	if (cf->auto_grid_resolution)
2356 		set_auto_grid_resolution(cp, cp->maxval_auto);
2357 	if (cf->cb_fixed_grids && !cf->cb_block)
2358 		(*cf->cb_fixed_grids)(cf, cf->cb_fixed_grids_data);
2359 
2360 	set_resolution_menubar_items_sensitivity(cf);
2361 
2362 	gkrellm_refresh_chart(cp);
2363 	}
2364 
2365 static void
2366 cb_line_draw_style(GtkWidget *widget, GkrellmChartdata *cd)
2367 	{
2368 	_GK.config_modified = TRUE;
2369 	cd->draw_style = GTK_TOGGLE_BUTTON(widget)->active;
2370 	gkrellm_rescale_chart(cd->chart);
2371 	}
2372 
2373 static void
2374 cb_auto_resolution(GtkWidget *widget, GkrellmChart *cp)
2375 	{
2376 	GtkWidget			*button;
2377 	GkrellmChartconfig	*cf = cp->config;
2378 
2379 	_GK.config_modified = TRUE;
2380 	cf->auto_grid_resolution = GTK_TOGGLE_BUTTON(widget)->active;
2381 
2382 	set_resolution_menubar_items_sensitivity(cf);
2383 	button = cf->grid_resolution_spin_button;
2384 	if (cf->auto_grid_resolution)
2385 		gtk_widget_set_sensitive(button, FALSE);
2386 	else
2387 		gtk_widget_set_sensitive(button, TRUE);
2388 	gkrellm_rescale_chart(cp);
2389 	}
2390 
2391 static void
2392 cb_inverted_draw_mode(GtkWidget *widget, GkrellmChartdata *cd)
2393 	{
2394 	_GK.config_modified = TRUE;
2395 	cd->inverted = GTK_TOGGLE_BUTTON(widget)->active;
2396 	gkrellm_rescale_chart(cd->chart);
2397 	}
2398 
2399 static void
2400 cb_hide(GtkWidget *widget, GkrellmChartdata *cd)
2401 	{
2402 	_GK.config_modified = TRUE;
2403 	cd->hide = GTK_TOGGLE_BUTTON(widget)->active;
2404 	set_chartdata_split_heights(cd->chart);
2405 	gkrellm_rescale_chart(cd->chart);
2406 	}
2407 
2408 static void
2409 cb_split_mode(GtkWidget *widget, GkrellmChartdata *cd)
2410 	{
2411 	_GK.config_modified = TRUE;
2412 	cd->split_chart = GTK_TOGGLE_BUTTON(widget)->active;
2413 	gtk_widget_set_sensitive(cd->split_fraction_spin_button, cd->split_chart);
2414 	set_chartdata_split_heights(cd->chart);
2415 	gkrellm_rescale_chart(cd->chart);
2416 	}
2417 
2418 static void
2419 cb_split_fraction(GtkWidget *adjustment, GkrellmChartdata *cd)
2420 	{
2421 	GtkSpinButton	*spin;
2422 
2423 	_GK.config_modified = TRUE;
2424 	spin = GTK_SPIN_BUTTON(cd->split_fraction_spin_button);
2425 	cd->split_fraction = gtk_spin_button_get_value(spin);
2426 	set_chartdata_split_heights(cd->chart);
2427 	gkrellm_rescale_chart(cd->chart);
2428 	}
2429 
2430 
2431 /* =================================================================== */
2432 
2433 static void
2434 cb_seq_control(GtkRadioAction *action, GtkRadioAction *current, GkrellmChart *cp )
2435 {
2436     GkrellmChartconfig *cf = cp->config;
2437 
2438     if (cf->sequence_125 == gtk_radio_action_get_current_value(action))
2439         return;
2440     cf->sequence_125 = gtk_radio_action_get_current_value(action);
2441 
2442     cf->grid_resolution = gkrellm_125_sequence(cf->grid_resolution,
2443                                                cf->sequence_125, cf->low, cf->high, TRUE, FALSE);
2444     set_grid_resolution_spin_button(cp, cf->grid_resolution);
2445 }
2446 
2447 static void
2448 cb_auto_stick_control(GtkToggleAction *action, GkrellmChart *cp )
2449 {
2450     GkrellmChartconfig *cf = cp->config;
2451 
2452     cf->auto_resolution_stick = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
2453     cp->maxval_auto_base = 0;
2454     gkrellm_refresh_chart(cp);
2455 }
2456 
2457 static void
2458 cb_auto_res_control(GtkAction *action, GkrellmChart *cp )
2459 {
2460     cp->maxval_auto_base = 0;
2461     cp->maxval_peak = 0;
2462     gkrellm_refresh_chart(cp);
2463 }
2464 
2465 static const char *auto_res_control_items = "\
2466 <ui>\
2467   <menubar>\
2468   <menu name=\"Control\" action=\"ControlAction\">\
2469     <separator/>\
2470     <menuitem name=\"AutoModeRecalibrate\" action=\"AutoModeRecalibrateAction\"/>\
2471     <menuitem name=\"AutoModeStickPeak\" action=\"AutoModeStickPeakAction\"/>\
2472     <menu name=\"SequenceMenu\" action=\"SequenceMenuAction\">\
2473       <menuitem name=\"Seq125\" action=\"Seq125Action\"/>\
2474       <menuitem name=\"Seq1357\" action=\"Seq1357Action\"/>\
2475     </menu>\
2476     <separator/>\
2477   </menu>\
2478   </menubar>\
2479 </ui>\
2480 ";
2481 
2482 static GtkActionEntry auto_res_control_entries[] =
2483 {
2484     { "ControlAction", NULL, N_("Control"),
2485       NULL, NULL, G_CALLBACK(NULL) },
2486     { "SequenceMenuAction", NULL, N_("Sequence..."),
2487       NULL, NULL, G_CALLBACK(NULL) },
2488     { "AutoModeRecalibrateAction", NULL, N_("Auto mode recalibrate"),
2489       NULL, NULL, G_CALLBACK(cb_auto_res_control) },
2490 };
2491 static guint n_auto_res_control_entries = G_N_ELEMENTS (auto_res_control_entries);
2492 
2493 static GtkToggleActionEntry auto_res_control_toggle_entries[] =
2494 {
2495     { "AutoModeStickPeakAction", NULL, N_("Auto mode sticks at peak value"),
2496       NULL, NULL, G_CALLBACK(cb_auto_stick_control), FALSE },
2497 };
2498 static guint n_auto_res_control_toggle_entries = G_N_ELEMENTS (auto_res_control_toggle_entries);
2499 
2500 static GtkRadioActionEntry auto_res_control_radio_entries[] =
2501 {
2502     { "Seq125Action", NULL, N_("1 2 5"),
2503       NULL, NULL, 1 },
2504     { "Seq1357Action", NULL, N_("1 1.5 2 3 5 7"),
2505       NULL, NULL, 0 },
2506 };
2507 static guint n_auto_res_control_radio_entries = G_N_ELEMENTS (auto_res_control_radio_entries);
2508 
2509 static void
2510 auto_resolution_control_menubar(GtkWidget **menubar, GkrellmChart *cp)
2511 	{
2512         GtkUIManager *ui_manager;
2513         GtkActionGroup *action_group;
2514 	GkrellmChartconfig		*cf = cp->config;
2515         GError *error;
2516 
2517         action_group = gtk_action_group_new ("ControlActions");
2518         gtk_action_group_add_actions (action_group, auto_res_control_entries,
2519                                       n_auto_res_control_entries, cp);
2520         gtk_action_group_add_toggle_actions (action_group, auto_res_control_toggle_entries,
2521                                              n_auto_res_control_toggle_entries, cp);
2522         gtk_action_group_add_radio_actions (action_group, auto_res_control_radio_entries,
2523                                             n_auto_res_control_radio_entries,
2524                                             !!cf->sequence_125, G_CALLBACK(cb_seq_control),
2525                                             cp);
2526         ui_manager = gtk_ui_manager_new ();
2527         error = NULL;
2528         gtk_ui_manager_add_ui_from_string (ui_manager, auto_res_control_items,
2529                                            strlen(auto_res_control_items), &error);
2530         if (error)
2531         {
2532             g_message ("building menus failed: %s", error->message);
2533             g_error_free (error);
2534             return;
2535         }
2536         gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
2537 	cf->auto_resolution_ui_manager = ui_manager;
2538 	set_resolution_menubar_items_sensitivity(cf);
2539 
2540 	if (menubar)
2541 		*menubar = gtk_ui_manager_get_widget(ui_manager, "/menubar");
2542 	}
2543 
2544 void
2545 gkrellm_chartconfig_window_create(GkrellmChart *cp)
2546 	{
2547 	GtkWidget			*main_vbox, *vbox, *vbox1, *vbox2, *hbox;
2548 	GtkWidget			*button;
2549 	GList				*list;
2550 	GkrellmChartconfig	*cf;
2551 	GkrellmChartdata	*cd;
2552 	GkrellmPanel		*p;
2553 	gchar				*s;
2554 
2555 	if (!cp || _GK.no_config)
2556 		return;
2557 	if (cp->config_window)
2558 		{
2559 		gtk_window_present(GTK_WINDOW(cp->config_window));
2560 		return;
2561 		}
2562 	cp->config_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2563 	g_signal_connect(G_OBJECT(cp->config_window), "delete_event",
2564 			G_CALLBACK(chart_config_window_delete_event), cp);
2565 
2566 	p = cp->panel;
2567 	cf = cp->config;
2568 	if (p && p->label)
2569 		s = p->label->string;
2570 	else
2571 		s = NULL;
2572 	gtk_window_set_title(GTK_WINDOW(cp->config_window),
2573 				_("GKrellM Chart Config"));
2574 	gtk_window_set_wmclass(GTK_WINDOW(cp->config_window),
2575 				"Gkrellm_conf", "Gkrellm");
2576 
2577 	main_vbox = gtk_vbox_new(FALSE, 0);
2578 	gtk_container_set_border_width(GTK_CONTAINER(cp->config_window), 4);
2579 	gtk_container_add(GTK_CONTAINER(cp->config_window), main_vbox);
2580 	vbox = gkrellm_gtk_framed_vbox(main_vbox, s, 4, FALSE, 4, 3);
2581 
2582 	hbox = gtk_hbox_new(TRUE, 0);
2583 	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
2584 	for (list = cp->cd_list; list; list = list->next)
2585 		{
2586 		cd = (GkrellmChartdata *) list->data;
2587 		if ((cd->flags & CHARTDATA_NO_CONFIG) == CHARTDATA_NO_CONFIG)
2588 			continue;
2589 		vbox1 = gkrellm_gtk_framed_vbox(hbox, cd->label, 2, TRUE, 2, 2);
2590 
2591 		if (!(cd->flags & CHARTDATA_NO_CONFIG_DRAW_STYLE))
2592 			{
2593 			gkrellm_gtk_check_button(vbox1, &button, cd->draw_style, FALSE, 0,
2594 					_("Line style"));
2595 			g_signal_connect(G_OBJECT(button), "toggled",
2596 					G_CALLBACK(cb_line_draw_style), cd);
2597 			}
2598 		if (!(cd->flags & CHARTDATA_NO_CONFIG_INVERTED))
2599 			{
2600 			gkrellm_gtk_check_button(vbox1, &button, cd->inverted, FALSE, 0,
2601 					_("Inverted"));
2602 			g_signal_connect(G_OBJECT(button), "toggled",
2603 					G_CALLBACK(cb_inverted_draw_mode), cd);
2604 			}
2605 		if (list != cp->cd_list && !(cd->flags & CHARTDATA_NO_CONFIG_SPLIT))
2606 			{
2607 			vbox2 = gkrellm_gtk_framed_vbox(vbox1, NULL, 2, FALSE, 2, 2);
2608 			gkrellm_gtk_check_button(vbox2, &button, cd->split_chart, FALSE, 0,
2609 					_("Split view"));
2610 			g_signal_connect(G_OBJECT(button), "toggled",
2611 					G_CALLBACK(cb_split_mode), cd);
2612 			gkrellm_gtk_spin_button(vbox2, &button, cd->split_fraction,
2613 					0.05, 0.95, 0.01, 0.05, 2, 55,
2614 					cb_split_fraction, cd, FALSE, "");
2615 			gtk_widget_set_sensitive(button, cd->split_chart);
2616 			cd->split_fraction_spin_button = button;
2617 			}
2618 		if (cd->flags & CHARTDATA_ALLOW_HIDE)
2619 			{
2620 			gkrellm_gtk_check_button(vbox1, &button, cd->hide, FALSE, 0,
2621 					_("Hide"));
2622 			g_signal_connect(G_OBJECT(button), "toggled",
2623 					G_CALLBACK(cb_hide), cd);
2624 			}
2625 		}
2626 
2627 	cf->auto_resolution_control_menubar = NULL;
2628 	cf->auto_resolution_ui_manager = NULL;
2629 	cf->grid_resolution_spin_button = NULL;
2630 	cf->fixed_grids_spin_button = NULL;
2631 
2632 	if (cf->adjustment_is_set)
2633 		{
2634 		gfloat	value;
2635 
2636 		vbox1 = gkrellm_gtk_framed_vbox(vbox, _("Resolution per Grid"),
2637 					2, FALSE, 2, 2);
2638 		if (cf->map_sequence)
2639 			value = (gfloat) cf->grid_resolution;
2640 		else
2641 			value = cf->grid_resolution / cf->spin_factor;
2642 		gkrellm_gtk_spin_button(vbox1, &button, value,
2643 			cf->low, cf->high, cf->step0, cf->step1, cf->digits, cf->width,
2644 			cb_chart_grid_resolution, cp, FALSE, cf->grid_resolution_label);
2645 		cf->grid_resolution_spin_button = button;
2646 		if (cp->config->auto_grid_resolution)
2647 			gtk_widget_set_sensitive(button, FALSE);
2648 
2649 		if (!(cp->config->flags & NO_CONFIG_AUTO_GRID_RESOLUTION))
2650 			{
2651 			hbox = gtk_hbox_new (FALSE, 0);
2652 			gtk_container_set_border_width(GTK_CONTAINER(hbox), 2);
2653 			gtk_container_add(GTK_CONTAINER(vbox1), hbox);
2654 			gkrellm_gtk_check_button(hbox, &button,
2655 					cp->config->auto_grid_resolution, TRUE, 0,
2656 					_("Auto"));
2657 			g_signal_connect(G_OBJECT(button), "toggled",
2658 					G_CALLBACK(cb_auto_resolution), cp);
2659 
2660 			auto_resolution_control_menubar(
2661 					&cf->auto_resolution_control_menubar, cp);
2662 			gtk_box_pack_start(GTK_BOX(hbox),
2663 				cf->auto_resolution_control_menubar, FALSE, TRUE, 10);
2664 			}
2665 		}
2666 	if (!(cp->config->flags & NO_CONFIG_FIXED_GRIDS))
2667 		{
2668 		vbox1 = gkrellm_gtk_framed_vbox(vbox, _("Number of Grids"), 2, FALSE,
2669 				2, 2);
2670 		gkrellm_gtk_spin_button(vbox1, &button, (gfloat) cf->fixed_grids,
2671 				0, 5, 1.0, 1.0, 0, 50,
2672 				cb_chart_fixed_grids, cp, FALSE,
2673 				_("0: Auto    1-5: Constant"));
2674 		cf->fixed_grids_spin_button = button;
2675 		}
2676 
2677 	vbox1 = gkrellm_gtk_framed_vbox(vbox, NULL, 2, FALSE, 2, 2);
2678 	gkrellm_gtk_spin_button(vbox1, &button, (gfloat) cp->h,
2679 			(gfloat) _GK.chart_height_min, (gfloat) _GK.chart_height_max,
2680 			5.0, 10.0, 0, 50,
2681 			cb_chart_height, cp, FALSE,
2682 			_("Chart height"));
2683 	cf->height_spin_button = button;
2684 
2685 	hbox = gtk_hbutton_box_new();
2686 	gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
2687 	gtk_box_set_spacing(GTK_BOX(hbox), 5);
2688 	gtk_box_pack_start(GTK_BOX(main_vbox), hbox, FALSE, FALSE, 0);
2689 
2690 	button = gtk_button_new_from_stock(GTK_STOCK_OK);
2691 	GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
2692 	g_signal_connect(G_OBJECT(button), "clicked",
2693 			G_CALLBACK(chart_config_window_close), cp);
2694     gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 15);
2695 	gtk_widget_grab_default(button);
2696 
2697 	gtk_widget_show_all(cp->config_window);
2698 	}
2699 
2700 void
2701 gkrellm_save_chartconfig(FILE *f, GkrellmChartconfig *cf, gchar *mon_keyword,
2702 			gchar *name)
2703 	{
2704 	GList			*list;
2705 	GkrellmChartdata *cd;
2706 
2707 	if (!f || !cf || !mon_keyword)
2708 		return;
2709 	if (name)
2710 		fprintf(f, "%s %s %s ", mon_keyword, GKRELLM_CHARTCONFIG_KEYWORD,name);
2711 	else
2712 		fprintf(f, "%s %s ", mon_keyword, GKRELLM_CHARTCONFIG_KEYWORD);
2713 	fprintf(f, "%d %d %d %d %d %d", cf->h, cf->grid_resolution,
2714 				cf->fixed_grids, cf->auto_grid_resolution,
2715 				cf->auto_resolution_stick, cf->sequence_125);
2716 	for (list = cf->cd_list; list; list = list->next)
2717 		{
2718 		cd = (GkrellmChartdata *) list->data;
2719 		fprintf(f, " : %d %d %d %d %.0f",
2720 				cd->draw_style, cd->inverted, cd->hide,
2721 				cd->split_chart, cd->split_fraction * GKRELLM_FLOAT_FACTOR);
2722 		}
2723 	fprintf(f, "\n");
2724 	}
2725 
2726 void
2727 gkrellm_load_chartconfig(GkrellmChartconfig **config, gchar *string,
2728 			gint max_cd)
2729 	{
2730 	GList				*list;
2731 	GkrellmChartdata	*cd;
2732 	GkrellmChartconfig	*cf;
2733 	gchar				*s;
2734 	gint				index = 0;
2735 
2736 	if (!config || !string)
2737 		return;
2738 	if (!*config)
2739 		{
2740 		*config = gkrellm_chartconfig_new0();
2741 		(*config)->auto_grid_resolution = TRUE;		/* the default */
2742 		}
2743 	cf = *config;
2744 	sscanf(string, "%d %d %d %d %d %d", &cf->h, &cf->grid_resolution,
2745 				&cf->fixed_grids, &cf->auto_grid_resolution,
2746 				&cf->auto_resolution_stick, &cf->sequence_125);
2747 	for (s = strchr(string, (int) ':'); s ; s = strchr(s, (int) ':'))
2748 		{
2749 		++s;
2750 		list = g_list_nth(cf->cd_list, index++);
2751 		if (!list)
2752 			{
2753 			cd = g_new0(GkrellmChartdata, 1);
2754 			cd->split_fraction = 0.5;
2755 			cf->cd_list = g_list_append(cf->cd_list, cd);
2756 			}
2757 		else
2758 			cd = (GkrellmChartdata *) list->data;
2759 		sscanf(s, "%d %d %d %d %f",
2760 				&cd->draw_style, &cd->inverted, &cd->hide,
2761 				&cd->split_chart, &cd->split_fraction);
2762 		cd->split_fraction /= _GK.float_factor;
2763 		if (cd->split_fraction <= 0.01 || cd->split_fraction >= 0.99)
2764 			cd->split_fraction = 0.5;
2765 
2766 		cf->config_loaded = TRUE;
2767 		if (max_cd && index >= max_cd)
2768 			break;
2769 		}
2770 	}
2771 
2772 void
2773 debug_dump_chart_list()
2774 	{
2775 	GList			*list, *cdlist;
2776 	GkrellmChart	*cp;
2777 	GkrellmPanel	*p;
2778 	GkrellmChartdata *cd;
2779 
2780 	g_debug("\n");
2781 	for (list = gkrellm_get_chart_list(); list; list = list->next)
2782 		{
2783 		cp = (GkrellmChart *) list->data;
2784 		p = cp->panel;
2785 		if (p && p->label && p->label->string)
2786 			g_debug("%s [%d]: ", p->label->string, cp->style_id);
2787 		else
2788 			g_debug("(null) [%d]: ", cp->style_id);
2789 		for (cdlist = cp->cd_list; cdlist; cdlist = cdlist->next)
2790 			{
2791 			cd = (GkrellmChartdata *) cdlist->data;
2792 			g_debug("%s %p->data ", cd->label, cd->data);
2793 			}
2794 		g_debug("\n");
2795 		}
2796 	}
2797