Import Tix 8.4.3.5 (as of svn r86089)
This commit is contained in:
466
generic/tixGrSort.c
Normal file
466
generic/tixGrSort.c
Normal file
@@ -0,0 +1,466 @@
|
||||
|
||||
/* $Id: tixGrSort.c,v 1.3 2008/02/28 04:05:29 hobbs Exp $ */
|
||||
|
||||
/*
|
||||
* tixGrSel.c --
|
||||
*
|
||||
* This module handles the sorting of the Grid widget.
|
||||
*
|
||||
* Copyright (c) 1996, Expert Interface Technologies
|
||||
*
|
||||
* See the file "license.terms" for information on usage and redistribution
|
||||
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <tixPort.h>
|
||||
#include <tixInt.h>
|
||||
#include <tixGrid.h>
|
||||
|
||||
/*
|
||||
* The variables below are used to implement the "lsort" command.
|
||||
* Unfortunately, this use of static variables prevents "lsort"
|
||||
* from being thread-safe, but there's no alternative given the
|
||||
* current implementation of qsort. In a threaded environment
|
||||
* these variables should be made thread-local if possible, or else
|
||||
* "lsort" needs internal mutual exclusion.
|
||||
*/
|
||||
|
||||
static Tcl_Interp *sortInterp = NULL; /* Interpreter for "lsort" command.
|
||||
* NULL means no lsort is active. */
|
||||
static enum {ASCII, INTEGER, REAL, COMMAND} sortMode;
|
||||
/* Mode for sorting:compare as strings,
|
||||
* compare as numbers, or call
|
||||
* user-defined command for
|
||||
* comparison. */
|
||||
static Tcl_DString sortCmd; /* Holds command if mode is COMMAND.
|
||||
* pre-initialized to hold base of
|
||||
* command. */
|
||||
static int sortIncreasing; /* 0 means sort in decreasing order,
|
||||
* 1 means increasing order. */
|
||||
static int sortCode; /* Anything other than TCL_OK means a
|
||||
* problem occurred while sorting; this
|
||||
* executing a comparison command, so
|
||||
* the sort was aborted. */
|
||||
|
||||
/*
|
||||
* Forward declarations for procedures defined in this file:
|
||||
*/
|
||||
|
||||
EXTERN TIX_DECLARE_SUBCMD(Tix_GrSort);
|
||||
|
||||
static int SortCompareProc _ANSI_ARGS_((CONST VOID *first,
|
||||
CONST VOID *second));
|
||||
char * Tix_GrGetCellText _ANSI_ARGS_((WidgetPtr wPtr,
|
||||
int x, int y));
|
||||
Tix_GrSortItem * Tix_GrGetSortItems _ANSI_ARGS_((WidgetPtr wPtr,
|
||||
int axis, int start, int end, int sortKeyIndex));
|
||||
void Tix_GrFreeSortItems _ANSI_ARGS_((WidgetPtr wPtr,
|
||||
Tix_GrSortItem * items, int numItems));
|
||||
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------
|
||||
*
|
||||
* Tcl_LsortCmd --
|
||||
*
|
||||
* This procedure is invoked to process the "lsort" Tcl command.
|
||||
* See the user documentation for details on what it does.
|
||||
*
|
||||
* Results:
|
||||
* A standard Tcl result.
|
||||
*
|
||||
* Side effects:
|
||||
* See the user documentation.
|
||||
*
|
||||
*----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/* ARGSUSED */
|
||||
|
||||
char *
|
||||
Tix_GrGetCellText(wPtr, x, y)
|
||||
WidgetPtr wPtr;
|
||||
int x;
|
||||
int y;
|
||||
{
|
||||
TixGrEntry* chPtr;
|
||||
|
||||
if ((chPtr = (TixGrEntry*) TixGridDataFindEntry(wPtr->dataSet, x, y))) {
|
||||
switch (Tix_DItemType(chPtr->iPtr)) {
|
||||
case TIX_DITEM_TEXT:
|
||||
return chPtr->iPtr->text.text;
|
||||
case TIX_DITEM_IMAGETEXT:
|
||||
return chPtr->iPtr->imagetext.text;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
Tix_GrSortItem *
|
||||
Tix_GrGetSortItems(wPtr, axis, start, end, sortKeyIndex)
|
||||
WidgetPtr wPtr;
|
||||
int axis;
|
||||
int start;
|
||||
int end;
|
||||
int sortKeyIndex;
|
||||
{
|
||||
int i, k, numItems;
|
||||
Tix_GrSortItem *items;
|
||||
|
||||
if (end <= start) {
|
||||
/* sanity check: no need to sort */
|
||||
return (Tix_GrSortItem *) NULL;
|
||||
}
|
||||
|
||||
numItems = end-start+1;
|
||||
items = (Tix_GrSortItem *)ckalloc(sizeof(Tix_GrSortItem) * numItems);
|
||||
|
||||
for (k=0,i=start; i<=end; i++, k++) {
|
||||
items[k].index = i;
|
||||
if (axis == 0) {
|
||||
items[k].data = Tix_GrGetCellText(wPtr, i, sortKeyIndex);
|
||||
} else {
|
||||
items[k].data = Tix_GrGetCellText(wPtr, sortKeyIndex, i);
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Tix_GrFreeSortItems(wPtr, items, numItems)
|
||||
WidgetPtr wPtr;
|
||||
Tix_GrSortItem * items;
|
||||
int numItems;
|
||||
{
|
||||
ckfree((char*)items);
|
||||
}
|
||||
|
||||
int
|
||||
Tix_GrSort(clientData, interp, argc, argv)
|
||||
ClientData clientData;
|
||||
Tcl_Interp *interp; /* Current interpreter. */
|
||||
int argc; /* Number of arguments. */
|
||||
CONST84 char **argv; /* Argument strings. */
|
||||
{
|
||||
WidgetPtr wPtr = (WidgetPtr) clientData;
|
||||
int i, axis, otherAxis, start, end;
|
||||
size_t len;
|
||||
Tix_GrSortItem *items = NULL;
|
||||
int numItems;
|
||||
CONST84 char *command = NULL; /* Initialization needed only to
|
||||
* prevent compiler warning. */
|
||||
int sortKeyIndex;
|
||||
int gridSize[2];
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
* Argument parsing
|
||||
*-------------------------------------------------------------------
|
||||
*/
|
||||
if (sortInterp != NULL) {
|
||||
Tcl_SetResult(interp,
|
||||
"can't invoke the tixGrid sort command recursively",
|
||||
TCL_STATIC);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
/* Figure out the sorting dimension
|
||||
*/
|
||||
len = strlen(argv[0]);
|
||||
if (strncmp(argv[0], "rows", len)==0) {
|
||||
axis = 1;
|
||||
otherAxis = 0;
|
||||
} else if (strncmp(argv[0], "column", len)==0) {
|
||||
axis = 0;
|
||||
otherAxis = 1;
|
||||
} else {
|
||||
Tcl_AppendResult(interp, "wrong dimension \"", argv[0],
|
||||
"\", should be row or column", (char *) NULL);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
/* get the start and end index
|
||||
*/
|
||||
if (axis == 0) {
|
||||
if (TixGridDataGetIndex(interp, wPtr, argv[1], NULL, &start, NULL)
|
||||
!=TCL_OK) {
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if (TixGridDataGetIndex(interp, wPtr, argv[2], NULL, &end, NULL)
|
||||
!=TCL_OK) {
|
||||
return TCL_ERROR;
|
||||
}
|
||||
} else {
|
||||
if (TixGridDataGetIndex(interp, wPtr, NULL, argv[1], NULL, &start)
|
||||
!=TCL_OK) {
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if (TixGridDataGetIndex(interp, wPtr, NULL, argv[2], NULL, &end)
|
||||
!=TCL_OK) {
|
||||
return TCL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check the range
|
||||
*/
|
||||
TixGridDataGetGridSize(wPtr->dataSet, &gridSize[0], &gridSize[1]);
|
||||
if (start > end) {
|
||||
int tmp = start;
|
||||
start = end;
|
||||
end = tmp;
|
||||
}
|
||||
if (start >= gridSize[axis]) {
|
||||
/* no need to sort */
|
||||
return TCL_OK;
|
||||
}
|
||||
if (end == start) {
|
||||
/* no need to sort */
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/* get the options
|
||||
*/
|
||||
if ((argc-3) %2 != 0) {
|
||||
Tcl_AppendResult(interp, "value for \"", argv[argc-1],
|
||||
"\" missing", NULL);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
sortInterp = interp;
|
||||
sortMode = ASCII;
|
||||
sortIncreasing = 1;
|
||||
sortCode = TCL_OK;
|
||||
sortKeyIndex = wPtr->hdrSize[otherAxis]; /* by default, use the first
|
||||
* scrollable item as the key
|
||||
*/
|
||||
for (i=3; i<argc; i+=2) {
|
||||
len = strlen(argv[i]);
|
||||
if (strncmp(argv[i], "-type", len) == 0) {
|
||||
if (strcmp(argv[i+1], "ascii") == 0) {
|
||||
sortMode = ASCII;
|
||||
} else if (strcmp(argv[i+1], "integer") == 0) {
|
||||
sortMode = INTEGER;
|
||||
} else if (strcmp(argv[i+1], "real") == 0) {
|
||||
sortMode = REAL;
|
||||
} else {
|
||||
Tcl_AppendResult(interp, "wrong type \"", argv[i+1],
|
||||
"\": must be ascii, integer or real", (char *) NULL);
|
||||
sortCode = TCL_ERROR;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
else if (strncmp(argv[i], "-order", len) == 0) {
|
||||
if (strcmp(argv[i+1], "increasing") == 0) {
|
||||
sortIncreasing = 1;
|
||||
} else if (strcmp(argv[i+1], "decreasing") == 0) {
|
||||
sortIncreasing = 0;
|
||||
} else {
|
||||
Tcl_AppendResult(interp, "wrong order \"", argv[i+1],
|
||||
"\": must be increasing or decreasing", (char *) NULL);
|
||||
sortCode = TCL_ERROR;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
else if (strncmp(argv[i], "-key", len) == 0) {
|
||||
if (axis == 0) {
|
||||
/* sort columns: the key is a column index (1) */
|
||||
if (TixGridDataGetIndex(interp, wPtr, NULL, argv[i+1], NULL,
|
||||
&sortKeyIndex) !=TCL_OK) {
|
||||
sortCode = TCL_ERROR;
|
||||
goto done;
|
||||
}
|
||||
} else {
|
||||
/* sort rows: the key is a row index (0)*/
|
||||
if (TixGridDataGetIndex(interp, wPtr, argv[i+1], NULL,
|
||||
&sortKeyIndex, NULL) !=TCL_OK) {
|
||||
sortCode = TCL_ERROR;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (strncmp(argv[i], "-command", len) == 0) {
|
||||
sortMode = COMMAND;
|
||||
command = argv[i+1];
|
||||
}
|
||||
else {
|
||||
Tcl_AppendResult(interp, "wrong option \"", argv[i],
|
||||
"\": must be -command, -key, -order or -type", (char *) NULL);
|
||||
sortCode = TCL_ERROR;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
if (sortMode == COMMAND) {
|
||||
Tcl_DStringInit(&sortCmd);
|
||||
Tcl_DStringAppend(&sortCmd, command, -1);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
* SORTING
|
||||
*------------------------------------------------------------------
|
||||
*/
|
||||
/* prepare the array to be sorted */
|
||||
numItems = end - start + 1;
|
||||
items = Tix_GrGetSortItems(wPtr, axis, start, end, sortKeyIndex);
|
||||
|
||||
if (items != NULL) {
|
||||
int sizeChanged;
|
||||
|
||||
qsort((VOID *)items, (size_t)numItems, sizeof(Tix_GrSortItem),
|
||||
SortCompareProc);
|
||||
|
||||
for (i=0; i<numItems; i++) {
|
||||
printf("%d\n", items[i].index);
|
||||
}
|
||||
sizeChanged = TixGridDataUpdateSort(wPtr->dataSet, axis, start, end,
|
||||
items);
|
||||
if (sizeChanged) {
|
||||
Tix_GrDoWhenIdle(wPtr, TIX_GR_RESIZE);
|
||||
} else {
|
||||
wPtr->toResetRB = 1;
|
||||
Tix_GrDoWhenIdle(wPtr, TIX_GR_REDRAW);
|
||||
}
|
||||
|
||||
Tix_GrFreeSortItems(wPtr, items, numItems);
|
||||
}
|
||||
|
||||
/* Done */
|
||||
if (sortCode == TCL_OK) {
|
||||
Tcl_ResetResult(interp);
|
||||
}
|
||||
if (sortMode == COMMAND) {
|
||||
Tcl_DStringFree(&sortCmd);
|
||||
}
|
||||
|
||||
done:
|
||||
sortInterp = NULL;
|
||||
return sortCode;
|
||||
}
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------
|
||||
*
|
||||
* SortCompareProc --
|
||||
*
|
||||
* This procedure is invoked by qsort to determine the proper
|
||||
* ordering between two elements.
|
||||
*
|
||||
* Results:
|
||||
* < 0 means first is "smaller" than "second", > 0 means "first"
|
||||
* is larger than "second", and 0 means they should be treated
|
||||
* as equal.
|
||||
*
|
||||
* Side effects:
|
||||
* None, unless a user-defined comparison command does something
|
||||
* weird.
|
||||
*
|
||||
*----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static int
|
||||
SortCompareProc(first, second)
|
||||
CONST VOID *first, *second; /* Elements to be compared. */
|
||||
{
|
||||
int order;
|
||||
char *firstString = ((Tix_GrSortItem*)first )->data;
|
||||
char *secondString = ((Tix_GrSortItem*)second)->data;
|
||||
|
||||
order = 0;
|
||||
if (sortCode != TCL_OK) {
|
||||
/*
|
||||
* Once an error has occurred, skip any future comparisons
|
||||
* so as to preserve the error message in sortInterp->result.
|
||||
*/
|
||||
|
||||
return order;
|
||||
}
|
||||
if (firstString == NULL && secondString == NULL) {
|
||||
/* equal */
|
||||
return order;
|
||||
}
|
||||
if (secondString == NULL) {
|
||||
/* first larger than second */
|
||||
order = 1;
|
||||
goto done;
|
||||
}
|
||||
if (firstString == NULL) {
|
||||
order = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (sortMode == ASCII) {
|
||||
order = strcmp(firstString, secondString);
|
||||
} else if (sortMode == INTEGER) {
|
||||
int a, b;
|
||||
|
||||
if ((Tcl_GetInt(sortInterp, firstString, &a) != TCL_OK)
|
||||
|| (Tcl_GetInt(sortInterp, secondString, &b) != TCL_OK)) {
|
||||
Tcl_AddErrorInfo(sortInterp,
|
||||
"\n (converting list element from string to integer)");
|
||||
sortCode = TCL_ERROR;
|
||||
return order;
|
||||
}
|
||||
if (a > b) {
|
||||
order = 1;
|
||||
} else if (b > a) {
|
||||
order = -1;
|
||||
}
|
||||
} else if (sortMode == REAL) {
|
||||
double a, b;
|
||||
|
||||
if ((Tcl_GetDouble(sortInterp, firstString, &a) != TCL_OK)
|
||||
|| (Tcl_GetDouble(sortInterp, secondString, &b) != TCL_OK)) {
|
||||
Tcl_AddErrorInfo(sortInterp,
|
||||
"\n (converting list element from string to real)");
|
||||
sortCode = TCL_ERROR;
|
||||
return order;
|
||||
}
|
||||
if (a > b) {
|
||||
order = 1;
|
||||
} else if (b > a) {
|
||||
order = -1;
|
||||
}
|
||||
} else {
|
||||
int oldLength;
|
||||
char *end;
|
||||
|
||||
/*
|
||||
* Generate and evaluate a command to determine which string comes
|
||||
* first.
|
||||
*/
|
||||
|
||||
oldLength = Tcl_DStringLength(&sortCmd);
|
||||
Tcl_DStringAppendElement(&sortCmd, firstString);
|
||||
Tcl_DStringAppendElement(&sortCmd, secondString);
|
||||
sortCode = Tcl_Eval(sortInterp, Tcl_DStringValue(&sortCmd));
|
||||
Tcl_DStringTrunc(&sortCmd, oldLength);
|
||||
if (sortCode != TCL_OK) {
|
||||
Tcl_AddErrorInfo(sortInterp,
|
||||
"\n (user-defined comparison command)");
|
||||
return order;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the result of the command.
|
||||
*/
|
||||
|
||||
order = strtol(sortInterp->result, &end, 0);
|
||||
if ((end == sortInterp->result) || (*end != 0)) {
|
||||
Tcl_ResetResult(sortInterp);
|
||||
Tcl_AppendResult(sortInterp,
|
||||
"comparison command returned non-numeric result",
|
||||
(char *) NULL);
|
||||
sortCode = TCL_ERROR;
|
||||
return order;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
if (!sortIncreasing) {
|
||||
order = -order;
|
||||
}
|
||||
return order;
|
||||
}
|
||||
Reference in New Issue
Block a user