5 * $Log: tableVisi.C,v $
6 * Revision 1.4 1995/11/20 20:20:20 tamches
7 * horizontal & vertical grid lines no longer expand past the
10 * Revision 1.3 1995/11/15 01:05:16 tamches
11 * fixed bug which clipped cells incorrectly
13 * Revision 1.2 1995/11/08 21:46:46 tamches
16 * Revision 1.1 1995/11/04 00:45:20 tamches
17 * First version of new table visi
23 #include "tableVisi.h"
25 /* ************************************************************* */
27 extern "C" {bool isnan(double);}
28 void tableVisi::double2string(char *buffer, double val) const {
35 char conversionString[100];
36 sprintf(conversionString, "%%.%dg", numSigFigs);
37 sprintf(buffer, conversionString, val);
39 // cout << "from " << buffer << " to " << flush;
42 if (strlen(buffer)==0)
45 const char *decPtr = strchr(buffer, '.');
47 if (decPtr && (decPtr - buffer <= 3))
48 return; // no commas will be added since there aren't at least 4 integer digits
51 // try for exponential notation
52 decPtr = strchr(buffer, 'e');
55 decPtr = &buffer[strlen(buffer)]; // the '\0'
58 // invariant: decPtr now points to the character AFTER the
59 // last integer digit (i.e., the decimal point), or the '\0'
62 // Now let's walk backwards, looking for places to insert commas
63 char integerPartBuffer[200]; // will include commas
64 char *integerPart = integerPartBuffer;
65 const char *walkPtr = decPtr-1;
67 while (walkPtr >= buffer) {
68 if (copyCount > 0 && copyCount % 3 == 0)
72 *integerPart++ = *walkPtr--;
75 char decimalPart[200];
76 // will incl dec point, if applicable. May be
78 strcpy(decimalPart, decPtr);
80 // note: integerPartBuffer is backwards...we must reverse it
81 char *bufferPtr = buffer;
82 while (--integerPart >= integerPartBuffer)
83 *bufferPtr++ = *integerPart;
84 strcpy(bufferPtr, decimalPart);
86 // cout << buffer << endl;
89 /* ************************************************************* */
91 XFontStruct *tableVisi::myXLoadQueryFont(const string &fontName) const {
92 XFontStruct *result = XLoadQueryFont(Tk_Display(theTkWindow),
93 fontName.string_of());
95 cerr << "could not find font " << fontName << endl;
102 XColor *tableVisi::myTkGetColor(Tcl_Interp *interp, const string &colorName) const {
103 XColor *result = Tk_GetColor(interp, theTkWindow, Tk_GetUid(colorName.string_of()));
104 if (result == NULL) {
105 cerr << "could not allocate color " << colorName << endl;
111 /* ************************************************************* */
113 tableVisi::tableVisi(Tcl_Interp *interp,
115 const string &metricFontName,
116 const string &metricUnitsFontName,
117 const string &focusFontName,
118 const string &cellFontName,
119 const string &iLineColorName,
120 const string &iMetricColorName,
121 const string &iMetricUnitsColorName,
122 const string &iFocusColorName,
123 const string &cellColorName,
124 const string &backgroundColorName,
127 // metrics[], foci[], indirectMetrics[], indirectFoci[], and cells[][]
128 // are all initialized to zero-sized arrays.
130 theTkWindow = iTkWindow;
131 theDisplay = Tk_Display(theTkWindow);
133 offscreenPixmap = (Pixmap)NULL;
134 // sorry, can't XCreatePixmap() until the window becomes mapped.
136 backgroundColor = myTkGetColor(interp, backgroundColorName);
138 offset_x = offset_y = 0;
139 all_cells_width = all_cells_height = 0;
141 metricNameFont = myXLoadQueryFont(metricFontName);
142 metricUnitsFont = myXLoadQueryFont(metricUnitsFontName);
143 focusNameFont = myXLoadQueryFont(focusFontName);
144 cellFont = myXLoadQueryFont(cellFontName);
146 focusLongNameMode = true;
147 numSigFigs = iSigFigs;
148 // dataFormat = Current;
150 maxFocusNamePixWidth = 0;
152 // The GC's, like offscreenPixmap, can't be created until the
153 // window becomes mapped.
154 lineColor = myTkGetColor(interp, iLineColorName);
157 metricNameColor = myTkGetColor(interp, iMetricColorName);
160 metricUnitsColor = myTkGetColor(interp, iMetricUnitsColorName);
161 metricUnitsGC = NULL;
163 focusNameColor = myTkGetColor(interp, iFocusColorName);
166 cellColor = myTkGetColor(interp, cellColorName);
170 tableVisi::~tableVisi() {
171 // arrays metrics[], foci[], indirectMetrics[], indirectFoci[], cells[][] will
172 // delete themselves.
174 Tk_FreeColor(cellColor);
175 Tk_FreeColor(focusNameColor);
176 Tk_FreeColor(metricUnitsColor);
177 Tk_FreeColor(metricNameColor);
178 Tk_FreeColor(lineColor);
179 Tk_FreeColor(backgroundColor);
181 XFreeFont(theDisplay, focusNameFont);
182 XFreeFont(theDisplay, metricUnitsFont);
183 XFreeFont(theDisplay, metricNameFont);
185 if (!offscreenPixmap)
186 // the offscreen pixmap was never allocated(!)...so, we never
187 // got around to mapping the window!
190 XFreeGC(theDisplay, cellGC);
191 XFreeGC(theDisplay, focusNameGC);
192 XFreeGC(theDisplay, metricUnitsGC);
193 XFreeGC(theDisplay, metricNameGC);
194 XFreeGC(theDisplay, lineColorGC);
195 XFreeGC(theDisplay, backgroundGC);
197 XFreePixmap(theDisplay, offscreenPixmap);
200 bool tableVisi::tryFirst() {
201 if (offscreenPixmap) {
202 // the offscreen pixmap has been allocated, so the window
203 // has presumably been mapped already
204 assert(Tk_WindowId(theTkWindow) != 0);
208 // the offscreen pixmap hasn't been allocated, so it's now time
209 // to check to see if it should be.
210 if (Tk_WindowId(theTkWindow) == 0)
211 return false; // nuts; not ready yet
213 // Ready to allocate graphical structures now!
214 offscreenPixmap = XCreatePixmap(Tk_Display(theTkWindow),
215 Tk_WindowId(theTkWindow),
216 1, 1, // dummy width, height
217 Tk_Depth(theTkWindow));
219 values.foreground = backgroundColor->pixel;
220 backgroundGC = XCreateGC(Tk_Display(theTkWindow), Tk_WindowId(theTkWindow),
221 GCForeground, &values);
223 values.foreground = lineColor->pixel;
224 lineColorGC = XCreateGC(Tk_Display(theTkWindow), Tk_WindowId(theTkWindow),
225 GCForeground, &values);
227 values.foreground = metricNameColor->pixel;
228 values.font = metricNameFont->fid;
229 metricNameGC = XCreateGC(Tk_Display(theTkWindow), Tk_WindowId(theTkWindow),
230 GCForeground | GCFont, &values);
232 values.foreground = metricUnitsColor->pixel;
233 values.font = metricUnitsFont->fid;
234 metricUnitsGC = XCreateGC(Tk_Display(theTkWindow), Tk_WindowId(theTkWindow),
235 GCForeground | GCFont, &values);
237 values.foreground = focusNameColor->pixel;
238 values.font = focusNameFont->fid;
239 focusNameGC = XCreateGC(Tk_Display(theTkWindow), Tk_WindowId(theTkWindow),
240 GCForeground | GCFont, &values);
242 values.foreground = cellColor->pixel;
243 values.font = cellFont->fid;
244 cellGC = XCreateGC(Tk_Display(theTkWindow), Tk_WindowId(theTkWindow),
245 GCForeground | GCFont, &values);
250 void tableVisi::resizeScrollbars(Tcl_Interp *interp) {
251 // used to be a tcl routine (resize1Scrollbar):
253 const int visible_cell_width = Tk_Width(theTkWindow) -
254 getFocusAreaPixWidth();
255 resizeScrollbar(interp, ".horizScrollbar",
256 all_cells_width, // total
259 const int visible_cell_height = Tk_Height(theTkWindow) -
260 getMetricAreaPixHeight();
261 resizeScrollbar(interp, ".vertScrollbar",
262 all_cells_height, // total
263 visible_cell_height);
266 bool tableVisi::adjustHorizSBOffset(Tcl_Interp *interp) {
268 getScrollBarValues(interp, ".horizScrollbar", first, last);
269 return adjustHorizSBOffset(interp, first);
272 bool tableVisi::adjustVertSBOffset(Tcl_Interp *interp) {
274 getScrollBarValues(interp, ".vertScrollbar", first, last);
275 return adjustVertSBOffset(interp, first);
278 void tableVisi::resize(Tcl_Interp *interp) {
279 // does not resize. Does things like resize the offscreen pixmap
281 if (offscreenPixmap) {
282 XFreePixmap(Tk_Display(theTkWindow), offscreenPixmap);
284 offscreenPixmap = XCreatePixmap(Tk_Display(theTkWindow),
285 Tk_WindowId(theTkWindow),
286 Tk_Width(theTkWindow), Tk_Height(theTkWindow),
287 Tk_Depth(theTkWindow));
291 resizeScrollbars(interp);
292 adjustHorizSBOffset(interp);
293 adjustVertSBOffset(interp);
296 void tableVisi::draw(bool xsynch) const {
297 if (!offscreenPixmap)
298 return; // we haven't done a tryFirst() yet
300 bool doubleBuffer = !xsynch;
302 Drawable theDrawable = doubleBuffer ? offscreenPixmap : Tk_WindowId(theTkWindow);
304 // XClearArea() works only on windows; the following works on pixmaps, too:
305 XFillRectangle(Tk_Display(theTkWindow), theDrawable,
308 Tk_Width(theTkWindow), Tk_Height(theTkWindow));
310 drawFocusNames(theDrawable);
311 // leftmost part of screen; unaffected by offset_x
312 drawMetricNames(theDrawable);
313 // topmost part of screen; unaffected byy offset_y
314 drawCells(theDrawable);
317 XCopyArea(Tk_Display(theTkWindow),
318 offscreenPixmap, // src drawable
319 Tk_WindowId(theTkWindow), // dest drawable
320 backgroundGC, // only a dummy GC is needed here (well, sort of)
321 0, 0, // src x, y offsets
322 Tk_Width(theTkWindow), Tk_Height(theTkWindow),
323 0, 0 // dest x, y offsets
328 * private metric helper functions
331 void tableVisi::drawMetricNames(Drawable theDrawable) const {
332 int curr_x = offset_x + getFocusAreaPixWidth();
334 const int minVisibleX = getFocusAreaPixWidth();
335 const int maxVisibleX = Tk_Width(theTkWindow) - 1;
337 const int metric_name_baseline = getMetricNameBaseline();
338 const int metric_units_baseline = getMetricUnitsBaseline();
340 // we need to clip 2 GC's: metricNameGC and metricUnitsGC.
341 // we don't need to clip lineGC, since our manual clipping is effective
343 clipRect.x = getFocusAreaPixWidth();
345 clipRect.width = Tk_Width(theTkWindow) - clipRect.x + 1;
346 clipRect.height = Tk_Height(theTkWindow);
348 XSetClipRectangles(Tk_Display(theTkWindow), metricNameGC,
349 0, 0, &clipRect, 1, YXBanded);
350 XSetClipRectangles(Tk_Display(theTkWindow), metricUnitsGC,
351 0, 0, &clipRect, 1, YXBanded);
353 for (unsigned metriclcv=0; metriclcv < indirectMetrics.size(); metriclcv++) {
354 if (curr_x > maxVisibleX)
355 break; // everthing else will be too far right
357 const tvMetric &theMetric = metrics[indirectMetrics[metriclcv]];
358 const int next_x = curr_x + theMetric.getColPixWidth();
360 if (next_x - 1 < minVisibleX) {
365 if (curr_x >= minVisibleX) // clipping
366 drawMetricVertLine(theDrawable, curr_x);
368 int curr_middle_x = (curr_x + next_x - 1) / 2;
370 // draw the metric name:
371 int metric_name_left = curr_middle_x - theMetric.getNamePixWidth() / 2;
372 const string &metricNameStr = theMetric.getName();
373 XDrawString(Tk_Display(theTkWindow), theDrawable,
375 metric_name_left, metric_name_baseline,
376 metricNameStr.string_of(), metricNameStr.length());
378 // draw the metric units:
379 int metric_units_left = curr_middle_x - theMetric.getUnitsPixWidth() / 2;
380 const string &metricUnitsNameStr = theMetric.getUnitsName();
381 XDrawString(Tk_Display(theTkWindow), theDrawable,
383 metric_units_left, metric_units_baseline,
384 metricUnitsNameStr.string_of(), metricUnitsNameStr.length());
389 if (curr_x >= minVisibleX) // clipping
390 drawMetricVertLine(theDrawable, curr_x);
392 XSetClipMask(Tk_Display(theTkWindow), metricNameGC, None);
393 XSetClipMask(Tk_Display(theTkWindow), metricUnitsGC, None);
396 void tableVisi::drawMetricVertLine(Drawable theDrawable, int x) const {
397 int line_height = getMetricAreaPixHeight() + get_total_cell_y_pix();
398 ipmin(line_height, Tk_Height(theTkWindow));
400 XDrawLine(Tk_Display(theTkWindow), theDrawable,
402 x, 0, x, 0+line_height-1);
405 unsigned tableVisi::getMetricAreaPixHeight() const {
406 return 3 + metricNameFont->ascent + metricNameFont->descent + 3 +
407 metricUnitsFont->ascent + metricUnitsFont->descent + 3;
410 unsigned tableVisi::getMetricNameBaseline() const {
411 return 3 + metricNameFont->ascent - 1;
414 unsigned tableVisi::getMetricUnitsBaseline() const {
415 return 3 + metricNameFont->ascent + metricNameFont->descent + 3 +
416 metricUnitsFont->ascent - 1;
420 * private focus helper functions
423 void tableVisi::drawFocusNames(Drawable theDrawable) const {
424 int curr_y = offset_y + getMetricAreaPixHeight();
426 const int minVisibleY = getMetricAreaPixHeight();
427 const int maxVisibleY = Tk_Height(theTkWindow) - 1;
431 clipRect.y = getMetricAreaPixHeight();
432 clipRect.width = Tk_Width(theTkWindow);
433 clipRect.height = Tk_Height(theTkWindow) - clipRect.y + 1;
435 XSetClipRectangles(Tk_Display(theTkWindow), focusNameGC,
436 0, 0, &clipRect, 1, YXBanded);
438 for (unsigned focuslcv = 0; focuslcv < indirectFoci.size(); focuslcv++) {
439 if (curr_y > maxVisibleY)
442 const tvFocus &theFocus = foci[indirectFoci[focuslcv]];
443 const int next_y = curr_y + getFocusLinePixHeight();
444 if (next_y - 1 < minVisibleY) {
449 if (curr_y >= minVisibleY)
450 drawFocusHorizLine(theDrawable, curr_y);
452 int curr_y_baseline = curr_y + getVertPixFocusTop2Baseline();
454 const string &theString = focusLongNameMode ? theFocus.getLongName() :
455 theFocus.getShortName();
457 XDrawString(Tk_Display(theTkWindow), theDrawable,
459 getHorizPixBeforeFocusName(),
461 theString.string_of(), theString.length());
466 XSetClipMask(Tk_Display(theTkWindow), focusNameGC, None);
468 if (curr_y >= minVisibleY)
469 drawFocusHorizLine(theDrawable, curr_y);
472 void tableVisi::drawFocusHorizLine(Drawable theDrawable, int y) const {
473 int line_width = getFocusAreaPixWidth() + get_total_cell_x_pix();
474 ipmin(line_width, get_visible_x_pix());
476 XDrawLine(Tk_Display(theTkWindow), theDrawable,
478 0, y, 0+line_width-1, y);
481 unsigned tableVisi::getFocusLinePixHeight() const {
482 return 2 + focusNameFont->ascent + focusNameFont->descent + 2;
485 unsigned tableVisi::getVertPixFocusTop2Baseline() const {
486 return 2 + focusNameFont->ascent;
489 unsigned tableVisi::getFocusAreaPixWidth() const {
490 return getHorizPixBeforeFocusName() + maxFocusNamePixWidth +
491 getHorizPixBeforeFocusName();
495 * private cell helper functions
498 void tableVisi::drawCells(Drawable theDrawable) const {
499 int curr_x = offset_x + getFocusAreaPixWidth();
501 const int minVisibleX = 0;
502 const int maxVisibleX = Tk_Width(theTkWindow)-1;
504 // we need to clip the GCs used for drawing cells s.t. neither the
505 // metrics nor foci are overwritten.
507 clipRect.x = getFocusAreaPixWidth();
508 clipRect.y = getMetricAreaPixHeight();
509 clipRect.width = Tk_Width(theTkWindow) - clipRect.x + 1;
510 clipRect.height = Tk_Height(theTkWindow) - clipRect.y + 1;
512 XSetClipRectangles(Tk_Display(theTkWindow), cellGC,
513 0, 0, &clipRect, 1, YXBanded);
515 for (unsigned metriclcv = 0; metriclcv < indirectMetrics.size(); metriclcv++) {
516 if (curr_x > maxVisibleX)
519 const tvMetric &theMetric = metrics[indirectMetrics[metriclcv]];
520 const int next_x = curr_x + theMetric.getColPixWidth();
522 if (next_x - 1 < minVisibleX) {
527 const vector<tvCell> &thisMetricCells = cells[indirectMetrics[metriclcv]];
528 drawCells1Col(theDrawable,
529 (curr_x + next_x - 1) / 2, // middle x
530 offset_y + getMetricAreaPixHeight(), // start y
536 XSetClipMask(Tk_Display(theTkWindow), cellGC, None);
539 void tableVisi::drawCells1Col(Drawable theDrawable, int middle_x, int top_y,
540 const vector<tvCell> &thisMetricCells) const {
541 // uses getVertPixFocusTop2Baseline() and getFocusLinePixHeight()
545 int maxVisibleY = Tk_Height(theTkWindow)-1;
547 for (unsigned focuslcv=0; focuslcv < indirectFoci.size(); focuslcv++) {
548 if (curr_y > maxVisibleY)
551 const int next_y = curr_y + getFocusLinePixHeight();
552 if (next_y - 1 < minVisibleY) {
557 const tvCell &theCell = thisMetricCells[indirectFoci[focuslcv]];
558 if (!theCell.isValid()) {
563 // making a new "string" would be toop expensive (calls new):
565 double2string(buffer, theCell.getData());
567 int buffer_len = strlen(buffer);
568 int string_pix_width = XTextWidth(cellFont, buffer, buffer_len);
570 XDrawString(Tk_Display(theTkWindow), theDrawable,
572 middle_x - string_pix_width / 2,
573 curr_y + getVertPixCellTop2Baseline(),
580 unsigned tableVisi::getVertPixCellTop2Baseline() const {
581 return 2 + cellFont->ascent;
584 /* *************************************************************** */
586 void tableVisi::clearMetrics(Tcl_Interp *interp) {
588 indirectMetrics.resize(0);
596 void tableVisi::clearFoci(Tcl_Interp *interp) {
598 indirectFoci.resize(0);
600 unsigned numMetrics = getNumMetrics();
601 for (unsigned i=0; i < numMetrics; i++) {
602 vector<tvCell> &theVec = cells[i];
606 all_cells_height = 0;
607 maxFocusNamePixWidth = 0;
613 void tableVisi::addMetric(const string &metricName, const string &metricUnits) {
614 tvMetric newTvMetric(metricName, metricUnits, metricNameFont, metricUnitsFont);
615 metrics += newTvMetric;
616 indirectMetrics += (metrics.size()-1);
617 cells += vector<tvCell>();
619 all_cells_width += newTvMetric.getColPixWidth();
621 assert(metrics.size() == indirectMetrics.size());
622 assert(cells.size() == metrics.size());
625 void tableVisi::addFocus(const string &focusName) {
626 tvFocus newTvFocus(focusName, focusNameFont);
628 indirectFoci += (foci.size()-1);
630 unsigned numMetrics = metrics.size();
631 for (unsigned metriclcv=0; metriclcv < numMetrics; metriclcv++) {
632 vector<tvCell> &metricCells = cells[metriclcv];
633 metricCells += tvCell();
636 if (focusLongNameMode)
637 ipmax(maxFocusNamePixWidth, newTvFocus.getLongNamePixWidth());
639 ipmax(maxFocusNamePixWidth, newTvFocus.getShortNamePixWidth());
641 all_cells_height += getFocusLinePixHeight();
643 assert(foci.size() == indirectFoci.size());
646 int tableVisi::partitionMetrics(int left, int right) {
647 const tvMetric &pivot = metrics[indirectMetrics[left]];
653 while (metrics[indirectMetrics[--r]] > pivot)
656 while (metrics[indirectMetrics[++l]] < pivot)
660 unsigned temp = indirectMetrics[l];
661 indirectMetrics[l] = indirectMetrics[r];
662 indirectMetrics[r] = temp;
669 void tableVisi::sortMetrics(int left, int right) {
671 int middle = partitionMetrics(left, right);
672 sortMetrics(left, middle);
673 sortMetrics(middle+1, right);
677 void tableVisi::sortMetrics() {
678 sortMetrics(0, metrics.size()-1);
681 void tableVisi::unsortMetrics() {
682 for (unsigned i=0; i < indirectMetrics.size(); i++)
683 indirectMetrics[i] = i;
686 int tableVisi::partitionFoci(int left, int right) {
687 const tvFocus &pivot = foci[indirectFoci[left]];
695 } while (foci[indirectFoci[r]].greater_than(pivot, focusLongNameMode));
699 } while (foci[indirectFoci[l]].less_than(pivot, focusLongNameMode));
702 unsigned temp = indirectFoci[l];
703 indirectFoci[l] = indirectFoci[r];
704 indirectFoci[r] = temp;
711 void tableVisi::sortFoci(int left, int right) {
713 int middle = partitionFoci(left, right);
714 sortFoci(left, middle);
715 sortFoci(middle+1, right);
719 void tableVisi::sortFoci() {
720 sortFoci(0, foci.size()-1);
723 void tableVisi::unsortFoci() {
724 for (unsigned i=0; i < indirectFoci.size(); i++)
728 bool tableVisi::setFocusNameMode(Tcl_Interp *interp, bool longNameMode) {
729 // returns true iff any changes
730 if (focusLongNameMode == longNameMode)
733 focusLongNameMode = longNameMode;
735 // recalculate maxFocusNamePixWidth:
736 maxFocusNamePixWidth=0;
737 for (unsigned focuslcv=0; focuslcv < foci.size(); focuslcv++) {
738 const tvFocus &theFocus = foci[focuslcv];
740 if (focusLongNameMode)
741 ipmax(maxFocusNamePixWidth, theFocus.getLongNamePixWidth());
743 ipmax(maxFocusNamePixWidth, theFocus.getShortNamePixWidth());
751 bool tableVisi::setSigFigs(unsigned newNumSigFigs) {
752 if (newNumSigFigs == numSigFigs)
755 numSigFigs = newNumSigFigs;
759 void tableVisi::invalidateCell(unsigned theMetric, unsigned theFocus) {
760 cells[theMetric][theFocus].invalidate();
763 void tableVisi::setCellValidData(unsigned theMetric, unsigned theFocus, double data) {
764 cells[theMetric][theFocus].setValidData(data);
767 /* *************************************************************** */
769 bool tableVisi::adjustHorizSBOffset(Tcl_Interp *interp, float newFirst) {
770 // doesn't redraw; returns true iff any changes.
771 newFirst = moveScrollBar(interp, ".horizScrollbar", newFirst);
773 int total_cell_width = get_total_cell_x_pix();
774 int old_offset_x = offset_x;
775 offset_x = -(int)(newFirst * total_cell_width); // yes, always <= 0
777 return (offset_x != old_offset_x);
780 bool tableVisi::adjustVertSBOffset(Tcl_Interp *interp, float newFirst) {
781 // doesn't redraw; returns true iff any changes.
782 newFirst = moveScrollBar(interp, ".vertScrollbar", newFirst);
784 int total_cell_height = get_total_cell_y_pix();
785 int old_offset_y = offset_y;
786 offset_y = -(int)(newFirst * total_cell_height); // yes, always <= 0
788 return (offset_y != old_offset_y);