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