/***********************************************************************/ /* Open Visualization Data Explorer */ /* (C) Copyright IBM Corp. 1989,1999 */ /* ALL RIGHTS RESERVED */ /* This code licensed under the */ /* "IBM PUBLIC LICENSE - Open Visualization Data Explorer" */ /***********************************************************************/ #include #include #include #include #include #include #include #include #include // // Is it a good idea to provide a button which will sort up/down the ticks // according to the tick value? Pro: The user doesn't have the ability to // move individual entries around in the list and the AutoAxes module seems // to care about sorted order. Con: The module should be able to deal with // the entries in any order, sort them if it wants to, and echo them back in // sorted order. The ui should just respond to that and not get cluttered up // with sorting code. // // If you use the SORT_TICKS code, then touch TickLabelList.h so that Makefile // does the right thing. // //#define UI_SHOULD_SORT_TICKS 1 #include "TickLabelList.h" #include "TickLabel.h" #include "ListIterator.h" #include "Strings.h" #include "DXApplication.h" // // String TickLabelList::DefaultResources[] = { "*tllMenuB.width: 20", "*tllMenuB.labelString: ...", "*tllMenuB.recomputeSize: False", "*listRC.marginWidth: 1", "*listRC.marginHeight: 1", "*listRC.spacing: 0", "*newTop.labelString: Insert at top", "*deleteAll.labelString: Delete all items", "*aasBut.labelString: Append after selection", "*iasBut.labelString: Insert above selection", "*dsBut.labelString: Delete Selection", "*suBut.labelString: Sort in Ascending Order", "*sdBut.labelString: Sort in Descending Order", NULL }; boolean TickLabelList::SortUp = TRUE; boolean TickLabelList::ClassInitialized = FALSE; #define DIRTY_APPEND 1 #define DIRTY_DELETE 2 TickLabelList::TickLabelList (const char *header, TickListModifyCB tlmcb, void *clientData): UIComponent ("tickList") { this->header = DuplicateString(header); this->dirty = 0; this->header_label = this->header_button = this->listRC = NULL; this->popupMenu = NULL; this->oldDvals = NULL; this->oldString = NULL; this->highest_set_number = 0; this->tlmcb = tlmcb; this->clientData = clientData; } void TickLabelList::initialize() { // // Initialize default resources (once only). // if (NOT TickLabelList::ClassInitialized) { this->setDefaultResources(theApplication->getRootWidget(), TickLabelList::DefaultResources); TickLabelList::ClassInitialized = TRUE; } } TickLabelList::~TickLabelList() { if (this->header) delete this->header; ListIterator it(this->ticks); TickLabel *tl; while (tl = (TickLabel*)it.getNext()) delete tl; this->ticks.clear(); if (this->oldDvals) delete this->oldDvals; if (this->oldString) delete this->oldString; } void TickLabelList::createList (Widget parent) { int n; Arg args[25]; this->initialize(); Widget form = XtVaCreateWidget (this->name, xmFormWidgetClass, parent, XmNshadowThickness, 2, XmNshadowType, XmSHADOW_ETCHED_IN, NULL); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNtopOffset, 50); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftOffset, 2); n++; XtSetArg (args[n], XmNrightOffset, 2); n++; XtSetArg (args[n], XmNbottomOffset, 2); n++; XtSetArg (args[n], XmNheight, 150); n++; XtSetArg (args[n], XmNshadowThickness, 0); n++; XtSetArg (args[n], XmNspacing, 0); n++; XtSetArg (args[n], XmNscrollingPolicy, XmAUTOMATIC); n++; Widget sw = XmCreateScrolledWindow (form, "ticksSW", args, n); XtManageChild (sw); Widget sb = NULL; XtVaGetValues (sw, XmNhorizontalScrollBar, &sb, NULL); if (sb) XtUnmanageChild (sb); n = 0; XtSetArg (args[n], XmNresizeWidth, False); n++; this->listRC = XmCreateRowColumn (sw, "listRC", args, n); XtManageChild (this->listRC); XmScrolledWindowSetAreas (sw, NULL, NULL, this->listRC); // // Title line... // XmString xmstr = XmStringCreate (this->header, "bold"); Widget xlab = XtVaCreateManagedWidget ("listHeader", xmLabelWidgetClass, form, XmNbottomAttachment, XmATTACH_WIDGET, XmNbottomWidget, sw, XmNbottomOffset, 20, XmNleftAttachment, XmATTACH_POSITION, XmNleftPosition, 50, XmNleftOffset, -50, XmNalignment, XmALIGNMENT_CENTER, XmNlabelString, xmstr, NULL); XmStringFree (xmstr); // // Labels over the numbers and the text. // xmstr = XmStringCreate ("Tick Location", "small_bold"); XtVaCreateManagedWidget ("tickTitle", xmLabelWidgetClass, form, XmNbottomAttachment, XmATTACH_WIDGET, XmNbottomWidget, sw, XmNbottomOffset, 1, XmNleftAttachment, XmATTACH_FORM, XmNleftOffset, 4, XmNlabelString, xmstr, NULL); XmStringFree(xmstr); xmstr = XmStringCreate ("Tick Label", "small_bold"); this->labelLabel = XtVaCreateManagedWidget ("textTitle", xmLabelWidgetClass, form, XmNbottomAttachment, XmATTACH_WIDGET, XmNbottomWidget, sw, XmNbottomOffset, 1, XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, 4, XmNlabelString, xmstr, NULL); XmStringFree(xmstr); // // button for list ops... // Widget button = XtVaCreateManagedWidget ("tllMenuB", xmPushButtonWidgetClass,form, XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, XmNbottomWidget, xlab, XmNbottomOffset, 0, XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, xlab, XmNleftOffset, 10, NULL); XtUninstallTranslations (button); XtAddEventHandler (button, ButtonPressMask|ButtonReleaseMask, False, (XtEventHandler)TickLabelList_ButtonEH, (XtPointer)this); n = 0; XtSetArg (args[0], XmNmenuPost, "Shift"); this->popupMenu = XmCreatePopupMenu(form, "tllPopup", args, 1); button = this->ntiButton = XtVaCreateManagedWidget ("newTop", xmPushButtonWidgetClass, this->popupMenu, XmNuserData, 0, NULL); XtAddCallback (button, XmNactivateCallback, (XtCallbackProc)TickLabelList_AppendCB, (XtPointer)this); button = this->daButton = XtVaCreateManagedWidget ("deleteAll", xmPushButtonWidgetClass, this->popupMenu, NULL); XtAddCallback (button, XmNactivateCallback, (XtCallbackProc)TickLabelList_DeleteAllCB, (XtPointer)this); XtVaCreateManagedWidget ("sep", xmSeparatorWidgetClass, this->popupMenu, NULL); button = this->iasButton = XtVaCreateManagedWidget ("iasBut", xmPushButtonWidgetClass, this->popupMenu, NULL); XtAddCallback (button, XmNactivateCallback, (XtCallbackProc)TickLabelList_InsertAboveSelCB, (XtPointer)this); button = this->aasButton = XtVaCreateManagedWidget ("aasBut", xmPushButtonWidgetClass, this->popupMenu, NULL); XtAddCallback (button, XmNactivateCallback, (XtCallbackProc)TickLabelList_AppendAfterSelCB, (XtPointer)this); button = this->dsButton = XtVaCreateManagedWidget ("dsBut", xmPushButtonWidgetClass, this->popupMenu, NULL); XtAddCallback (button, XmNactivateCallback, (XtCallbackProc)TickLabelList_DeleteSelCB, (XtPointer)this); #if UI_SHOULD_SORT_TICKS XtVaCreateManagedWidget ("sep", xmSeparatorWidgetClass, this->popupMenu, NULL); button = this->suButton = XtVaCreateManagedWidget("suBut", xmPushButtonWidgetClass, this->popupMenu, NULL); XtAddCallback (button, XmNactivateCallback, (XtCallbackProc)TickLabelList_SortUpCB, (XtPointer)this); button = this->sdButton = XtVaCreateManagedWidget("sdBut", xmPushButtonWidgetClass, this->popupMenu, NULL); XtAddCallback (button, XmNactivateCallback, (XtCallbackProc)TickLabelList_SortDownCB, (XtPointer)this); #endif // // Create a dummy drawing area in order to catch resize events. This is // necessary for scrolled windows. Their contents' must be manually stretched // horizontally when the window is resized. // n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNtopOffset, 35); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftOffset, 2); n++; XtSetArg (args[n], XmNrightOffset, 2); n++; XtSetArg (args[n], XmNbottomOffset, 2); n++; XtSetArg (args[n], XmNmappedWhenManaged, False); n++; Widget drawa = XmCreateDrawingArea (form, "junk", args, n); XtManageChild (drawa); XtAddCallback (drawa, XmNresizeCallback, (XtCallbackProc)TickLabelList_ResizeTicksCB, (XtPointer)this); this->setRootWidget(form); ListIterator it(this->ticks); TickLabel *tl; while (tl = (TickLabel*)it.getNext()) tl->createLine (this->listRC); } void TickLabelList::createLine (double dval, const char *str, int pos) { TickLabel *tl = new TickLabel (dval, str, pos, (TickSelectCB)TickLabelList::SelectCB, (void*)this); ASSERT (pos); ASSERT (pos <= (this->ticks.getSize() + 1)); this->ticks.insertElement((void *)tl, pos); this->dirty|= DIRTY_APPEND; if (this->listRC) tl->createLine(this->listRC); int newpos = this->ticks.getPosition((void*)tl); ASSERT (newpos == pos); this->highest_set_number = MAX(pos, this->highest_set_number); this->resizeCallback(); } void TickLabelList::SelectCB (TickLabel* tlab, void* clientData) { ASSERT(tlab); if (!tlab->isSelected()) return ; ASSERT(clientData); TickLabelList *tll = (TickLabelList*)clientData; ListIterator it(tll->ticks); TickLabel *nextTL; while (nextTL = (TickLabel*)it.getNext()) { if ((nextTL != tlab) && (nextTL->isSelected())) { nextTL->setSelected (FALSE, FALSE); } } if (tll->tlmcb) tll->tlmcb (tll, tll->clientData); } boolean TickLabelList::isModified() { if (this->dirty) return TRUE; TickLabel *tl; ListIterator it(this->ticks); while (tl = (TickLabel*)it.getNext()) if (tl->isModified()) return TRUE; return FALSE; } boolean TickLabelList::isNumberModified() { if (this->dirty) return TRUE; TickLabel *tl; ListIterator it(this->ticks); while (tl = (TickLabel*)it.getNext()) if (tl->isNumberModified()) return TRUE; return FALSE; } boolean TickLabelList::isTextModified() { if (this->dirty) return TRUE; TickLabel *tl; ListIterator it(this->ticks); while (tl = (TickLabel*)it.getNext()) if (tl->isTextModified()) return TRUE; return FALSE; } // // Caller must not free the memory. // double * TickLabelList::getTickNumbers() { if (!this->ticks.getSize()) return NULL; if (this->oldDvals) { delete this->oldDvals; this->oldDvals = NULL; } ListIterator it(this->ticks); TickLabel *tl; double *dvals = new double[this->ticks.getSize()]; int i = 0; while (tl = (TickLabel*)it.getNext()) dvals[i++] = tl->getNumber(); return (this->oldDvals = dvals); } // // Caller must not free the memory. // String TickLabelList::getTickTextString() { int size = this->ticks.getSize(); if (!size) return NULL; if ((!this->isTextModified())&&(this->oldString)) return this->oldString; if (this->oldString) { delete this->oldString; this->oldString = NULL; } boolean is_any_text_set = FALSE; ListIterator it(this->ticks); TickLabel *tl; int len = 0; const char *cp; while (tl = (TickLabel*)it.getNext()) { cp = tl->getText(); if (!cp) continue; len+= strlen(cp); if ((len!=2)&&(len!=0)) is_any_text_set = TRUE; else if ((len == 2) && (strcmp(cp, "\"\""))) is_any_text_set = TRUE; } if (!is_any_text_set) { return NULL; } int newSize = len+10 + (5*size); char *buf = this->oldString = new char[newSize]; strcpy (buf, "{ "); int bufLen = strlen(buf); boolean first_loop = TRUE; boolean add_quotes; it.setList(this->ticks); while (tl = (TickLabel*)it.getNext()) { if (!first_loop) { strcpy (&buf[bufLen], ", "); bufLen+= 2; } cp = tl->getText(); len = strlen(cp); add_quotes = TRUE; if ((cp[0] == '"') && (cp[len-1] == '"')) add_quotes = FALSE; if (add_quotes) buf[bufLen++] = '"'; strcpy (&buf[bufLen], cp); bufLen+= len; if (add_quotes) buf[bufLen++] = '"'; first_loop = FALSE; } ASSERT (newSize > bufLen); strcpy (&buf[bufLen], " }"); bufLen+= 2; return this->oldString; } void TickLabelList::markClean() { this->dirty = 0; TickLabel *tl; ListIterator it(this->ticks); while (tl = (TickLabel*)it.getNext()) { tl->setText(); tl->setNumber(); } } void TickLabelList::markNumbersClean() { TickLabel *tl; ListIterator it(this->ticks); while (tl = (TickLabel*)it.getNext()) { tl->setNumber(); } } void TickLabelList::markTextClean() { TickLabel *tl; ListIterator it(this->ticks); while (tl = (TickLabel*)it.getNext()) { tl->setText(); } } void TickLabelList::clear() { ListIterator it(this->ticks); TickLabel *tl; while (tl = (TickLabel*)it.getNext()) delete tl; this->ticks.clear(); ASSERT (this->ticks.getSize() == 0); this->dirty = 0; } void TickLabelList::setNumber (int pos, double dval) { int size = this->ticks.getSize(); if (pos > size) this->setListSize (pos); TickLabel *tl = (TickLabel*)this->ticks.getElement(pos); ASSERT(tl); tl->setNumber(dval); this->highest_set_number = MAX(pos, this->highest_set_number); } void TickLabelList::setText (int pos, char *str) { int size = this->ticks.getSize(); if (pos > size) this->setListSize (pos); TickLabel *tl = (TickLabel*)this->ticks.getElement(pos); ASSERT(tl); tl->setText(str); } void TickLabelList::setListSize (int new_size) { int i, current_size = this->ticks.getSize(); TickLabel *tl; if (new_size == current_size) { } else if (new_size > current_size) { for (i=current_size+1; i<=new_size; i++) this->createLine (0.0, "", i); } else { List tmpList; for (i=new_size+1; i<=current_size; i++) { tl = (TickLabel*)this->ticks.getElement(i); ASSERT(tl); tmpList.appendElement((void*)tl); } ListIterator it(tmpList); while (tl = (TickLabel*)it.getNext()) { this->ticks.removeElement((void*)tl); delete tl; } } } void TickLabelList::resizeCallback () { Dimension width, height, rch; Widget sw = this->listRC; while ((sw) && (XtClass(sw)!=xmScrolledWindowWidgetClass)) sw = XtParent(sw); XtVaGetValues (sw, XmNwidth, &width, XmNheight, &height, NULL); XtVaGetValues (this->listRC, XmNheight, &rch, NULL); boolean scrollbar_in_use; if (rch > height) scrollbar_in_use = TRUE; else scrollbar_in_use = FALSE; if (scrollbar_in_use) { XtVaSetValues (this->listRC, XmNwidth, width-20, NULL); XtVaSetValues (this->labelLabel, XmNrightOffset, 26, NULL); } else { XtVaSetValues (this->listRC, XmNwidth, width-2, NULL); XtVaSetValues (this->labelLabel, XmNrightOffset, 8, NULL); } } // // Using the list of TickLabel objects, create an array. Sort the array. // Then clear the original list and rebuild it using the contents of the array. // void TickLabelList::sortList (boolean up) { #if UI_SHOULD_SORT_TICKS int size = this->ticks.getSize(); TickLabelList::SortUp = up; ASSERT (size > 1); int i; TickLabel **tla = new TickLabel*[size]; for (i=1; i<=size; i++) tla[i-1] = (TickLabel*)this->ticks.getElement(i); qsort (tla, size, sizeof(TickLabel*), TickLabelList_SortFunc); this->ticks.clear(); XtUnmanageChild (this->listRC); for (i=0; iticks.appendElement((void*)tla[i]); tla[i]->destroyLine(); } for (i=0; isetPosition(i+1); tla[i]->createLine(this->listRC); } XtManageChild (this->listRC); this->dirty|= DIRTY_APPEND; delete tla; #endif } extern "C" { int TickLabelList_SortFunc (const void* e1, const void* e2) { #if UI_SHOULD_SORT_TICKS TickLabel** tlp1 = (TickLabel**)e1; TickLabel** tlp2 = (TickLabel**)e2; TickLabel* tl1 = *tlp1; TickLabel* tl2 = *tlp2; double dval1 = tl1->getNumber(); double dval2 = tl2->getNumber(); if (TickLabelList::SortUp) { if (dval1 > dval2) return 1; if (dval1 < dval2) return -1; } else { if (dval1 < dval2) return 1; if (dval1 > dval2) return -1; } ASSERT (dval1 == dval2); #endif return 0; } void TickLabelList_ButtonEH(Widget , XtPointer clientData, XEvent *e, Boolean *) { TickLabelList *tlab = (TickLabelList *)clientData; ASSERT(tlab); XmMenuPosition(tlab->popupMenu , (XButtonPressedEvent *)e); if (e->type == ButtonRelease) XtManageChild(tlab->popupMenu); else XtAppAddTimeOut (theApplication->getApplicationContext(), 100, (XtTimerCallbackProc)XtManageChild, (XtPointer)tlab->popupMenu); // // Enable/Disable certain items based on list selection. // TickLabel *tl; ListIterator it(tlab->ticks); boolean any_selected = FALSE; while ((!any_selected) && (tl = (TickLabel*)it.getNext())) if (tl->isSelected()) any_selected = TRUE; XtSetSensitive (tlab->aasButton, (Boolean)any_selected); XtSetSensitive (tlab->iasButton, (Boolean)any_selected); XtSetSensitive (tlab->dsButton, (Boolean)any_selected); XtSetSensitive (tlab->daButton, (Boolean)(tlab->ticks.getSize() != 0)); #if UI_SHOULD_SORT_TICKS XtSetSensitive (tlab->suButton, (Boolean)(tlab->ticks.getSize() > 1)); XtSetSensitive (tlab->sdButton, (Boolean)(tlab->ticks.getSize() > 1)); #endif } // // Horizontally resize the contents of the 3 scrolled windows in TicksLabels. // void TickLabelList_ResizeTicksCB(Widget , XtPointer clientData, XtPointer) { ASSERT(clientData); TickLabelList *tll = (TickLabelList *)clientData; tll->resizeCallback(); } void TickLabelList_DeleteAllCB(Widget , XtPointer clientData, XtPointer) { ASSERT(clientData); TickLabelList *tll = (TickLabelList *)clientData; tll->clear(); tll->dirty|= DIRTY_DELETE; tll->highest_set_number = 0; tll->resizeCallback(); if (tll->tlmcb) tll->tlmcb (tll, tll->clientData); } void TickLabelList_AppendCB(Widget w, XtPointer clientData, XtPointer) { ASSERT(clientData); TickLabelList *tll = (TickLabelList *)clientData; // // Inovke the callback only if adding the first iterm. // boolean call_callback = FALSE; int size = tll->ticks.getSize(); if (size == 0) call_callback = TRUE; int newTail; XtVaGetValues (w, XmNuserData, &newTail, NULL); int newPos; if (newTail) newPos = 1 + size; else newPos = 1; tll->createLine (0.0, "", newPos); if ((tll->tlmcb) && (call_callback)) tll->tlmcb (tll, tll->clientData); } void TickLabelList_AppendAfterSelCB(Widget , XtPointer clientData, XtPointer) { int newPos; ASSERT(clientData); TickLabelList *tll = (TickLabelList *)clientData; TickLabel *tl, *sel; ListIterator it(tll->ticks); sel = NULL; newPos = 1; while (tl = (TickLabel*)it.getNext()) { if (tl->isSelected()) { sel = tl; break; } newPos++; } ASSERT(sel); tll->createLine (0.0, "", newPos+1); } void TickLabelList_InsertAboveSelCB(Widget , XtPointer clientData, XtPointer) { int newPos; ASSERT(clientData); TickLabelList *tll = (TickLabelList *)clientData; TickLabel *tl, *sel; ListIterator it(tll->ticks); sel = NULL; newPos = 1; while (tl = (TickLabel*)it.getNext()) { if (tl->isSelected()) { sel = tl; break; } newPos++; } ASSERT(sel); tll->createLine (0.0, "", newPos); } void TickLabelList_DeleteSelCB(Widget , XtPointer clientData, XtPointer) { ASSERT(clientData); TickLabelList *tll = (TickLabelList *)clientData; TickLabel *tl, *sel; ListIterator it(tll->ticks); sel = NULL; while (tl = (TickLabel*)it.getNext()) { if (tl->isSelected()) { sel = tl; break; } } ASSERT(sel); tll->ticks.removeElement((void*)sel); delete sel; tll->resizeCallback(); } void TickLabelList_SortUpCB(Widget , XtPointer clientData, XtPointer) { ASSERT(clientData); TickLabelList *tll = (TickLabelList *)clientData; tll->sortList(TRUE); } void TickLabelList_SortDownCB(Widget , XtPointer clientData, XtPointer) { ASSERT(clientData); TickLabelList *tll = (TickLabelList *)clientData; tll->sortList(FALSE); } } // extern "C"