fixed bug which clipped cells incorrectly
[dyninst.git] / visiClients / tableVisi / src / tableVisi.C
1 // tableVisi.C
2 // Ariel Tamches
3
4 /*
5  * $Log: tableVisi.C,v $
6  * Revision 1.3  1995/11/15 01:05:16  tamches
7  * fixed bug which clipped cells incorrectly
8  *
9  * Revision 1.2  1995/11/08 21:46:46  tamches
10  * some ui bug fixes
11  *
12  * Revision 1.1  1995/11/04 00:45:20  tamches
13  * First version of new table visi
14  *
15  */
16
17 #include "minmax.h"
18 #include "tkTools.h"
19 #include "tableVisi.h"
20
21 /* ************************************************************* */
22
23 extern "C" {bool isnan(double);}
24 void tableVisi::double2string(char *buffer, double val) const {
25    // uses numSigFigs
26    if (isnan(val)) {
27       buffer[0] = '\0';
28       return;
29    }
30
31    char conversionString[100];
32    sprintf(conversionString, "%%.%dg", numSigFigs);
33    sprintf(buffer, conversionString, val);
34
35 //   cout << "from " << buffer << " to " << flush;
36
37    // add commas
38    if (strlen(buffer)==0)
39       return;
40
41    const char *decPtr = strchr(buffer, '.');
42
43    if (decPtr && (decPtr - buffer <= 3))
44       return; // no commas will be added since there aren't at least 4 integer digits
45
46    if (decPtr == NULL) {
47       // try for exponential notation
48       decPtr = strchr(buffer, 'e');
49
50       if (decPtr==NULL)
51          decPtr = &buffer[strlen(buffer)]; // the '\0'
52    }
53
54    // invariant: decPtr now points to the character AFTER the
55    // last integer digit (i.e., the decimal point), or the '\0'
56    // if none exists.
57
58    // Now let's walk backwards, looking for places to insert commas
59    char integerPartBuffer[200]; // will include commas
60    char *integerPart = integerPartBuffer;
61    const char *walkPtr = decPtr-1;
62    unsigned copyCount=0;
63    while (walkPtr >= buffer) {
64       if (copyCount > 0 && copyCount % 3 == 0)
65          *integerPart++ = ',';
66
67       copyCount++;
68       *integerPart++ = *walkPtr--;
69    }
70
71    char decimalPart[200];
72       // will incl dec point, if applicable.  May be
73       // the empty-string.
74    strcpy(decimalPart, decPtr);
75
76    // note: integerPartBuffer is backwards...we must reverse it
77    char *bufferPtr = buffer;
78    while (--integerPart >= integerPartBuffer)
79       *bufferPtr++ = *integerPart;
80    strcpy(bufferPtr, decimalPart);
81
82 //   cout << buffer << endl;
83 }
84
85 /* ************************************************************* */
86
87 XFontStruct *tableVisi::myXLoadQueryFont(const string &fontName) const {
88    XFontStruct *result = XLoadQueryFont(Tk_Display(theTkWindow),
89                                         fontName.string_of());
90    if (result == NULL) {
91       cerr << "could not find font " << fontName << endl;
92       exit(5);
93    }
94
95    return result;
96 }
97
98 XColor *tableVisi::myTkGetColor(Tcl_Interp *interp, const string &colorName) const {
99    XColor *result = Tk_GetColor(interp, theTkWindow, Tk_GetUid(colorName.string_of()));
100    if (result == NULL) {
101       cerr << "could not allocate color " << colorName << endl;
102       exit(5);
103    }
104    return result;
105 }
106
107 /* ************************************************************* */
108
109 tableVisi::tableVisi(Tcl_Interp *interp,
110                      Tk_Window iTkWindow,
111                      const string &metricFontName,
112                      const string &metricUnitsFontName,
113                      const string &focusFontName,
114                      const string &cellFontName,
115                      const string &iLineColorName,
116                      const string &iMetricColorName,
117                      const string &iMetricUnitsColorName,
118                      const string &iFocusColorName,
119                      const string &cellColorName,
120                      const string &backgroundColorName,
121                      unsigned iSigFigs
122                      ) {
123    // metrics[], foci[], indirectMetrics[], indirectFoci[], and cells[][]
124    // are all initialized to zero-sized arrays.
125
126    theTkWindow = iTkWindow;
127    theDisplay = Tk_Display(theTkWindow);
128
129    offscreenPixmap = (Pixmap)NULL;
130       // sorry, can't XCreatePixmap() until the window becomes mapped.
131
132    backgroundColor = myTkGetColor(interp, backgroundColorName);
133
134    offset_x = offset_y = 0;
135    all_cells_width = all_cells_height = 0;
136
137    metricNameFont = myXLoadQueryFont(metricFontName);
138    metricUnitsFont = myXLoadQueryFont(metricUnitsFontName);
139    focusNameFont = myXLoadQueryFont(focusFontName);
140    cellFont = myXLoadQueryFont(cellFontName);
141
142    focusLongNameMode = true;
143    numSigFigs = iSigFigs;
144 //   dataFormat = Current;
145
146    maxFocusNamePixWidth = 0;
147
148    // The GC's, like offscreenPixmap, can't be created until the
149    // window becomes mapped.
150    lineColor = myTkGetColor(interp, iLineColorName);
151    lineColorGC = NULL;
152
153    metricNameColor = myTkGetColor(interp, iMetricColorName);
154    metricNameGC = NULL;
155
156    metricUnitsColor = myTkGetColor(interp, iMetricUnitsColorName);
157    metricUnitsGC = NULL;
158   
159    focusNameColor = myTkGetColor(interp, iFocusColorName);
160    focusNameGC = NULL;
161
162    cellColor = myTkGetColor(interp, cellColorName);
163    cellGC = NULL;
164 }
165
166 tableVisi::~tableVisi() {
167    // arrays metrics[], foci[], indirectMetrics[], indirectFoci[], cells[][] will
168    // delete themselves.
169
170    Tk_FreeColor(cellColor);
171    Tk_FreeColor(focusNameColor);
172    Tk_FreeColor(metricUnitsColor);
173    Tk_FreeColor(metricNameColor);
174    Tk_FreeColor(lineColor);
175    Tk_FreeColor(backgroundColor);
176
177    XFreeFont(theDisplay, focusNameFont);
178    XFreeFont(theDisplay, metricUnitsFont);
179    XFreeFont(theDisplay, metricNameFont);
180
181    if (!offscreenPixmap)
182       // the offscreen pixmap was never allocated(!)...so, we never
183       // got around to mapping the window!
184       return;
185
186    XFreeGC(theDisplay, cellGC);
187    XFreeGC(theDisplay, focusNameGC);
188    XFreeGC(theDisplay, metricUnitsGC);
189    XFreeGC(theDisplay, metricNameGC);
190    XFreeGC(theDisplay, lineColorGC);
191    XFreeGC(theDisplay, backgroundGC);
192
193    XFreePixmap(theDisplay, offscreenPixmap);
194 }
195
196 bool tableVisi::tryFirst() {
197    if (offscreenPixmap) {
198       // the offscreen pixmap has been allocated, so the window
199       // has presumably been mapped already
200       assert(Tk_WindowId(theTkWindow) != 0);
201       return true;
202    }
203
204    // the offscreen pixmap hasn't been allocated, so it's now time
205    // to check to see if it should be.
206    if (Tk_WindowId(theTkWindow) == 0)
207       return false; // nuts; not ready yet
208
209    // Ready to allocate graphical structures now!
210    offscreenPixmap = XCreatePixmap(Tk_Display(theTkWindow),
211                                    Tk_WindowId(theTkWindow),
212                                    1, 1, // dummy width, height
213                                    Tk_Depth(theTkWindow));
214    XGCValues values;
215    values.foreground = backgroundColor->pixel;
216    backgroundGC = XCreateGC(Tk_Display(theTkWindow), Tk_WindowId(theTkWindow),
217                             GCForeground, &values);
218
219    values.foreground = lineColor->pixel;
220    lineColorGC = XCreateGC(Tk_Display(theTkWindow), Tk_WindowId(theTkWindow),
221                            GCForeground, &values);
222
223    values.foreground = metricNameColor->pixel;
224    values.font = metricNameFont->fid;
225    metricNameGC = XCreateGC(Tk_Display(theTkWindow), Tk_WindowId(theTkWindow),
226                             GCForeground | GCFont, &values);
227
228    values.foreground = metricUnitsColor->pixel;
229    values.font = metricUnitsFont->fid;
230    metricUnitsGC = XCreateGC(Tk_Display(theTkWindow), Tk_WindowId(theTkWindow),
231                              GCForeground | GCFont, &values);
232
233    values.foreground = focusNameColor->pixel;
234    values.font = focusNameFont->fid;
235    focusNameGC = XCreateGC(Tk_Display(theTkWindow), Tk_WindowId(theTkWindow),
236                            GCForeground | GCFont, &values);
237
238    values.foreground = cellColor->pixel;
239    values.font = cellFont->fid;
240    cellGC = XCreateGC(Tk_Display(theTkWindow), Tk_WindowId(theTkWindow),
241                       GCForeground | GCFont, &values);
242
243    return true;
244 }
245
246 void tableVisi::resizeScrollbars(Tcl_Interp *interp) {
247    // used to be a tcl routine (resize1Scrollbar):
248
249    const int visible_cell_width = Tk_Width(theTkWindow) -
250                                   getFocusAreaPixWidth();
251    resizeScrollbar(interp, ".horizScrollbar",
252                    all_cells_width, // total
253                    visible_cell_width);
254
255    const int visible_cell_height = Tk_Height(theTkWindow) -
256                                    getMetricAreaPixHeight();
257    resizeScrollbar(interp, ".vertScrollbar",
258                    all_cells_height, // total
259                    visible_cell_height);
260 }
261
262 bool tableVisi::adjustHorizSBOffset(Tcl_Interp *interp) {
263    float first, last;
264    getScrollBarValues(interp, ".horizScrollbar", first, last);
265    return adjustHorizSBOffset(interp, first);
266 }
267
268 bool tableVisi::adjustVertSBOffset(Tcl_Interp *interp) {
269    float first, last;
270    getScrollBarValues(interp, ".vertScrollbar", first, last);
271    return adjustVertSBOffset(interp, first);
272 }
273
274 void tableVisi::resize(Tcl_Interp *interp) {
275    // does not resize.  Does things like resize the offscreen pixmap
276    if (tryFirst()) {
277       if (offscreenPixmap) {
278          XFreePixmap(Tk_Display(theTkWindow), offscreenPixmap);
279
280          offscreenPixmap = XCreatePixmap(Tk_Display(theTkWindow),
281                                          Tk_WindowId(theTkWindow),
282                                          Tk_Width(theTkWindow), Tk_Height(theTkWindow),
283                                          Tk_Depth(theTkWindow));
284       }
285    }
286
287    resizeScrollbars(interp);
288    adjustHorizSBOffset(interp);
289    adjustVertSBOffset(interp);
290 }
291
292 void tableVisi::draw(bool xsynch) const {
293    if (!offscreenPixmap)
294       return; // we haven't done a tryFirst() yet
295
296    bool doubleBuffer = !xsynch;
297
298    Drawable theDrawable = doubleBuffer ? offscreenPixmap : Tk_WindowId(theTkWindow);
299
300    // XClearArea() works only on windows; the following works on pixmaps, too:
301    XFillRectangle(Tk_Display(theTkWindow), theDrawable,
302                   backgroundGC,
303                   0, 0, // x, y offset
304                   Tk_Width(theTkWindow), Tk_Height(theTkWindow));
305
306    drawFocusNames(theDrawable);
307       // leftmost part of screen; unaffected by offset_x
308    drawMetricNames(theDrawable);
309       // topmost part of screen; unaffected byy offset_y
310    drawCells(theDrawable);
311
312    if (doubleBuffer)
313       XCopyArea(Tk_Display(theTkWindow),
314                 offscreenPixmap, // src drawable
315                 Tk_WindowId(theTkWindow), // dest drawable
316                 backgroundGC, // only a dummy GC is needed here (well, sort of)
317                 0, 0, // src x, y offsets
318                 Tk_Width(theTkWindow), Tk_Height(theTkWindow),
319                 0, 0 // dest x, y offsets
320                 );
321 }
322
323 /*
324  * private metric helper functions
325  *
326  */
327 void tableVisi::drawMetricNames(Drawable theDrawable) const {
328    int curr_x = offset_x + getFocusAreaPixWidth();
329
330    const int minVisibleX = getFocusAreaPixWidth();
331    const int maxVisibleX = Tk_Width(theTkWindow) - 1;
332
333    const int metric_name_baseline = getMetricNameBaseline();
334    const int metric_units_baseline = getMetricUnitsBaseline();
335
336    // we need to clip 2 GC's: metricNameGC and metricUnitsGC.
337    // we don't need to clip lineGC, since our manual clipping is effective
338    XRectangle clipRect;
339    clipRect.x = getFocusAreaPixWidth();
340    clipRect.y = 0;
341    clipRect.width = Tk_Width(theTkWindow) - clipRect.x + 1;
342    clipRect.height = Tk_Height(theTkWindow);
343
344    XSetClipRectangles(Tk_Display(theTkWindow), metricNameGC,
345                       0, 0, &clipRect, 1, YXBanded);
346    XSetClipRectangles(Tk_Display(theTkWindow), metricUnitsGC,
347                       0, 0, &clipRect, 1, YXBanded);
348
349    for (unsigned metriclcv=0; metriclcv < indirectMetrics.size(); metriclcv++) {
350       if (curr_x > maxVisibleX)
351          break; // everthing else will be too far right
352
353       const tvMetric &theMetric = metrics[indirectMetrics[metriclcv]];
354       const int next_x = curr_x + theMetric.getColPixWidth();
355
356       if (next_x - 1 < minVisibleX) {
357          curr_x = next_x;
358          continue;
359       }
360
361       if (curr_x >= minVisibleX) // clipping
362          drawMetricVertLine(theDrawable, curr_x);
363
364       int curr_middle_x = (curr_x + next_x - 1) / 2;
365
366       // draw the metric name:
367       int metric_name_left = curr_middle_x - theMetric.getNamePixWidth() / 2;
368       const string &metricNameStr = theMetric.getName();
369       XDrawString(Tk_Display(theTkWindow), theDrawable,
370                   metricNameGC,
371                   metric_name_left, metric_name_baseline,
372                   metricNameStr.string_of(), metricNameStr.length());
373
374       // draw the metric units:
375       int metric_units_left = curr_middle_x - theMetric.getUnitsPixWidth() / 2;
376       const string &metricUnitsNameStr = theMetric.getUnitsName();
377       XDrawString(Tk_Display(theTkWindow), theDrawable,
378                   metricUnitsGC,
379                   metric_units_left, metric_units_baseline,
380                   metricUnitsNameStr.string_of(), metricUnitsNameStr.length());
381
382       curr_x = next_x;
383    }
384
385    if (curr_x >= minVisibleX) // clipping
386       drawMetricVertLine(theDrawable, curr_x);
387
388    XSetClipMask(Tk_Display(theTkWindow), metricNameGC, None);
389    XSetClipMask(Tk_Display(theTkWindow), metricUnitsGC, None);
390 }
391
392 void tableVisi::drawMetricVertLine(Drawable theDrawable, int x) const {
393    XDrawLine(Tk_Display(theTkWindow), theDrawable,
394              lineColorGC,
395              x, 0, x, Tk_Height(theTkWindow)-1);
396 }
397
398 unsigned tableVisi::getMetricAreaPixHeight() const {
399    return 3 + metricNameFont->ascent + metricNameFont->descent + 3 +
400               metricUnitsFont->ascent + metricUnitsFont->descent + 3;
401 }
402
403 unsigned tableVisi::getMetricNameBaseline() const {
404    return 3 + metricNameFont->ascent - 1;
405 }
406
407 unsigned tableVisi::getMetricUnitsBaseline() const {
408    return 3 + metricNameFont->ascent + metricNameFont->descent + 3 +
409               metricUnitsFont->ascent - 1;
410 }
411
412 /*
413  * private focus helper functions
414  *
415  */
416 void tableVisi::drawFocusNames(Drawable theDrawable) const {
417    int curr_y = offset_y + getMetricAreaPixHeight();
418
419    const int minVisibleY = getMetricAreaPixHeight();
420    const int maxVisibleY = Tk_Height(theTkWindow) - 1;
421
422    XRectangle clipRect;
423    clipRect.x = 0;
424    clipRect.y = getMetricAreaPixHeight();
425    clipRect.width = Tk_Width(theTkWindow);
426    clipRect.height = Tk_Height(theTkWindow) - clipRect.y + 1;
427
428    XSetClipRectangles(Tk_Display(theTkWindow), focusNameGC,
429                       0, 0, &clipRect, 1, YXBanded);
430    
431    for (unsigned focuslcv = 0; focuslcv < indirectFoci.size(); focuslcv++) {
432       if (curr_y > maxVisibleY)
433          break;
434
435       const tvFocus &theFocus = foci[indirectFoci[focuslcv]];
436       const int next_y = curr_y + getFocusLinePixHeight();
437       if (next_y - 1 < minVisibleY) {
438          curr_y = next_y;
439          continue;
440       }
441
442       if (curr_y >= minVisibleY)
443          drawFocusHorizLine(theDrawable, curr_y);
444
445       int curr_y_baseline = curr_y + getVertPixFocusTop2Baseline();
446
447       const string &theString = focusLongNameMode ? theFocus.getLongName() :
448                                                     theFocus.getShortName();
449
450       XDrawString(Tk_Display(theTkWindow), theDrawable,
451                   focusNameGC,
452                   getHorizPixBeforeFocusName(),
453                   curr_y_baseline,
454                   theString.string_of(), theString.length());
455
456       curr_y = next_y;
457    }
458
459    XSetClipMask(Tk_Display(theTkWindow), focusNameGC, None);
460
461    if (curr_y >= minVisibleY)
462       drawFocusHorizLine(theDrawable, curr_y);
463 }
464
465 void tableVisi::drawFocusHorizLine(Drawable theDrawable, int y) const {
466    XDrawLine(Tk_Display(theTkWindow), theDrawable,
467              lineColorGC,
468              0, y, Tk_Width(theTkWindow)-1, y);
469 }
470
471 unsigned tableVisi::getFocusLinePixHeight() const {
472    return 2 + focusNameFont->ascent + focusNameFont->descent + 2;
473 }
474
475 unsigned tableVisi::getVertPixFocusTop2Baseline() const {
476    return 2 + focusNameFont->ascent;
477 }
478
479 unsigned tableVisi::getFocusAreaPixWidth() const {
480    return getHorizPixBeforeFocusName() + maxFocusNamePixWidth +
481           getHorizPixBeforeFocusName();
482 }
483
484 /*
485  * private cell helper functions
486  *
487  */
488 void tableVisi::drawCells(Drawable theDrawable) const {
489    int curr_x = offset_x + getFocusAreaPixWidth();
490
491    const int minVisibleX = 0;
492    const int maxVisibleX = Tk_Width(theTkWindow)-1;
493
494    // we need to clip the GCs used for drawing cells s.t. neither the
495    // metrics nor foci are overwritten.
496    XRectangle clipRect;
497    clipRect.x = getFocusAreaPixWidth();
498    clipRect.y = getMetricAreaPixHeight();
499    clipRect.width = Tk_Width(theTkWindow) - clipRect.x + 1;
500    clipRect.height = Tk_Height(theTkWindow) - clipRect.y + 1;
501
502    XSetClipRectangles(Tk_Display(theTkWindow), cellGC,
503                       0, 0, &clipRect, 1, YXBanded);
504    
505    for (unsigned metriclcv = 0; metriclcv < indirectMetrics.size(); metriclcv++) {
506       if (curr_x > maxVisibleX)
507          break;
508
509       const tvMetric &theMetric = metrics[indirectMetrics[metriclcv]];
510       const int next_x = curr_x + theMetric.getColPixWidth();
511
512       if (next_x - 1 < minVisibleX) {
513          curr_x = next_x;
514          continue;
515       }
516
517       const vector<tvCell> &thisMetricCells = cells[indirectMetrics[metriclcv]];
518       drawCells1Col(theDrawable,
519                     (curr_x + next_x - 1) / 2, // middle x
520                     offset_y + getMetricAreaPixHeight(), // start y
521                     thisMetricCells);
522
523       curr_x = next_x;
524    }
525
526    XSetClipMask(Tk_Display(theTkWindow), cellGC, None);
527 }
528
529 void tableVisi::drawCells1Col(Drawable theDrawable, int middle_x, int top_y,
530                               const vector<tvCell> &thisMetricCells) const {
531    // uses getVertPixFocusTop2Baseline() and getFocusLinePixHeight()
532    int curr_y = top_y;
533
534    int minVisibleY = 0;
535    int maxVisibleY = Tk_Height(theTkWindow)-1;
536
537    for (unsigned focuslcv=0; focuslcv < indirectFoci.size(); focuslcv++) {
538       if (curr_y > maxVisibleY)
539          break;
540
541       const int next_y = curr_y + getFocusLinePixHeight();
542       if (next_y - 1 < minVisibleY) {
543          curr_y = next_y;
544          continue;
545       }
546
547       const tvCell &theCell = thisMetricCells[indirectFoci[focuslcv]];
548       if (!theCell.isValid()) {
549          curr_y = next_y;
550          continue;
551       }
552
553       // making a new "string" would be toop expensive (calls new):
554       char buffer[200];
555       double2string(buffer, theCell.getData());
556
557       int buffer_len = strlen(buffer);
558       int string_pix_width = XTextWidth(cellFont, buffer, buffer_len);
559  
560       XDrawString(Tk_Display(theTkWindow), theDrawable,
561                   cellGC,
562                   middle_x - string_pix_width / 2,
563                   curr_y + getVertPixCellTop2Baseline(),
564                   buffer, buffer_len);
565
566       curr_y = next_y;
567    }
568 }
569
570 unsigned tableVisi::getVertPixCellTop2Baseline() const {
571    return 2 + cellFont->ascent;
572 }
573
574 /* *************************************************************** */
575
576 void tableVisi::clearMetrics(Tcl_Interp *interp) {
577    metrics.resize(0);
578    indirectMetrics.resize(0);
579    cells.resize(0);
580    all_cells_width = 0;
581
582    if (offscreenPixmap)
583       resize(interp);
584 }
585
586 void tableVisi::clearFoci(Tcl_Interp *interp) {
587    foci.resize(0);
588    indirectFoci.resize(0);
589
590    unsigned numMetrics = getNumMetrics();
591    for (unsigned i=0; i < numMetrics; i++) {
592       vector<tvCell> &theVec = cells[i];
593       theVec.resize(0);
594    }
595
596    all_cells_height = 0;
597    maxFocusNamePixWidth = 0;
598
599    if (offscreenPixmap)
600       resize(interp);
601 }
602
603 void tableVisi::addMetric(const string &metricName, const string &metricUnits) {
604    tvMetric newTvMetric(metricName, metricUnits, metricNameFont, metricUnitsFont);
605    metrics += newTvMetric;
606    indirectMetrics += (metrics.size()-1);
607    cells += vector<tvCell>();
608
609    all_cells_width += newTvMetric.getColPixWidth();
610
611    assert(metrics.size() == indirectMetrics.size());
612    assert(cells.size() == metrics.size());
613 }
614
615 void tableVisi::addFocus(const string &focusName) {
616    tvFocus newTvFocus(focusName, focusNameFont);
617    foci += newTvFocus;
618    indirectFoci += (foci.size()-1);
619
620    unsigned numMetrics = metrics.size();
621    for (unsigned metriclcv=0; metriclcv < numMetrics; metriclcv++) {
622       vector<tvCell> &metricCells = cells[metriclcv];
623       metricCells += tvCell();
624    }
625
626    if (focusLongNameMode)
627       ipmax(maxFocusNamePixWidth, newTvFocus.getLongNamePixWidth());
628    else
629       ipmax(maxFocusNamePixWidth, newTvFocus.getShortNamePixWidth());
630
631    all_cells_height += getFocusLinePixHeight();
632
633    assert(foci.size() == indirectFoci.size());
634 }
635
636 int tableVisi::partitionMetrics(int left, int right) {
637    const tvMetric &pivot = metrics[indirectMetrics[left]];
638
639    int l = left-1;
640    int r = right+1;
641
642    while (true) {
643       while (metrics[indirectMetrics[--r]] > pivot)
644          ;
645
646       while (metrics[indirectMetrics[++l]] < pivot)
647          ;
648
649       if (l < r) {
650          unsigned temp = indirectMetrics[l];
651          indirectMetrics[l] = indirectMetrics[r];
652          indirectMetrics[r] = temp;
653       }
654       else
655          return r;
656    }
657 }
658
659 void tableVisi::sortMetrics(int left, int right) {
660    if (left < right) {
661       int middle = partitionMetrics(left, right);
662       sortMetrics(left, middle);
663       sortMetrics(middle+1, right);
664    }
665 }
666
667 void tableVisi::sortMetrics() {
668    sortMetrics(0, metrics.size()-1);
669 }
670
671 void tableVisi::unsortMetrics() {
672    for (unsigned i=0; i < indirectMetrics.size(); i++)
673       indirectMetrics[i] = i;
674 }
675
676 int tableVisi::partitionFoci(int left, int right) {
677    const tvFocus &pivot = foci[indirectFoci[left]];
678
679    int l = left-1;
680    int r = right+1;
681
682    while (true) {
683       do {
684          r--;
685       } while (foci[indirectFoci[r]].greater_than(pivot, focusLongNameMode)); 
686
687       do {
688          l++;
689       } while (foci[indirectFoci[l]].less_than(pivot, focusLongNameMode));
690
691       if (l < r) {
692          unsigned temp = indirectFoci[l];
693          indirectFoci[l] = indirectFoci[r];
694          indirectFoci[r] = temp;
695       }
696       else
697          return r;
698    }
699 }
700
701 void tableVisi::sortFoci(int left, int right) {
702    if (left < right) {
703       int middle = partitionFoci(left, right);
704       sortFoci(left, middle);
705       sortFoci(middle+1, right);
706    }
707 }
708
709 void tableVisi::sortFoci() {
710    sortFoci(0, foci.size()-1);
711 }
712
713 void tableVisi::unsortFoci() {
714    for (unsigned i=0; i < indirectFoci.size(); i++)
715       indirectFoci[i] = i;
716 }
717
718 bool tableVisi::setFocusNameMode(Tcl_Interp *interp, bool longNameMode) {
719    // returns true iff any changes
720    if (focusLongNameMode == longNameMode)
721       return false;
722
723    focusLongNameMode = longNameMode;
724
725    // recalculate maxFocusNamePixWidth:
726    maxFocusNamePixWidth=0;
727    for (unsigned focuslcv=0; focuslcv < foci.size(); focuslcv++) {
728       const tvFocus &theFocus = foci[focuslcv];
729
730       if (focusLongNameMode)
731          ipmax(maxFocusNamePixWidth, theFocus.getLongNamePixWidth());
732       else
733          ipmax(maxFocusNamePixWidth, theFocus.getShortNamePixWidth());
734    }
735
736    resize(interp);
737
738    return true;
739 }
740
741 bool tableVisi::setSigFigs(unsigned newNumSigFigs) {
742    if (newNumSigFigs == numSigFigs)
743       return false;
744
745    numSigFigs = newNumSigFigs;
746    return true;
747 }
748
749 void tableVisi::invalidateCell(unsigned theMetric, unsigned theFocus) {
750    cells[theMetric][theFocus].invalidate();
751 }
752
753 void tableVisi::setCellValidData(unsigned theMetric, unsigned theFocus, double data) {
754    cells[theMetric][theFocus].setValidData(data);
755 }
756
757 /* *************************************************************** */
758
759 bool tableVisi::adjustHorizSBOffset(Tcl_Interp *interp, float newFirst) {
760    // doesn't redraw; returns true iff any changes.
761    newFirst = moveScrollBar(interp, ".horizScrollbar", newFirst);
762
763    int total_cell_width = get_total_cell_x_pix();
764    int old_offset_x = offset_x;
765    offset_x = -(int)(newFirst * total_cell_width); // yes, always <= 0
766
767    return (offset_x != old_offset_x);
768 }
769
770 bool tableVisi::adjustVertSBOffset(Tcl_Interp *interp, float newFirst) {
771    // doesn't redraw; returns true iff any changes.
772    newFirst = moveScrollBar(interp, ".vertScrollbar", newFirst);
773
774    int total_cell_height = get_total_cell_y_pix();
775    int old_offset_y = offset_y;
776    offset_y = -(int)(newFirst * total_cell_height); // yes, always <= 0
777
778    return (offset_y != old_offset_y);
779 }