401 lines
11 KiB
C
401 lines
11 KiB
C
/*
|
|
* tkTableCell.c --
|
|
*
|
|
* This module implements cell sort functions for table
|
|
* widgets. The MergeSort algorithm and other aux sorting
|
|
* functions were taken from tclCmdIL.c lsort command:
|
|
|
|
* tclCmdIL.c --
|
|
*
|
|
* This file contains the top-level command routines for most of
|
|
* the Tcl built-in commands whose names begin with the letters
|
|
* I through L. It contains only commands in the generic core
|
|
* (i.e. those that don't depend much upon UNIX facilities).
|
|
*
|
|
* Copyright (c) 1987-1993 The Regents of the University of California.
|
|
* Copyright (c) 1993-1997 Lucent Technologies.
|
|
* Copyright (c) 1994-1997 Sun Microsystems, Inc.
|
|
* Copyright (c) 1998-1999 by Scriptics Corporation.
|
|
|
|
*
|
|
* Copyright (c) 1998-2002 Jeffrey Hobbs
|
|
*
|
|
* See the file "license.terms" for information on usage and redistribution
|
|
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
|
*
|
|
*/
|
|
|
|
#include "tkTable.h"
|
|
|
|
#ifndef UCHAR
|
|
#define UCHAR(c) ((unsigned char) (c))
|
|
#endif
|
|
|
|
/*
|
|
* During execution of the "lsort" command, structures of the following
|
|
* type are used to arrange the objects being sorted into a collection
|
|
* of linked lists.
|
|
*/
|
|
|
|
typedef struct SortElement {
|
|
Tcl_Obj *objPtr; /* Object being sorted. */
|
|
struct SortElement *nextPtr; /* Next element in the list, or
|
|
* NULL for end of list. */
|
|
} SortElement;
|
|
|
|
static int TableSortCompareProc _ANSI_ARGS_((CONST VOID *first,
|
|
CONST VOID *second));
|
|
static SortElement * MergeSort _ANSI_ARGS_((SortElement *headPt));
|
|
static SortElement * MergeLists _ANSI_ARGS_((SortElement *leftPtr,
|
|
SortElement *rightPtr));
|
|
static int DictionaryCompare _ANSI_ARGS_((char *left,
|
|
char *right));
|
|
|
|
/*
|
|
*----------------------------------------------------------------------
|
|
*
|
|
* TableSortCompareProc --
|
|
* 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
|
|
TableSortCompareProc(first, second)
|
|
CONST VOID *first, *second; /* Elements to be compared. */
|
|
{
|
|
char *str1 = *((char **) first);
|
|
char *str2 = *((char **) second);
|
|
|
|
return DictionaryCompare(str1, str2);
|
|
}
|
|
|
|
/*
|
|
*----------------------------------------------------------------------
|
|
*
|
|
* TableCellSort --
|
|
* Sort a list of table cell elements (of form row,col)
|
|
*
|
|
* Results:
|
|
* Returns the sorted list of elements. Because Tcl_Merge allocs
|
|
* the space for result, it must later be Tcl_Free'd by caller.
|
|
*
|
|
* Side effects:
|
|
* Behaviour undefined for ill-formed input list of elements.
|
|
*
|
|
*----------------------------------------------------------------------
|
|
*/
|
|
char *
|
|
TableCellSort(Table *tablePtr, char *str)
|
|
{
|
|
int listArgc;
|
|
CONST84 char **listArgv;
|
|
char *result;
|
|
|
|
if (Tcl_SplitList(tablePtr->interp, str, &listArgc, &listArgv) != TCL_OK) {
|
|
return str;
|
|
}
|
|
/* Thread safety: qsort is reportedly not thread-safe... */
|
|
qsort((VOID *) listArgv, (size_t) listArgc, sizeof (char *),
|
|
TableSortCompareProc);
|
|
result = Tcl_Merge(listArgc, listArgv);
|
|
ckfree((char *) listArgv);
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
*----------------------------------------------------------------------
|
|
*
|
|
* DictionaryCompare - Not the Unicode version
|
|
*
|
|
* This function compares two strings as if they were being used in
|
|
* an index or card catalog. The case of alphabetic characters is
|
|
* ignored, except to break ties. Thus "B" comes before "b" but
|
|
* after "a". Also, integers embedded in the strings compare in
|
|
* numerical order. In other words, "x10y" comes after "x9y", not
|
|
* before it as it would when using strcmp().
|
|
*
|
|
* Results:
|
|
* A negative result means that the first element comes before the
|
|
* second, and a positive result means that the second element
|
|
* should come first. A result of zero means the two elements
|
|
* are equal and it doesn't matter which comes first.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
*----------------------------------------------------------------------
|
|
*/
|
|
|
|
static int
|
|
DictionaryCompare(left, right)
|
|
char *left, *right; /* The strings to compare */
|
|
{
|
|
int diff, zeros;
|
|
int secondaryDiff = 0;
|
|
|
|
while (1) {
|
|
if (isdigit(UCHAR(*right)) && isdigit(UCHAR(*left))) {
|
|
/*
|
|
* There are decimal numbers embedded in the two
|
|
* strings. Compare them as numbers, rather than
|
|
* strings. If one number has more leading zeros than
|
|
* the other, the number with more leading zeros sorts
|
|
* later, but only as a secondary choice.
|
|
*/
|
|
|
|
zeros = 0;
|
|
while ((*right == '0') && (isdigit(UCHAR(right[1])))) {
|
|
right++;
|
|
zeros--;
|
|
}
|
|
while ((*left == '0') && (isdigit(UCHAR(left[1])))) {
|
|
left++;
|
|
zeros++;
|
|
}
|
|
if (secondaryDiff == 0) {
|
|
secondaryDiff = zeros;
|
|
}
|
|
|
|
/*
|
|
* The code below compares the numbers in the two
|
|
* strings without ever converting them to integers. It
|
|
* does this by first comparing the lengths of the
|
|
* numbers and then comparing the digit values.
|
|
*/
|
|
|
|
diff = 0;
|
|
while (1) {
|
|
if (diff == 0) {
|
|
diff = UCHAR(*left) - UCHAR(*right);
|
|
}
|
|
right++;
|
|
left++;
|
|
if (!isdigit(UCHAR(*right))) {
|
|
if (isdigit(UCHAR(*left))) {
|
|
return 1;
|
|
} else {
|
|
/*
|
|
* The two numbers have the same length. See
|
|
* if their values are different.
|
|
*/
|
|
|
|
if (diff != 0) {
|
|
return diff;
|
|
}
|
|
break;
|
|
}
|
|
} else if (!isdigit(UCHAR(*left))) {
|
|
return -1;
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
diff = UCHAR(*left) - UCHAR(*right);
|
|
if (diff) {
|
|
if (isupper(UCHAR(*left)) && islower(UCHAR(*right))) {
|
|
diff = UCHAR(tolower(*left)) - UCHAR(*right);
|
|
if (diff) {
|
|
return diff;
|
|
} else if (secondaryDiff == 0) {
|
|
secondaryDiff = -1;
|
|
}
|
|
} else if (isupper(UCHAR(*right)) && islower(UCHAR(*left))) {
|
|
diff = UCHAR(*left) - UCHAR(tolower(UCHAR(*right)));
|
|
if (diff) {
|
|
return diff;
|
|
} else if (secondaryDiff == 0) {
|
|
secondaryDiff = 1;
|
|
}
|
|
} else {
|
|
return diff;
|
|
}
|
|
}
|
|
if (*left == 0) {
|
|
break;
|
|
}
|
|
left++;
|
|
right++;
|
|
}
|
|
if (diff == 0) {
|
|
diff = secondaryDiff;
|
|
}
|
|
return diff;
|
|
}
|
|
|
|
/*
|
|
*----------------------------------------------------------------------
|
|
*
|
|
* MergeLists -
|
|
*
|
|
* This procedure combines two sorted lists of SortElement structures
|
|
* into a single sorted list.
|
|
*
|
|
* Results:
|
|
* The unified list of SortElement structures.
|
|
*
|
|
* Side effects:
|
|
* None, unless a user-defined comparison command does something
|
|
* weird.
|
|
*
|
|
*----------------------------------------------------------------------
|
|
*/
|
|
|
|
static SortElement *
|
|
MergeLists(leftPtr, rightPtr)
|
|
SortElement *leftPtr; /* First list to be merged; may be
|
|
* NULL. */
|
|
SortElement *rightPtr; /* Second list to be merged; may be
|
|
* NULL. */
|
|
{
|
|
SortElement *headPtr;
|
|
SortElement *tailPtr;
|
|
|
|
if (leftPtr == NULL) {
|
|
return rightPtr;
|
|
}
|
|
if (rightPtr == NULL) {
|
|
return leftPtr;
|
|
}
|
|
if (DictionaryCompare(Tcl_GetString(leftPtr->objPtr),
|
|
Tcl_GetString(rightPtr->objPtr)) > 0) {
|
|
tailPtr = rightPtr;
|
|
rightPtr = rightPtr->nextPtr;
|
|
} else {
|
|
tailPtr = leftPtr;
|
|
leftPtr = leftPtr->nextPtr;
|
|
}
|
|
headPtr = tailPtr;
|
|
while ((leftPtr != NULL) && (rightPtr != NULL)) {
|
|
if (DictionaryCompare(Tcl_GetString(leftPtr->objPtr),
|
|
Tcl_GetString(rightPtr->objPtr)) > 0) {
|
|
tailPtr->nextPtr = rightPtr;
|
|
tailPtr = rightPtr;
|
|
rightPtr = rightPtr->nextPtr;
|
|
} else {
|
|
tailPtr->nextPtr = leftPtr;
|
|
tailPtr = leftPtr;
|
|
leftPtr = leftPtr->nextPtr;
|
|
}
|
|
}
|
|
if (leftPtr != NULL) {
|
|
tailPtr->nextPtr = leftPtr;
|
|
} else {
|
|
tailPtr->nextPtr = rightPtr;
|
|
}
|
|
return headPtr;
|
|
}
|
|
|
|
/*
|
|
*----------------------------------------------------------------------
|
|
*
|
|
* MergeSort -
|
|
*
|
|
* This procedure sorts a linked list of SortElement structures
|
|
* use the merge-sort algorithm.
|
|
*
|
|
* Results:
|
|
* A pointer to the head of the list after sorting is returned.
|
|
*
|
|
* Side effects:
|
|
* None, unless a user-defined comparison command does something
|
|
* weird.
|
|
*
|
|
*----------------------------------------------------------------------
|
|
*/
|
|
|
|
static SortElement *
|
|
MergeSort(headPtr)
|
|
SortElement *headPtr; /* First element on the list */
|
|
{
|
|
/*
|
|
* The subList array below holds pointers to temporary lists built
|
|
* during the merge sort. Element i of the array holds a list of
|
|
* length 2**i.
|
|
*/
|
|
|
|
# define NUM_LISTS 30
|
|
SortElement *subList[NUM_LISTS];
|
|
SortElement *elementPtr;
|
|
int i;
|
|
|
|
for(i = 0; i < NUM_LISTS; i++){
|
|
subList[i] = NULL;
|
|
}
|
|
while (headPtr != NULL) {
|
|
elementPtr = headPtr;
|
|
headPtr = headPtr->nextPtr;
|
|
elementPtr->nextPtr = 0;
|
|
for (i = 0; (i < NUM_LISTS) && (subList[i] != NULL); i++){
|
|
elementPtr = MergeLists(subList[i], elementPtr);
|
|
subList[i] = NULL;
|
|
}
|
|
if (i >= NUM_LISTS) {
|
|
i = NUM_LISTS-1;
|
|
}
|
|
subList[i] = elementPtr;
|
|
}
|
|
elementPtr = NULL;
|
|
for (i = 0; i < NUM_LISTS; i++){
|
|
elementPtr = MergeLists(subList[i], elementPtr);
|
|
}
|
|
return elementPtr;
|
|
}
|
|
|
|
#ifndef NO_SORT_CELLS
|
|
/*
|
|
*----------------------------------------------------------------------
|
|
*
|
|
* TableCellSortObj --
|
|
* Sorts a list of table cell elements (of form row,col) in place
|
|
*
|
|
* Results:
|
|
* Sorts list of elements in place.
|
|
*
|
|
* Side effects:
|
|
* Behaviour undefined for ill-formed input list of elements.
|
|
*
|
|
*----------------------------------------------------------------------
|
|
*/
|
|
Tcl_Obj *
|
|
TableCellSortObj(Tcl_Interp *interp, Tcl_Obj *listObjPtr)
|
|
{
|
|
int length, i;
|
|
Tcl_Obj *sortedObjPtr, **listObjPtrs;
|
|
SortElement *elementArray;
|
|
SortElement *elementPtr;
|
|
|
|
if (Tcl_ListObjGetElements(interp, listObjPtr,
|
|
&length, &listObjPtrs) != TCL_OK) {
|
|
return NULL;
|
|
}
|
|
if (length <= 0) {
|
|
return listObjPtr;
|
|
}
|
|
|
|
elementArray = (SortElement *) ckalloc(length * sizeof(SortElement));
|
|
for (i=0; i < length; i++){
|
|
elementArray[i].objPtr = listObjPtrs[i];
|
|
elementArray[i].nextPtr = &elementArray[i+1];
|
|
}
|
|
elementArray[length-1].nextPtr = NULL;
|
|
elementPtr = MergeSort(elementArray);
|
|
sortedObjPtr = Tcl_NewObj();
|
|
for (; elementPtr != NULL; elementPtr = elementPtr->nextPtr){
|
|
Tcl_ListObjAppendElement(NULL, sortedObjPtr, elementPtr->objPtr);
|
|
}
|
|
ckfree((char*) elementArray);
|
|
|
|
return sortedObjPtr;
|
|
}
|
|
#endif
|