initial implementation. A to-do list is kept in barChart.tcl
[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
5 // $Log: barChart.C,v $
6 // Revision 1.1  1994/09/29 19:48:27  tamches
7 // initial implementation.  A to-do list is kept in barChart.tcl
8 //
9
10 // tk/tcl has a very nice interface for mixing C++ and tk/tcl
11 // scripts.  From within tcl, C++ code can be called via
12 // new commands; new commands are created with a call
13 // to Tcl_CreateCommand(...)  Unfortunately, you can see
14 // a catch-22 here; in order to even call Tcl_CreateCommand(),
15 // we need to be out of tcl and in C++!  The only way around
16 // this problem is to start the process in C++ and have it
17 // call tk/tcl.  This is done with Tcl_EvalFile(...).  Of
18 // course, just before this, we sneak in our Tcl_CreateCommand()
19 // call.
20
21 // Somewhere in the tk library, there is a main() that
22 // basically does this for us; you have to provide a -f tcl-file-name
23 // option so it knows what to send to Tcl_EvalFile(...)
24 // This main() assumes a function Tcl_InitApp(), which will
25 // be called before the Tcl_EvalFile() is performed...that's
26 // the time to sneak in our Tcl_CreateCommand() stuff...
27
28 #include <assert.h>
29 #include <stdlib.h> // exit()
30 #include <iostream.h>
31 #include <math.h>
32
33 #include <tcl.h>
34 #include <tk.h>
35
36 #include "visi/h/visualization.h"
37 #include "dg2.h"
38 #include "barChartTcl.h"
39
40 #pragma implementation "array2d.h"
41
42 #include "barChart.h"
43
44 BarChart *theBarChart; // main data structure; holds bar information.  Does not
45                        // hold x axis or y axis information (or contents), because
46                        // they are managed just fine from tcl (barChart.tcl) at present.
47                        // Created dynamically in DrawBarsInstallCommand.  Cannot be
48                        // created before then since necessary constructor arguments
49                        // such as tk window name are not yet known.
50
51 /* ********************************************************************
52  * *********************** BarChart methods ***************************
53  * ********************************************************************
54 */
55
56 BarChart::BarChart(char *tkWindowName,
57                    const bool dblBuffer, const bool noFlicker,
58                    const int initNumMetrics, const int initNumResources,
59                    const bool initFlushFlag) :
60             metricColors(initNumMetrics),
61             prevBarHeights(initNumMetrics, initNumResources),
62             barXoffsets(initNumMetrics, initNumResources),
63             barWidths  (initNumMetrics, initNumResources),
64             barHeights (initNumMetrics, initNumResources),
65             barValues  (initNumMetrics, initNumResources),
66             metricCurrMaxYs(initNumMetrics),
67             flushFlag (initFlushFlag)
68              {
69
70    theWindowName = new char[strlen(tkWindowName)+1];
71    if (theWindowName == NULL)
72       panic("BarChart constructor: out of memory!");
73    strcpy(theWindowName, tkWindowName);
74
75    theWindow = Tk_NameToWindow(MainInterp, tkWindowName, Tk_MainWindow(MainInterp));
76    if (theWindow == NULL)
77       panic("BarChart constructor: Tk_NameToWindow() failed!");
78
79    HaveSeenFirstGoodWid = false;
80    wid      = Tk_WindowId(theWindow);
81    borderPix = 2;
82    currScrollOffset = 0;
83    width    = Tk_Width(theWindow);
84    height   = Tk_Height(theWindow);
85    isMapped = Tk_IsMapped(theWindow);
86    display  = Tk_Display(theWindow);
87    width  -= borderPix*2;
88    height -= borderPix*2;
89    
90    greyColor = Tk_GetColor(MainInterp, theWindow, None, Tk_GetUid("grey"));
91
92    DataFormat = Current;
93    
94    drawMode = dblBuffer ? DoubleBuffer : (noFlicker ? NoFlicker : Flicker);
95
96    RethinkMetricsAndResources();
97       // sets numMetrics, numResources, barXoffets, barWidths,
98       // barHeights, prevBarHeights, barValues, metricCurrMaxYs
99 }
100
101 BarChart::~BarChart() {
102    Tk_CancelIdleCall(lowestLevelDrawBars, NULL);
103
104    delete [] theWindowName;
105
106    XFreeGC(display, myGC);   
107
108    Tk_FreeColor(greyColor);
109    for (int metriclcv=0; metriclcv<numMetrics; metriclcv++)
110       Tk_FreeColor(metricColors[metriclcv]);
111
112    changeDoubleBuffering(false, false); // free offscreen pixmap, if necessary
113 }
114
115 void BarChart::changeDoubleBuffering(bool doubleBuffer, bool noFlicker) {
116    // note that we don't try to optimize and return immediately if
117    // the mode hasn't changed.  Why? to accomodate possible change in size.
118
119    if (!HaveSeenFirstGoodWid)
120       return;
121
122    DrawModes newDrawMode;
123    if (doubleBuffer)
124       newDrawMode = DoubleBuffer;
125    else if (noFlicker)
126       newDrawMode = NoFlicker;
127    else
128       newDrawMode = Flicker;
129
130    if (drawMode == DoubleBuffer)
131       // erase offscreen pixmap and reallocate with new size
132       XFreePixmap(display, doubleBufferPixmap);
133
134    drawMode = newDrawMode;
135
136    if (drawMode == DoubleBuffer)
137       doubleBufferPixmap = XCreatePixmap(display, wid,
138                                          Tk_Width(theWindow),
139                                          Tk_Height(theWindow),
140                                          Tk_Depth(theWindow));
141          // note that we use Tk_Width and Tk_Height instead of width and height.
142          // these values differ by (borderPix) in each dimension.
143    else if (drawMode == NoFlicker) 
144       ResetPrevBarHeights();
145 }
146
147 bool BarChart::TryFirstGoodWid() {
148    // returns true if the wid is valid
149
150    if (!HaveSeenFirstGoodWid) {
151       wid = Tk_WindowId(theWindow);
152       if (wid == 0)
153          return false; // sigh; still invalid
154
155       HaveSeenFirstGoodWid = true;
156
157       // initialize gc
158       XGCValues values;
159       values.foreground = greyColor->pixel;
160       values.background = greyColor->pixel;
161       // values.graphics_exposures = False;
162
163       myGC = XCreateGC(display, wid,
164                        GCForeground | GCBackground,
165                        &values);
166       if (NULL==myGC)
167          panic("BarChart constructor: XCreateGC() failed!");
168
169       // initialize offscreen pixmap
170       if (drawMode == DoubleBuffer)
171          doubleBufferPixmap = XCreatePixmap(display,
172                                             wid, // used only to determine the screen
173                                             Tk_Width(theWindow),
174                                             Tk_Height(theWindow),
175                                             Tk_Depth(theWindow)
176                                             );
177                                          
178       // simulate a resize
179       processResizeWindow();
180    }
181
182    return true;   
183 }
184
185 void BarChart::processResizeWindow() {
186    if (!TryFirstGoodWid())
187       return; // our window is still invalid
188
189    width  = Tk_Width(theWindow) - 2*borderPix; // subtract left+right border
190    height = Tk_Height(theWindow) - 2*borderPix; // subract top+bottom border
191
192    // update off-screen pixmap's dimensions to accomodate the resize
193    changeDoubleBuffering(drawMode==DoubleBuffer, drawMode==NoFlicker);
194
195    RethinkBarLayouts();
196 }
197
198 void BarChart::processExposeWindow() {
199    if (!TryFirstGoodWid())
200       return; // our window is still invalid
201
202    cout << "Welcome to BarChart::processExposeWindow" << endl;
203    if (drawMode == NoFlicker)
204       ResetPrevBarHeights();
205
206    lowLevelDrawBars();
207 }
208
209 char *gimmeColorName(const int metriclcv) {
210    static char *theNames[] = {
211       "blue",
212       "red",
213       "green",
214       "orange"
215    };
216
217    return theNames[metriclcv % 4];
218 }
219
220 void BarChart::RethinkMetricColors() {
221    metricColors.reallocate(numMetrics);
222
223    for (int metriclcv=0; metriclcv<numMetrics; metriclcv++) {
224       XColor *theColor = Tk_GetColor(MainInterp, theWindow, None, Tk_GetUid(gimmeColorName(metriclcv)));
225
226       metricColors[metriclcv] = theColor;
227    }
228 }
229
230 void BarChart::ClearScreen() {
231    XSetForeground(display, myGC, greyColor->pixel);
232    XSetBackground(display, myGC, greyColor->pixel);
233
234    XFillRectangle(display, wid, myGC,
235                   borderPix, // start-x
236                   borderPix, // start-y
237                   width, // this vrble should have already been adjusted re: borderPix
238                   height);
239 }
240
241 void BarChart::ResetPrevBarHeights() {
242    prevBarHeights.reallocate(numMetrics, numResources);
243
244    if (drawMode == NoFlicker) {
245       for (int metriclcv=0; metriclcv<numMetrics; metriclcv++)
246          for (int resourcelcv=0; resourcelcv<numResources; resourcelcv++)
247             prevBarHeights[metriclcv][resourcelcv]=0;
248       if (HaveSeenFirstGoodWid)
249          ClearScreen();
250    }
251 }
252
253 void BarChart::RethinkMetricsAndResources() {
254    // clear window, erase and reallocate barXoffsets, barWidths, prevBarHeights,
255    // barHeights, metricCurrMaxYs; set numMetrics, numResources -- all based on a
256    // complete re-reading from dataGrid[][].  (If visi had provided more fine-grained
257    // callbacks than ADDMETRICSRESOURCES, such a crude routine would not be necessary.)
258
259    // When done, redraw.
260
261    // #include "visualization.h" from visi-lib to get global
262    // variable "dataGrid"
263
264    numMetrics   = dataGrid.NumMetrics();
265    numResources = dataGrid.NumResources();
266
267    cout << "Welcome to BarChart::RethinkMetricsAndResources; m=" << numMetrics << "; r=" << numResources << endl;
268
269    // the following is very unfortunate for existing metric/resource pairs;
270    // their values do not change and so should not be reset.  This is
271    // particularly troublesome for metricCurrMaxYs[], where resetting their
272    // values (as we do here) can be considered a bug.
273    barXoffsets.reallocate(numMetrics, numResources);
274    barWidths  .reallocate(numMetrics, numResources);
275    barHeights .reallocate(numMetrics, numResources);
276    prevBarHeights.reallocate(numMetrics, numResources);
277    barValues  .reallocate(numMetrics, numResources);
278    metricCurrMaxYs.reallocate(numMetrics);
279
280    // reset bar values (very unfortunate for existing pairs)
281    for (int metriclcv=0; metriclcv<numMetrics; metriclcv++)
282       for (int resourcelcv=0; resourcelcv<numResources; resourcelcv++)
283          barValues[metriclcv][resourcelcv]=0;
284
285    // reset max y values (unfortunate for existing pairs)
286    for (metriclcv=0; metriclcv<numMetrics; metriclcv++) {
287       char buffer[64];
288       sprintf(buffer, "%d", metriclcv);
289       char *str = Tcl_GetVar2(MainInterp, "metricMaxValues", buffer, TCL_GLOBAL_ONLY);
290       if (str == NULL)
291          panic("BarChart::RethinkMetricsAndResources() -- could not read 'metricMaxValues'");
292
293       metricCurrMaxYs[metriclcv] = atof(str);
294    }
295
296    RethinkMetricColors(); // reallocates and rethinks metricColors[]
297
298    RethinkBarLayouts(); // as if there were a resize (no reallocations, but
299                         // resets/rethinks barXoffsets[], barWidths[], barHeights[],
300                         // prevBarHeights[])
301
302    if (HaveSeenFirstGoodWid)
303       lowLevelDrawBars();
304 }
305
306 void BarChart::processNewData(int newBucketIndex) {
307    // assuming new data has arrived at the given bucket index for all
308    // metric/rsrc pairs, read the new information from dataGrid[][],
309    // update barHeights[][], barValues[][], and metricCurrMaxYs[] (if needed),
310    // and call lowLevelDrawBars()
311    
312    // PASS 1: Update barValues[][] and check for y-axis overflow, calling
313    //         setMetricNewMaxY() if overflow is detected.
314
315    for (int metriclcv=0; metriclcv<numMetrics; metriclcv++) {
316       visi_GridHistoArray &metricValues = dataGrid[metriclcv];
317
318       for (int resourcelcv=0; resourcelcv<numResources; resourcelcv++) {
319          visi_GridCellHisto &theCell = metricValues[resourcelcv];
320
321          double newVal;
322          switch (DataFormat) {
323             case Current:
324                newVal = theCell.Value(newBucketIndex);
325                break;
326             case Average:
327                // newVal = theCell.AggregateValue(0); // ???
328                newVal = dataGrid.AggregateValue(metriclcv, resourcelcv);
329                break;
330             case Total:
331                // newVal = theCell.SumValue(1); // ???
332                newVal = dataGrid.SumValue(metriclcv, resourcelcv);
333                break;
334             default:
335                assert(false);
336          }
337
338          barValues[metriclcv][resourcelcv] = newVal;
339
340          // the dreaded check for y-axis overflow (slows things down greatly if there
341          // is indeed overflow)
342          double currMaxY = metricCurrMaxYs[metriclcv];
343          if (newVal > currMaxY)
344             setMetricNewMaxY(metriclcv, newVal);
345       }
346    }
347
348    // PASS 2: Now that the y-axis overflows, if any, have been processed, we can
349    //         calculate barHeights with confidence that the scale isn't going to
350    //         change.
351
352    RethinkBarHeights();
353 }
354
355 double BarChart::nicelyRoundedMetricNewYMaxValue(int metricindex, double newmaxval) {
356    assert(0<=metricindex && metricindex<numMetrics);
357
358    char buffer[256];
359    sprintf(buffer, "lindex [getMetricHints %s] 3", dataGrid.MetricName(metricindex));
360
361    if (TCL_OK != Tcl_Eval(MainInterp, buffer))
362       panic("BarChart::nicelyRoundedMetricNewYMaxValue() -- could not eval");
363
364    double hintedStep = atof(MainInterp->result);
365
366    double result = newmaxval + (hintedStep - fmod(newmaxval, hintedStep));
367       // if fmod() doesn't return a number greater than 0, this won't work quite right
368
369    return result;
370 }
371
372 void BarChart::setMetricNewMaxY(int metricindex, double newmaxval) {
373    // given an actual bar value (newmaxval) that overflows the current
374    // y-axis maximum value for the given metric, rethink what the
375    // y-axis maximum value for the given metric should be.
376    // Note that it may not be exactly "newmaxval"; we usually want
377    // to round to a relatively nice number.
378
379    assert(0<=metricindex && metricindex<numMetrics);
380
381    newmaxval = nicelyRoundedMetricNewYMaxValue(metricindex, newmaxval);
382    metricCurrMaxYs[metricindex] = newmaxval;
383
384    char buffer[256];
385    sprintf(buffer, "processNewMetricMax %d %g", metricindex, newmaxval);
386    if (TCL_OK != Tcl_Eval(MainInterp, buffer))
387       cerr << "warning -- BarChart::setMetricNewMaxY() could not inform barChart.tcl of new-max-y-value (no script processNewMetricMax?)" << endl;
388 }
389
390 void BarChart::RethinkBarLayouts() {
391    // assuming a complete resize (but not an added or deleted metric
392    // or resource or even new data values), fill in barXoffsets, barWidths, etc.
393
394    // does not touch metricCurrMaxYs or barValues
395
396    // note: the following loop starts with resources, then does metrics.  should not
397    // cause any big problems, and more intuitive in this case...
398    char *fullResourceWidthStr = Tcl_GetVar(MainInterp, "resourceWidth", TCL_GLOBAL_ONLY);
399    if (NULL == fullResourceWidthStr)
400       panic("BarChart::RethinkBarLayouts() -- could not read 'resourceWidth' from tcl");
401
402    int totalResourceWidth = atoi(fullResourceWidthStr);
403    int fullResourceWidth = (totalResourceWidth * 90) / 100;
404
405    int resourceBorderWidth = (totalResourceWidth - fullResourceWidth) / 2;
406    int individualResourceWidth = (numMetrics == 0) ? 0 : fullResourceWidth / numMetrics;
407
408    for (int resourcelcv=0; resourcelcv<numResources; resourcelcv++) {
409       int left = borderPix + (resourcelcv * totalResourceWidth) + resourceBorderWidth;
410
411       for (int metriclcv=0; metriclcv<numMetrics; metriclcv++) {
412          barXoffsets[metriclcv][resourcelcv] = left + metriclcv * individualResourceWidth;
413          barWidths  [metriclcv][resourcelcv] = individualResourceWidth;
414          barHeights [metriclcv][resourcelcv] = 0; // all bars start off flat
415       }
416    }
417
418    if (drawMode == NoFlicker)
419       ResetPrevBarHeights();
420 }
421
422 void BarChart::RethinkBarHeights() {
423    // set the height of each bar to the fraction of window height
424    // that equals the fraction of the bar's current value to its
425    // metric max y value.
426
427    for (int metriclcv=0; metriclcv<numMetrics; metriclcv++) {
428       for (int resourcelcv=0; resourcelcv<numResources; resourcelcv++) {
429          double theHeight = barValues[metriclcv][resourcelcv] /
430                             metricCurrMaxYs[metriclcv];
431          theHeight *= this->height; // scale by window height (excluding border pixels)
432          barHeights[metriclcv][resourcelcv] = (int)theHeight;
433       }
434    }
435
436    if (HaveSeenFirstGoodWid)
437       lowLevelDrawBars();
438 }
439
440 bool currentlyInstalledLowLevelDrawBars=false;
441
442 void BarChart::lowLevelDrawBars() {
443    // perhaps we should check to see if the barchart program has
444    // been shut down before attempting to do anything?  Nah,
445    // just be sure to call Tk_CancelIdleCall() on lowestLevelDrawBars()
446    // in the destructor.
447
448    isMapped = Tk_IsMapped(theWindow);
449    if (!isMapped)
450       return;
451
452    if (!HaveSeenFirstGoodWid)
453       return;
454
455    if (!currentlyInstalledLowLevelDrawBars) {
456       currentlyInstalledLowLevelDrawBars = true;
457
458       Tk_DoWhenIdle(lowestLevelDrawBars, NULL);
459    }
460 }
461
462 void BarChart::lowestLevelDrawBarsDoubleBuffer() {
463    unsigned long bgPixel = greyColor->pixel;
464
465    // note that the double-buffer pixmap DOES include space for border
466    // pixels, even though we don't make use of such pixels.  This was
467    // done because keeping the offscreen pixmap the same size as the
468    // window simplifies the drawing; e.g. barXoffsets[] already have
469    // borderPix added in to each element and it would be clumsy to
470    // compensate.
471
472    // Our XCopyArea() (when done drawing) is rigged (check the args) to
473    // not copy the border pixel area...
474
475    // clear the offscreen buffer.  XClearArea() works only on windows, so fill a rectangle
476    // with the background color.
477    XSetForeground(display, myGC, bgPixel);
478    XSetBackground(display, myGC, bgPixel);
479    XFillRectangle(display, doubleBufferPixmap,
480                   myGC,
481                   borderPix, // x-offset, relative to drawable
482                   borderPix, // y-offset, relative to drawable
483                   width,     // does not include border pixels
484                   height     // does not include border pixels
485                   );
486
487    if (flushFlag)
488       XSync(display, False);
489
490    // do the drawing onto offscreen pixmap
491    for (int metriclcv=0; metriclcv<numMetrics; metriclcv++) {
492       XSetForeground(display, myGC, metricColors[metriclcv]->pixel);
493    
494       for (int resourcelcv=0; resourcelcv<numResources; resourcelcv++) {
495          XFillRectangle(display,
496                         doubleBufferPixmap,
497                         myGC,
498                         currScrollOffset + barXoffsets[metriclcv][resourcelcv], // left-x
499                         height - barHeights[metriclcv][resourcelcv] - borderPix, // top-y
500                         barWidths[metriclcv][resourcelcv], // width
501                         barHeights[metriclcv][resourcelcv] // height
502                        );
503       }
504    }
505
506    if (flushFlag)
507       XSync(display, False);
508
509    // copy offscreen pixmap onto screen
510    // cout << '{'; cout.flush();
511    
512    XCopyArea(display,
513              doubleBufferPixmap, // source
514              wid,                // dest
515              myGC,
516              borderPix, borderPix, // source x, y (note how we exclude the border)
517              width, height, // these vrbles are preset to exclude the borders
518              borderPix, borderPix // dest x, y (note how we exclude the border)
519             );
520    // cout << '}'; cout.flush();
521
522    if (flushFlag)
523       XSync(display, False);
524 }
525
526 void BarChart::lowestLevelDrawBarsNoFlicker() {
527   // be smart about what gets drawn verses what gets erased; if a bar is lower than
528   // last time, erase the appropriate chunk on top; if a bar is higher than
529   // last time, draw the approrpriate chunk on top.
530   
531   unsigned long bgPixel = greyColor->pixel;
532
533   for (int metriclcv=0; metriclcv<numMetrics; metriclcv++) {
534      unsigned long fgPixel = metricColors[metriclcv]->pixel;
535
536      for (int resourcelcv=0; resourcelcv<numResources; resourcelcv++) {
537         int newHeight = barHeights    [metriclcv][resourcelcv];
538         int oldHeight = prevBarHeights[metriclcv][resourcelcv];
539  
540         if (newHeight > oldHeight) {
541            // the bar is higher than last time; draw the appropriate chunk on top
542            XSetForeground(display, myGC, fgPixel);
543            XSetBackground(display, myGC, bgPixel);
544
545            XFillRectangle(display, wid, myGC,
546                           currScrollOffset + barXoffsets[metriclcv][resourcelcv], // left-x
547                           height - newHeight - borderPix, // top-y
548                           barWidths[metriclcv][resourcelcv], // width
549                           newHeight - oldHeight // height
550                          );
551         }
552         else if (newHeight < oldHeight) {
553            // the bar is lower than last time; erase appropriate chunk on top
554            XSetForeground(display, myGC, bgPixel);
555            XSetBackground(display, myGC, fgPixel);
556
557            XFillRectangle(display, wid, myGC,
558                           currScrollOffset + barXoffsets[metriclcv][resourcelcv], // left-x
559                           height - oldHeight - borderPix, // top-y
560                           barWidths[metriclcv][resourcelcv], // width
561                           oldHeight - newHeight // height
562                           );
563         }
564         else
565             ; // no need to redraw bar at the same height as before
566
567         // and finally...
568         prevBarHeights[metriclcv][resourcelcv] = newHeight;
569      }
570   }
571
572   if (flushFlag)
573      XSync(display, False);
574 }
575
576 void BarChart::lowestLevelDrawBarsFlicker() {
577    ClearScreen(); // clear window, leaving border pixels alone
578    if (flushFlag)
579       XSync(display, False);
580
581    unsigned long bgPixel = greyColor->pixel;
582
583    // do the drawing onto offscreen pixmap
584    for (int metriclcv=0; metriclcv<numMetrics; metriclcv++) {
585       XSetForeground(display, myGC, metricColors[metriclcv]->pixel);
586       XSetBackground(display, myGC, bgPixel);
587    
588       for (int resourcelcv=0; resourcelcv<numResources; resourcelcv++) {
589          XFillRectangle(display, wid,
590                         myGC,
591                         currScrollOffset + barXoffsets[metriclcv][resourcelcv], // left-x
592                         height - barHeights[metriclcv][resourcelcv] - borderPix, // top-y
593                         barWidths[metriclcv][resourcelcv], // width
594                         barHeights[metriclcv][resourcelcv] // height
595                        );
596    
597       }
598    }
599
600    if (flushFlag)
601       XSync(display, False);
602 }
603
604 void BarChart::lowestLevelDrawBars(ClientData ignore) {
605    // cout << '['; cout.flush();
606
607    // NOTE: a --static-- member function --> no "this" exists!!
608
609    if (!theBarChart->HaveSeenFirstGoodWid) {
610       cout << "BarChart::lowestLevelDrawBars -- haven't yet mapped? (ignoring)" << endl;
611       return;
612    }
613
614    currentlyInstalledLowLevelDrawBars = false; // now, new requests will be handled
615       // It seems wrong to delay this line until the bottom of the
616       // routine; it could cause new data to be ignored
617
618    switch (theBarChart->drawMode) {
619       case DoubleBuffer:
620          theBarChart->lowestLevelDrawBarsDoubleBuffer();
621          break;
622       case NoFlicker:
623          theBarChart->lowestLevelDrawBarsNoFlicker();
624          break;
625       case Flicker:
626          theBarChart->lowestLevelDrawBarsFlicker();
627          break;
628       default: assert(0);
629    }
630
631    // cout << ']'; cout.flush();
632 }
633
634 void BarChart::processNewScrollPosition(int newPos) {
635    if (!HaveSeenFirstGoodWid)
636       return;
637
638    // cout << "BarChart::processNewScrollPosition -- new pos=" << newPos << endl;
639
640    // adjust the current x-pixel scroll offset value and
641    // simulate an expose event
642    currScrollOffset = -newPos;
643  
644    // tk is clever with its canvas widgets to never start the leftmost
645    // drawing portion at less than 0; we must be just as clever to keep
646    // everything positioned correctly.
647 //   if (currScrollOffset < 0 && scrollbarisfullwidth)
648 //      currScrollOffset=0;
649
650    if (drawMode == NoFlicker)
651       ResetPrevBarHeights();
652
653    lowLevelDrawBars();
654 }
655
656 void BarChart::rethinkDataFormat() {
657    // assuming the data format has changed, read its
658    // value (from tcl) and adjust our internal settings accordingly.
659
660    char *dataFormatString = Tcl_GetVar(MainInterp, "DataFormat", TCL_GLOBAL_ONLY);
661    if (dataFormatString == NULL) {
662       cerr << "warning: BarChart::rethinkDataFormat() -- could not read tcl vrble 'DataFormat'; ignoring" << endl;
663       return;
664    }
665
666    if (0==strcmp(dataFormatString, "Instantaneous"))
667       DataFormat = Current;
668    else if (0==strcmp(dataFormatString, "Average"))
669       DataFormat = Average;
670    else if (0==strcmp(dataFormatString, "Sum"))
671       DataFormat = Total;
672    else {
673       cerr << "BarChart::rethinkDataFormat() -- unrecognized format '"
674            << dataFormatString << "'; ignoring" << endl;
675       return;
676    }
677
678    // we have changed the data format.  The max y-values need
679    // to be "re-thought" (else, what if we change from total to current;
680    // the max y value will be so high and won't ever get lowered)
681    RethinkMetricsAndResources();
682       // a bit more than is really needed!
683 }