changes due to new visiLib
[dyninst.git] / visiClients / barchart / src / barChart.C
1 // This is the C++ portion of barchart version 2.
2 // Much of the user interface mundane stuff (menus, etc.) is still
3 // programmed in tk/tcl in barChart.tcl.
4 // There is fairly extensive assertion checking, even in critical
5 // path routines.  May want to #define NDEBUG to turn off assertions
6 // for maximum speed.  Can be done from makefile, e.g. a "make optimized"
7 // option which does -O and -DNDEBUG
8
9 /* $Log: barChart.C,v $
10 /* Revision 1.20  1996/01/17 18:31:10  newhall
11 /* changes due to new visiLib
12 /*
13  * Revision 1.19  1996/01/10 21:10:11  tamches
14  * metricMaxValues now indexed by metric units
15  *
16  * Revision 1.18  1996/01/10 02:36:11  tamches
17  * changed uses of dynamic1dArray/2d to the vector class
18  * removed theWindowName
19  * added a tkInstallIdle
20  * int --> unsigned for many index vrbles
21  * constructor now takes an array of color names
22  * added getMetricColorName; removed gimmeColorName, RethinkMetricColors
23  *
24  * Revision 1.17  1995/09/22 19:24:21  tamches
25  * removed warnings under g++ 2.7.0
26  *
27  * Revision 1.16  1995/08/06  22:09:47  tamches
28  * tk.h, tcl.h --> tkclean.h, tclclean.h
29  *
30  * Revision 1.15  1995/07/06  18:54:32  tamches
31  * Update for tk4.0
32  *
33  * Revision 1.14  1995/05/10  22:27:30  tamches
34  * Added an extra (probably unnecessary) isnan() check for HP.
35  *
36  * Revision 1.13  1995/04/01  01:35:13  tamches
37  * Implemented NaN checking (needed on HP) of incoming dataGrid values
38  *
39  * Revision 1.12  1994/11/11  06:41:30  tamches
40  * Fixed bug that required all metrics to be valid or else would
41  * crash with assertion error.  Just because we haven't implemented
42  * deleting does not mean that metrics cannot become invalid; they
43  * can become invalid when no more met/res pairs for the metric
44  * are Enabled() in datagrid...
45  *
46  * Revision 1.11  1994/11/06  10:31:40  tamches
47  * greatly improved commenting
48  * Changed bar height algorithm to pin at a minimum individual bar
49  * height.
50  * Much better implementation of numValidResources, validResources[],
51  * and indirectResources[], especially w.r.t assertion checking.
52  * Many loops now use numValidResources instead of numResources
53  * as their upper bounds, as they always should have (e.g. in
54  * calculating the total needed height)
55  *
56  * Revision 1.10  1994/10/14  10:27:40  tamches
57  * Swapped the x and y axes -- now resources print vertically and
58  * metrics print horizontally.  Can fit many, many more resources
59  * on screen at once with no label overlap.  Multiple metrics
60  * are now shown in the metrics axis.  Metric names are shown in
61  * a "key" in the lower-left.
62  *
63  * Revision 1.9  1994/10/13  00:49:57  tamches
64  * Implemented sorting of resources.
65  * Fixed deleting of resources.
66  * Rearranged menus to be more standards-ish
67  *
68  */
69
70 // tk/tcl has a very nice interface for mixing C++ and tk/tcl
71 // scripts.  From within tcl, C++ code can be called via
72 // new commands; new commands are created with a call
73 // to Tcl_CreateCommand(...)  Unfortunately, you can see
74 // a catch-22 here; in order to even call Tcl_CreateCommand(),
75 // we need to be out of tcl and in C++!  The only way around
76 // this problem is to start the process in C++ and have it
77 // call tk/tcl.  This is done with Tcl_EvalFile(...).  Of
78 // course, just before this, we sneak in our Tcl_CreateCommand()
79 // call.
80
81 // Somewhere in the tk library, there is a main() that
82 // basically does this for us; you have to provide a -f tcl-file-name
83 // option so it knows what to send to Tcl_EvalFile(...)
84 // This main() assumes a function Tcl_InitApp(), which will
85 // be called before the Tcl_EvalFile() is performed...that's
86 // the time to sneak in our Tcl_CreateCommand() stuff...
87
88 #include <assert.h>
89 #include <stdlib.h> // exit()
90 #include <iostream.h>
91 #include <math.h>
92
93 #include "tcl.h"
94 #include "tk.h"
95
96 #include "visi/h/visualization.h"
97 #include "dg2.h"
98 #include "barChartTcl.h"
99
100 #include "barChart.h"
101
102  // main data structure; holds bar information.  Does not
103  // hold resources axis or metrics axis information (or contents), because
104  // they are managed just fine from tcl (barChart.tcl) at present.
105  // Created dynamically in DrawBarsInstallCommand.  Cannot be
106  // created before then since necessary constructor arguments
107  // such as tk window name are not yet known.
108
109 BarChart *theBarChart;
110
111 /* ********************************************************************
112  * *********************** BarChart methods ***************************
113  * ********************************************************************
114 */
115
116 BarChart::BarChart(char *tkWindowName,
117                    unsigned initNumMetrics, unsigned initNumResources,
118                    const vector<string> &barColorNames) :
119             drawWhenIdle(&lowestLevelDrawBars),
120             metricColorNames(barColorNames),
121             metricColors(metricColorNames.size()),
122             indirectResources(initNumResources),
123             validMetrics(initNumMetrics),
124             validResources(initNumResources),
125             values  (initNumMetrics, initNumResources),
126             barPixWidths (initNumMetrics, initNumResources),
127             metricCurrMaxVals(initNumMetrics)
128              {
129
130    theWindow = Tk_NameToWindow(MainInterp, tkWindowName, Tk_MainWindow(MainInterp));
131    if (theWindow == NULL)
132       panic("BarChart constructor: Tk_NameToWindow() failed!");
133
134    HaveSeenFirstGoodWid = false;
135    borderPix = 2;
136    currScrollOffset = 0;
137    width    = Tk_Width(theWindow);
138    height   = Tk_Height(theWindow);
139    display  = Tk_Display(theWindow);
140    width  -= borderPix*2;
141    height -= borderPix*2;
142    
143    greyColor = Tk_GetColor(MainInterp, theWindow, Tk_GetUid("grey"));
144    for (unsigned color=0; color < barColorNames.size(); color++) {
145       const string &colorName = barColorNames[color];
146
147       XColor *theColor = Tk_GetColor(MainInterp, theWindow,
148                                      Tk_GetUid(colorName.string_of()));
149       assert(theColor);
150
151       metricColors[color] = theColor;
152    }
153
154    DataFormat = Current;
155    
156    RethinkMetricsAndResources();
157    // Rethinks numMetrics, numResources, numValidMetrics, numValidResources,
158    //         validMetrics[], validResources[], values[][] from DataGrid.
159    // Rethinks indirectResources[] from tcl.
160    // Rethinks metric max values, colors, bar positioning
161    // Clears screen; redraws.
162 }
163
164 BarChart::~BarChart() {
165    Tk_CancelIdleCall(lowestLevelDrawBars, NULL);
166
167    XFreeGC(display, myGC);   
168
169    Tk_FreeColor(greyColor);
170    for (unsigned metriclcv=0; metriclcv<metricColors.size(); metriclcv++)
171       Tk_FreeColor(metricColors[metriclcv]);
172
173    XFreePixmap(display, doubleBufferPixmap);
174 }
175
176 void BarChart::changeDoubleBuffering() {
177    // note that we don't try to optimize and return immediately if
178    // the mode hasn't changed.  Why? to accomodate possible change in size.
179
180    if (!HaveSeenFirstGoodWid)
181       return;
182
183    // erase offscreen pixmap and reallocate with new size
184    XFreePixmap(display, doubleBufferPixmap);
185
186    doubleBufferPixmap = XCreatePixmap(display, Tk_WindowId(theWindow),
187                                       Tk_Width(theWindow),
188                                       Tk_Height(theWindow),
189                                       Tk_Depth(theWindow));
190          // note that we use Tk_Width and Tk_Height instead of width and height.
191          // these values differ by (borderPix) in each dimension.
192 }
193
194 bool BarChart::TryFirstGoodWid() {
195    // returns true if the wid is valid
196
197    if (HaveSeenFirstGoodWid)
198       return true;
199
200    if (Tk_WindowId(theWindow) == 0)
201       return false; // sigh; still invalid
202
203    HaveSeenFirstGoodWid = true;
204
205    // initialize gc
206    XGCValues values;
207    values.foreground = greyColor->pixel;
208    values.background = greyColor->pixel;
209    // values.graphics_exposures = False;
210
211    myGC = XCreateGC(display, Tk_WindowId(theWindow),
212                     GCForeground | GCBackground,
213                     &values);
214    if (NULL==myGC)
215       panic("BarChart constructor: XCreateGC() failed!");
216
217    // initialize offscreen pixmap
218    doubleBufferPixmap = XCreatePixmap(display, Tk_WindowId(theWindow),
219                                       1, 1, 1); // temporary
220    changeDoubleBuffering();
221                                          
222    // simulate a resize
223    processResizeWindow(Tk_Width(theWindow), Tk_Height(theWindow));
224
225    return true;   
226 }
227
228 void BarChart::processResizeWindow(const int newWidth, const int newHeight) {
229    if (!TryFirstGoodWid())
230       return;
231
232    width  = newWidth - 2*borderPix; // subtract left+right border
233    height = newHeight - 2*borderPix; // subract top+bottom border
234
235    // update off-screen pixmap's dimensions to accomodate the resize
236    changeDoubleBuffering();
237
238    // rethink resource height and rethink widths of each bar
239    RethinkBarLayouts();
240 }
241
242 void BarChart::processExposeWindow() {
243    if (!TryFirstGoodWid())
244       return;
245
246    drawWhenIdle.install(this);
247 }
248
249 void BarChart::RethinkMetricsAndResources() {
250    // Rethink numMetrics, numResources, numValidMetrics, numValidResources,
251    //         validMetrics[], validResources[], values[][] from DataGrid.
252    // Rethink indirectResources[] from tcl.
253    // rethink metric max values, colors, bar positioning
254    // Clears screen; redraws.
255
256    numMetrics   = visi_NumMetrics();
257    numResources = visi_NumResources();
258
259    // reallocate and rethink validMetrics, validResources
260    rethinkValidMetricsAndResources(); 
261
262    // reallocate values[][], then copy values in from the dataGrid
263    rethinkValues();
264
265    myTclEval(MainInterp, "rethinkIndirectResources false");
266
267    rethinkIndirectResources();
268
269    // rethink metric max values from tcl
270    rethinkMetricMaxValues();
271    RethinkBarLayouts(); // as if there were a resize (no reallocations, but
272                         // resets/rethinks barXoffsets[], barPixWidths[])
273
274    if (HaveSeenFirstGoodWid)
275       drawWhenIdle.install(this);
276 }
277
278 void BarChart::rethinkValues() {
279    // Given: updated numMetrics, numResources, numValidMetrics,
280    //        numValidResources, validMetrics[], validResources[]
281    // Does:  reallocates and rethinks values[][] from dataGrid
282    // Does not: do anything to the screen
283
284    values.resize(numMetrics);
285    for (unsigned met=0; met < numMetrics; met++)
286       values[met].resize(numResources);
287
288    for (unsigned metriclcv=0; metriclcv<numMetrics; metriclcv++) {
289       if (!validMetrics[metriclcv]) continue;
290
291       for (unsigned resourcelcv=0; resourcelcv<numResources; resourcelcv++) {
292          if (!validResources[resourcelcv]) continue;
293          int buck_num = visi_LastBucketFilled(metriclcv,resourcelcv);
294          const visi_sampleType theValue = visi_DataValue(metriclcv,
295                                                     resourcelcv,buck_num);
296             // warning: "theValue" may be a NaN
297          values[metriclcv][resourcelcv] = isnan(theValue) ? 0 : theValue;
298       }
299    }
300 }
301
302 void BarChart::rethinkMetricMaxValues() {
303    // Given: updated numMetrics, validMetrics[]
304    // Does:  reallocates and rethinks metricCurrMaxVals[] from tcl
305    // Does not: draw anything
306
307    metricCurrMaxVals.resize(numMetrics);
308    
309    for (unsigned metriclcv=0; metriclcv<numMetrics; metriclcv++) {
310       if (!validMetrics[metriclcv]) continue;
311
312       char buffer[64];
313       sprintf(buffer, "%d", metriclcv);
314       char *str = Tcl_GetVar2(MainInterp, "metricMaxValues",
315                               visi_MetricLabel(metriclcv), // units
316                               TCL_GLOBAL_ONLY);
317       if (str == NULL)
318          panic("BarChart::RethinkMetricMaxValues() -- could not read 'metricMaxValues'");
319
320       metricCurrMaxVals[metriclcv] = atof(str);
321    }
322 }
323
324 void BarChart::processNewData(const int newBucketIndex) {
325    // Given: new datagrid bucket data for all enabled metric/rsrc pairs.
326    //        up-to-date numMetrics, numResources,
327    //                   numValidMetrics, numValidResources,
328    //                   validMetrics[], validResources[]
329    // Does:  rethinks values[][] and (if necessary) metricCurrMaxVals[].
330    //        Then calls rethinkBarPixWidths() which rethinks barPixWidths[][]
331    //        and redraws.
332    // Does not: care about sorting order in any way (a future, optimized
333    //           version will)
334
335    // Called on the critical path -- we must be quick!
336    
337    for (unsigned metriclcv=0; metriclcv<numMetrics; metriclcv++) {
338       if (!validMetrics[metriclcv]) continue;
339       for (unsigned resourcelcv=0; resourcelcv<numResources; resourcelcv++) {
340          if (!validResources[resourcelcv]) continue;
341
342          if (visi_Valid(metriclcv,resourcelcv)) {
343             // note that we check the .Valid flag, not the .enabled flag
344
345             register double newVal;
346             switch (DataFormat) {
347                case Current:
348                   newVal = visi_DataValue(metriclcv,resourcelcv,newBucketIndex);
349                   break;
350                case Average:
351                   newVal = visi_AverageValue(metriclcv, resourcelcv);
352                   break;
353                case Total:
354                   newVal = visi_SumValue(metriclcv, resourcelcv);
355                   break;
356                default:
357                   panic("BarChart::processNewData() -- unknown data format!");
358             }
359
360             values[metriclcv][resourcelcv] = isnan(newVal) ? 0 : newVal;
361
362             // the dreaded check for y-axis overflow (slows things down
363             // greatly if there is indeed overflow)
364             if (newVal > metricCurrMaxVals[metriclcv])
365                setMetricNewMax(metriclcv, newVal); // nuts!
366          }
367          else
368             values[metriclcv][resourcelcv]=0; // hmmm...
369       }
370    }
371
372    rethinkBarPixWidths();
373 }
374
375 double BarChart::nicelyRoundedMetricNewMaxValue(unsigned metricindex,
376                                                 double newmaxval) {
377    assert(metricindex<numMetrics);
378
379    // far too arbitrary:
380    double hintedStep;
381    hintedStep = (double)((unsigned)(newmaxval / 10));
382    if (hintedStep == 0)
383       hintedStep = 0.1;
384
385    double result = newmaxval + (hintedStep - fmod(newmaxval, hintedStep));
386       // if fmod() doesn't return a number greater than 0, this won't work quite right
387
388    return result;
389 }
390
391 void BarChart::setMetricNewMax(unsigned metricindex, double newmaxval) {
392    // given an actual bar value (newmaxval) that overflows the current
393    // y-axis maximum value for the given metric, rethink what the
394    // y-axis maximum value for the given metric should be.
395    // Note that it may not be exactly "newmaxval"; we usually want
396    // to round to a relatively nice number.
397
398    assert(metricindex<numMetrics);
399
400    newmaxval = nicelyRoundedMetricNewMaxValue(metricindex, newmaxval);
401    metricCurrMaxVals[metricindex] = newmaxval;
402
403    // Inform our tcl code of the change, so it may update stuff:
404    string commandStr = string("processNewMetricMax ");
405    commandStr += string(metricindex);
406    commandStr += " ";
407    commandStr += string(newmaxval);
408    myTclEval(MainInterp, commandStr);
409 }
410
411 void BarChart::RethinkBarLayouts() {
412    // note -- validResources[] **must** be set before calling this routine
413
414    // Assuming a resize, change in sorting order, or added/deleted metric
415    // (but not new data callbacks), reallocate and fill in bar heights and
416    // calculate individualResourceHeight, etc.
417
418    // does not touch metricCurrMaxVals or values[][]
419
420    // Start off by reading some tcl vrbles (barChart.tcl)
421    char *totalResourceHeightStr =
422         Tcl_GetVar(MainInterp, "currResourceHeight", TCL_GLOBAL_ONLY);
423    if (NULL == totalResourceHeightStr)
424       panic("BarChart::RethinkBarLayouts(): couldn't read tcl 'currResourceHeight'");
425    totalResourceHeight  = atoi(totalResourceHeightStr);
426
427    char *maxIndividualColorHeightStr =
428         Tcl_GetVar(MainInterp, "maxIndividualColorHeight", TCL_GLOBAL_ONLY);
429    if (NULL == maxIndividualColorHeightStr)
430       panic("BarChart::RethinkBarLayouts(): couldn't read tcl 'maxIndividualColorHeight'");
431    const int maxIndividualColorHeight = atoi(maxIndividualColorHeightStr);
432
433 // minIndividualColorHeight [not yet implemented]
434 //   char *minIndividualColorHeightStr =
435 //        Tcl_GetVar(MainInterp, "minIndividualColorHeight", TCL_GLOBAL_ONLY);
436 //   if (NULL == minIndividualColorHeightStr)
437 //      panic("BarChart::RethinkBarLayouts(): couldn't read tcl 'minIndividualColorHeight'");
438 //   const int minIndividualColorHeight = atoi(minIndividualColorHeightStr);
439
440    // Here we go:   
441
442    // First of all, we want to use only 90% of the total resource height,
443    // to avoid bars of 2 resources touching each other. (The extra space will
444    // be padded evenly on both sides of the bars of this resource)
445    int fullResourceHeight   = (totalResourceHeight * 90) / 100;
446
447    // calculate height of each bar... [individualResourceHeight should be named
448    //                                  individualColorHeight]
449    individualResourceHeight = (numValidMetrics == 0) ? 0 :
450                               fullResourceHeight / numValidMetrics;
451    // ... but there is a maximum value (e.g. if we have just 1 metric)
452    if (individualResourceHeight > maxIndividualColorHeight) {
453 //      cout << "Pinning individual color height from " << individualResourceHeight
454 //           << " to " << maxIndividualColorHeight << endl;
455       individualResourceHeight = maxIndividualColorHeight;
456    }   
457
458    // rethink fullResourceHeight now...
459    fullResourceHeight = individualResourceHeight * numValidMetrics;
460    assert(fullResourceHeight * 100 <= 90 * totalResourceHeight);
461
462    resourceBorderHeight = (totalResourceHeight - fullResourceHeight) / 2;
463
464    barPixWidths.resize(numMetrics);
465    for (unsigned met=0; met < numMetrics; met++)
466       barPixWidths[met].resize(numResources);
467
468    rethinkBarPixWidths();
469 }
470
471 void BarChart::rethinkBarPixWidths() {
472    // Given: udpated numValidMetrics, indirectResources[], validResources[],
473    //                numValidResourcse, validMetrics[], values[][],
474    //                metricCurrMaxVals[]
475    // Does: rethinks barPixWidths[][], redraws
476
477    // Set the height of each bar to the fraction of window height
478    // that equals the fraction of the bar's current value to its
479    // metric max value.
480
481    // This routine is called on the "critical path" --- when "new data
482    // callbacks" arrive.  We must be quick! (but keep the assert()s)
483
484    // Move on screen in sorted order, but store changes in actual order
485    for (unsigned resourcelcv=0; resourcelcv<numValidResources; resourcelcv++) {
486       // account for possible resources sorting:
487       const unsigned actualResource = indirectResources[resourcelcv];
488       assert(actualResource<numResources);
489       assert(validResources[actualResource]);
490
491       for (unsigned metriclcv=0; metriclcv<numValidMetrics; metriclcv++) {
492          // account for possible metrics sorting:
493          const unsigned actualMetric = metriclcv;
494          assert(actualMetric<numMetrics);
495          assert(validMetrics[actualMetric]);
496
497          register double theWidth = values[actualMetric][actualResource] /
498                                     metricCurrMaxVals[actualMetric];
499          // scale by window width (excluding border pixels)
500          theWidth *= this->width;
501
502          // truncate, double-check for isnan(), and store
503          register int integerResult = isnan(theWidth) ? 0 : (int)theWidth;
504          barPixWidths[actualMetric][actualResource] = integerResult;
505       }
506    }
507
508    // Draw!
509    if (HaveSeenFirstGoodWid)
510       drawWhenIdle.install(this);
511 }
512
513 void BarChart::processNewScrollPosition(int newPos) {
514    // Given: new scrollbar top position
515    //        up to date bar heights
516    // Does:  updates currScrollOffset and redraws the bars
517
518    if (!HaveSeenFirstGoodWid)
519       return;
520
521    // cout << "BarChart::processNewScrollPosition -- new pos=" << newPos << endl;
522
523    // adjust the current y-pixel scroll offset value and
524    // simulate an expose event
525    currScrollOffset = -newPos;
526  
527    drawWhenIdle.install(this);
528 }
529
530 void BarChart::rethinkDataFormat(BarChart::DataFormats theNewFormat) {
531    DataFormat = theNewFormat;
532
533    // we have changed the data format.  The max y-values need
534    // to be "re-thought" (else, what if we change from total to current;
535    // the max y value will be so high and won't ever get lowered)
536    RethinkMetricsAndResources();
537       // a bit more than this is really needed!
538 }
539
540 void BarChart::rethinkValidMetricsAndResources() {
541    // Given: updated numMetrics, numResources
542    // Does:  reallocates and rethinks validMetrics[], validResources[],
543    //        numValidMetrics, numValidResources based on dataGrid enabled
544    //        flags
545
546    validMetrics.resize(numMetrics);
547    for (unsigned metriclcv=0; metriclcv<numMetrics; metriclcv++)
548       validMetrics[metriclcv]=false;
549
550    validResources.resize(numResources);
551    for (unsigned resourcelcv=0; resourcelcv<numResources; resourcelcv++) 
552       validResources[resourcelcv]=false;
553
554    for (unsigned metlcv=0; metlcv<numMetrics; metlcv++)
555       for (unsigned reslcv=0; reslcv<numResources; reslcv++)
556          if (visi_Enabled(metlcv,reslcv)) {
557             validMetrics[metlcv] = true;
558             validResources[reslcv] = true;
559          }
560
561    numValidMetrics=0;
562    for (unsigned metlcv2=0; metlcv2<numMetrics; metlcv2++)
563       if (validMetrics[metlcv2])
564          numValidMetrics++;
565
566    numValidResources=0;
567    for (unsigned reslcv=0; reslcv<numResources; reslcv++)
568       if (validResources[reslcv])
569          numValidResources++;
570 }
571
572 void BarChart::rethinkIndirectResources() {
573    // Given: updated numValidResources, updated tcl array indirectResources()
574    // Does:  reallocates and rethinks indirectResources[]
575    //        extensive assertion checking
576
577    indirectResources.resize(numValidResources);
578
579    for (unsigned resourcelcv=0; resourcelcv<numValidResources; resourcelcv++) {
580       char buffer[20];
581       sprintf(buffer, "%d", resourcelcv);
582
583       char *string = Tcl_GetVar2(MainInterp, "indirectResources",
584                                  buffer, TCL_GLOBAL_ONLY);
585       if (string == NULL)
586          panic("BarChart::rethinkIndirectResources -- could not read indirectResources() from tcl");
587
588       const unsigned indirectNum = atoi(string);
589       if (indirectNum>=numResources) {
590          cerr << "BarChart::rethinkIndirectResources -- indirect value of "
591               << indirectNum << ' ' << "is too high (numResources="
592               << numResources << ')' << endl;
593          panic("");
594       }
595       else if (!validResources[indirectNum]) {
596          cerr << "BarChart::rethinkIndirectResources -- resource #"
597               << indirectNum << "is not presently valid" << endl;
598          panic("");
599       }
600
601       indirectResources[resourcelcv] = indirectNum;
602    }
603 }
604
605 void BarChart::lowestLevelDrawBarsDoubleBuffer() {
606    // Called on the critical path -- we must be quick!
607
608    unsigned long bgPixel = greyColor->pixel;
609
610    // note that the double-buffer pixmap DOES include space for border
611    // pixels, even though we don't make use of such pixels.  This was
612    // done because keeping the offscreen pixmap the same size as the
613    // window simplifies the drawing; e.g. barXoffsets[] already have
614    // borderPix added in to each element and it would be clumsy to
615    // compensate.
616
617    // Our XCopyArea() (when done drawing) is rigged (check the args) to
618    // not copy the border pixel area...
619
620    // clear the offscreen buffer.  XClearArea() works only on windows,
621    // so fill a rectangle with the background color.
622    XSetForeground(display, myGC, bgPixel);
623    XSetBackground(display, myGC, bgPixel);
624    XFillRectangle(display, doubleBufferPixmap,
625                   myGC,
626                   borderPix, // x-offset, relative to drawable
627                   borderPix, // y-offset, relative to drawable
628                   width,     // does not include border pixels
629                   height     // does not include border pixels
630                   );
631
632    // Do the drawing onto offscreen pixmap [yikes -- expensive!]
633
634    // Loop through the resources in sorted order
635    int resourceBoundary = borderPix + currScrollOffset;
636    const int left = 0 + borderPix;
637    
638    for (unsigned resourcelcv=0; resourcelcv<numValidResources; resourcelcv++) {
639       // account for sorted resources:
640       const unsigned actualResource = indirectResources[resourcelcv];
641       assert(actualResource<numResources);
642       assert(validResources[actualResource]);
643
644       int top = resourceBoundary + resourceBorderHeight;
645
646       // for robustness:
647       unsigned thisTimeNumValidMetrics=0;      
648
649       for (unsigned metriclcv=0; metriclcv<numMetrics; metriclcv++) {
650          // Eventually, we will account for sorted metrics here.
651          // Until then, we test the validMetrics[] field.  If false,
652          // then skip this metric.
653          const unsigned actualMetric = metriclcv;
654          if (!validMetrics[actualMetric]) continue;
655
656          assert(actualMetric<numMetrics);
657          assert(validMetrics[actualMetric]);
658
659          // for robustness:
660          thisTimeNumValidMetrics++;
661
662          XSetForeground(display, myGC,
663                         metricColors[actualMetric % metricColors.size()]->pixel);
664    
665          const int thisBarWidth = barPixWidths[actualMetric][actualResource];
666          XFillRectangle(display,
667                         doubleBufferPixmap,
668                         myGC,
669                         left,
670                         top,
671                         thisBarWidth,
672                         individualResourceHeight // height
673                         );
674
675          top += individualResourceHeight;
676       }
677
678       resourceBoundary += totalResourceHeight;
679  
680       assert(thisTimeNumValidMetrics == numValidMetrics);
681    }
682
683    // copy offscreen pixmap onto screen
684    
685    XCopyArea(display,
686              doubleBufferPixmap, // source
687              Tk_WindowId(theWindow), // dest
688              myGC,
689              borderPix, borderPix, // source x, y (note how we exclude the border)
690              width, height, // these vrbles are preset to exclude the borders
691              borderPix, borderPix // dest x, y (note how we exclude the border)
692              );
693 }
694
695 void BarChart::lowestLevelDrawBars(ClientData cd) {
696    // NOTE: a --static-- member function --> no "this" exists!!
697    BarChart *pthis = (BarChart *)cd;
698
699    if (!pthis->HaveSeenFirstGoodWid) {
700       // Perhaps a race condition; new data arrived before the barchart
701       // was fully constructed?
702       cout << "BarChart::lowestLevelDrawBars -- haven't yet mapped? (ignoring)" << endl;
703       return;
704    }
705
706    // Finally, we actually draw:
707    pthis->lowestLevelDrawBarsDoubleBuffer();
708 }