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