All pastes #1919287 Raw Edit

Stuff

public diff v1 · immutable
#1919287 ·published 2010-08-18 08:33 UTC
rendered paste body
Index: table.c===================================================================--- table.c	(revision 8866)+++ table.c	(working copy)@@ -369,8 +369,8 @@ 	return (0); } -static __inline__ void-PrintCell(AG_TableCell *c, char *buf, size_t bufsz)+void+AG_TablePrintCell(const AG_TableCell *c, char *buf, size_t bufsz) { 	switch (c->type) { 	case AG_CELL_INT:@@ -454,23 +454,14 @@ 	case AG_CELL_FN_TXT: 		c->fnTxt(c->data.p, buf, bufsz); 		break;-	case AG_CELL_FN_SU:-	case AG_CELL_FN_SU_NODUP:-		Strlcpy(buf, "<image>", bufsz);-		break; 	case AG_CELL_POINTER: 		Snprintf(buf, bufsz, c->fmt, c->data.p); 		break;-	case AG_CELL_NULL:-		if (c->fmt[0] == '\0') {-			Strlcpy(buf, "<null>", bufsz);-		} else {-			Strlcpy(buf, c->fmt, bufsz);+	default:+		if (bufsz > 0) {+			buf[0] = '\0'; 		} 		break;-	case AG_CELL_WIDGET:-		Strlcpy(buf, "<widget>", bufsz);-		break; 	} } @@ -523,7 +514,7 @@ 		} 		break; 	default:-		PrintCell(c, txt, sizeof(txt));+		AG_TablePrintCell(c, txt, sizeof(txt)); 		break; 	} 	AG_TextColor(agColors[TEXT_COLOR]);@@ -841,36 +832,18 @@ 	} } -/*- * Add an entry to a column pool.- * Table must be locked.- */-int-AG_TablePoolAdd(AG_Table *t, int m, int n)+static __inline__ Uint+HashPrevCell(AG_Table *t, const AG_TableCell *c) {-	AG_TableCol *tc = &t->cols[n];-	-	tc->pool = Realloc(tc->pool, (tc->mpool+1)*sizeof(AG_TableCell));-	memcpy(&tc->pool[tc->mpool], &t->cells[m][n], sizeof(AG_TableCell));-	return (tc->mpool++);-}+	char buf[AG_TABLE_HASHBUF_MAX];+	Uint h;+	Uchar *p; -/*- * Cleanup a column pool.- * Table must be locked.- */-void-AG_TablePoolFree(AG_Table *t, int n)-{-	AG_TableCol *tc = &t->cols[n];-	int m;--	for (m = 0; m < tc->mpool; m++) {-		AG_TableFreeCell(t, &tc->pool[m]);+	AG_TablePrintCell(c, buf, sizeof(buf));+	for (h = 0, p = (Uchar *)buf; *p != '\0'; p++) {+		h = 31*h + *p; 	}-	Free(tc->pool);-	tc->pool = NULL;-	tc->mpool = 0;+	return (h % t->nPrevBuckets); }  /*@@ -882,11 +855,18 @@ { 	int m, n; -	AG_ObjectLock(t);-	/* Copy the existing cells to the column pools and free the table. */+	AG_ObjectLock(t);		/* Lock across TableBegin/End */++	/* Copy the existing cells to the backing store and free the table. */ 	for (m = 0; m < t->m; m++) { 		for (n = 0; n < t->n; n++) {-			AG_TablePoolAdd(t, m, n);+			AG_TableCell *c = &t->cells[m][n], *cPrev;+			AG_TableBucket *tbPrev = &t->cPrev[HashPrevCell(t,c)];++			cPrev = Malloc(sizeof(AG_TableCell));+			memcpy(cPrev, c, sizeof(AG_TableCell));+			TAILQ_INSERT_HEAD(&tbPrev->cells, cPrev, cells);+			TAILQ_INSERT_HEAD(&t->cPrevList, cPrev, cells_list); 		} 		Free(t->cells[m]); 	}@@ -896,6 +876,7 @@ 	t->flags &= ~(AG_TABLE_WIDGETS); } +/* Compare two "%[Ft]" cells. */ static int AG_TableCompareFnTxtCells(const AG_TableCell *c1, const AG_TableCell *c2) {@@ -907,6 +888,7 @@ 	return (strcoll(b1, b2)); } +/* Compare two table cells. */ int AG_TableCompareCells(const AG_TableCell *c1, const AG_TableCell *c2) {@@ -916,6 +898,9 @@ 		} 		return (1); 	}+	if (c1->id != 0 && c2->id != 0) {+		return (c1->id - c2->id);+	} 	switch (c1->type) { 	case AG_CELL_STRING: 		return (strcoll(c1->data.s, c2->data.s));@@ -975,43 +960,148 @@ 	return (1); } +/* Restore selection state on a per-row basis. */+static void+TableRestoreRowSelections(AG_Table *t)+{+	int m, n;+	int nMatched, nCompared, sel;++	for (m = 0; m < t->m; m++) {+		nMatched = 0;+		nCompared = 0;+		sel = 0;+		for (n = 0; n < t->n; n++) {+			AG_TableCell *c = &t->cells[m][n];+			AG_TableCell *cPrev;+			AG_TableBucket *tb;++			if (c->type == AG_CELL_NULL) {+				continue;+			}+			tb = &t->cPrev[HashPrevCell(t,c)];+			TAILQ_FOREACH(cPrev, &tb->cells, cells) {+				if (AG_TableCompareCells(c, cPrev) != 0) {+					continue;+				}+				c->surface = cPrev->surface;+				cPrev->surface = -1;+				if (!(cPrev->flags & AG_TABLE_CELL_NOCOMPARE)) {+					if (cPrev->selected)+						nMatched++;+				}+			}+			if (!(c->flags & AG_TABLE_CELL_NOCOMPARE))+				nCompared++;+		}+		if (nMatched == nCompared)+			AG_TableSelectRow(t, m);+	}+}+ /*- * Restore the selection state and recover the surfaces of matching- * items in the column pool. Unlock the table.+ * Restore selection state on a per-cell basis. In most applications, the+ * user will almost always need to specify per-cell unique IDs for selections+ * to restore properly.  */-void-AG_TableEnd(AG_Table *t)+static void+TableRestoreCellSelections(AG_Table *t) {-	int m, n, mPool;+	int m, n;  	for (n = 0; n < t->n; n++) {-		AG_TableCol *tc = &t->cols[n];+		for (m = 0; m < t->m; m++) {+			AG_TableCell *c = &t->cells[m][n];+			AG_TableCell *cPrev;+			AG_TableBucket *tb;+			+			if (c->type == AG_CELL_NULL) {+				continue;+			}+			tb = &t->cPrev[HashPrevCell(t,c)];+			TAILQ_FOREACH(cPrev, &tb->cells, cells) {+				if (AG_TableCompareCells(c, cPrev) != 0) {+					continue;+				}+				c->selected = cPrev->selected;+				c->surface = cPrev->surface;+				cPrev->surface = -1;+				break;+			}+		}+	}+} -		/*-		 * Compare the new cells against the backing store to-		 * recycle surfaces and recover selection state.-		 */-		for (mPool = 0; mPool < tc->mpool; mPool++) {-			AG_TableCell *cPool = &tc->pool[mPool];+/*+ * Restore selection state on a per-column basis.+ */+static void+TableRestoreColSelections(AG_Table *t)+{+	int m, n; -			for (m = 0; m < t->m; m++) {-				AG_TableCell *c = &t->cells[m][n];-				-				if (AG_TableCompareCells(c, cPool) == 0) {-					c->surface = cPool->surface;-					c->selected = cPool->selected;-					cPool->surface = -1;+	for (n = 0; n < t->n; n++) {+		for (m = 0; m < t->m; m++) {+			AG_TableCell *c = &t->cells[m][n];+			AG_TableCell *cPrev;+			AG_TableBucket *tb;+			+			if (c->type == AG_CELL_NULL) {+				continue;+			}+			tb = &t->cPrev[HashPrevCell(t,c)];++			TAILQ_FOREACH(cPrev, &tb->cells, cells) {+				if (AG_TableCompareCells(c, cPrev) != 0) {+					continue; 				}+				c->surface = cPrev->surface;+				cPrev->surface = -1; 			}-			AG_TableFreeCell(t, cPool); 		}-		Free(tc->pool);-		tc->pool = NULL;-		tc->mpool = 0; 	}-	AG_ObjectUnlock(t); } +/*+ * Restore the selection state and recover the surfaces of matching+ * items in the backing store. Unlock the table.+ */+void+AG_TableEnd(AG_Table *t)+{+	AG_TableCell *tc, *tcNext;++	if (t->n == 0)+		goto out;+	+	/* Recover surfaces and selection state from the backing store. */+	switch (t->selMode) {+	case AG_TABLE_SEL_ROWS:+		TableRestoreRowSelections(t);+		break;+	case AG_TABLE_SEL_CELLS:+		TableRestoreCellSelections(t);+		break;+	case AG_TABLE_SEL_COLS:+		TableRestoreColSelections(t);+		break;+	}++	/* Clear the backing store. */+	for (tc = TAILQ_FIRST(&t->cPrevList);+	     tc != TAILQ_END(&t->cPrevList);+	     tc = tcNext) {+		tcNext = TAILQ_NEXT(tc, cells_list);+		Free(tc);+	}+	TAILQ_INIT(&t->cPrevList);+	/* It is safe to use memset() in place of TAILQ_INIT(). */+	memset(t->cPrev, 0, t->nPrevBuckets*sizeof(AG_TableBucket));++out:+	AG_ObjectUnlock(t);		/* Lock across TableBegin/End */+}+ static int AG_TableSortCellsAsc(const void *p1, const void *p2) {@@ -1818,8 +1908,6 @@ 	tc->selected = 0; 	tc->w = 0; 	tc->wPct = -1;-	tc->pool = NULL;-	tc->mpool = 0;  	AG_PushTextState(); 	AG_TextColor(agColors[TEXT_COLOR]);@@ -1851,13 +1939,13 @@  	/* Resize the row arrays. */ 	for (m = 0; m < t->m; m++) {-		AG_TableCell *cellsNew;+		AG_TableCell *cNew; -		if ((cellsNew = TryRealloc(t->cells[m],+		if ((cNew = TryRealloc(t->cells[m], 		    (t->n+1)*sizeof(AG_TableCell))) == NULL) { 			goto fail; 		}-		t->cells[m] = cellsNew;+		t->cells[m] = cNew; 		AG_TableInitCell(t, &t->cells[m][t->n]); 	} 	n = t->n++;@@ -1881,6 +1969,8 @@ 	c->surface = -1; 	c->widget = NULL; 	c->tbl = t;+	c->id = 0;+	c->flags = 0; }  int@@ -1889,21 +1979,20 @@ 	char fmt[64], *sp = &fmt[0]; 	va_list ap; 	int n, rv;-	AG_TableCell **cellsNew;+	AG_TableCell **cNew;  	Strlcpy(fmt, fmtp, sizeof(fmt));  	AG_ObjectLock(t); -	if ((cellsNew = TryRealloc(t->cells, (t->m+1)*sizeof(AG_TableCell)))-	    == NULL) {+	if ((cNew = TryRealloc(t->cells, (t->m+1)*sizeof(AG_TableCell))) == NULL) { 		goto fail; 	}-	if ((cellsNew[t->m] = TryMalloc(t->n*sizeof(AG_TableCell))) == NULL) {-		Free(cellsNew);+	if ((cNew[t->m] = TryMalloc(t->n*sizeof(AG_TableCell))) == NULL) {+		Free(cNew); 		goto fail; 	}-	t->cells = cellsNew;+	t->cells = cNew;  	va_start(ap, fmtp); 	for (n = 0; n < t->n; n++) {@@ -2116,7 +2205,7 @@ 			if (t->cols[n].name[0] == '\0') { 				continue; 			}-			PrintCell(&t->cells[m][n], txt, sizeof(txt));+			AG_TablePrintCell(&t->cells[m][n], txt, sizeof(txt)); 			fputs(txt, f); 			fputc(sep, f); 		}@@ -2163,6 +2252,7 @@ Init(void *obj) { 	AG_Table *t = obj;+	Uint i;  	WIDGET(t)->flags |= AG_WIDGET_FOCUSABLE| 	                    AG_WIDGET_UNFOCUSED_MOTION|@@ -2219,6 +2309,14 @@ 	t->wheelTicks = 0; 	SLIST_INIT(&t->popups); +	t->nPrevBuckets = 256;+	t->cPrev = Malloc(t->nPrevBuckets*sizeof(AG_TableBucket));+	for (i = 0; i < t->nPrevBuckets; i++) {+		AG_TableBucket *tb = &t->cPrev[i];+		TAILQ_INIT(&tb->cells);+	}+	TAILQ_INIT(&t->cPrevList);+ 	AG_SetEvent(t, "mouse-button-down", MouseButtonDown, NULL); 	AG_SetEvent(t, "mouse-button-up", MouseButtonUp, NULL); 	AG_SetEvent(t, "mouse-motion", MouseMotion, NULL);@@ -2258,23 +2356,38 @@ Destroy(void *obj) { 	AG_Table *t = obj;-	AG_TablePopup *tp, *tpn;+	AG_TablePopup *pop, *nPop;+	AG_TableCell *c, *cNext; 	int i;-	-	for (tp = SLIST_FIRST(&t->popups);-	     tp != SLIST_END(&t->popups);-	     tp = tpn) {-		tpn = SLIST_NEXT(tp, popups);-		AG_ObjectDestroy(tp->menu);-		Free(tp);++	/* Free the attached popup menus. */+	for (pop = SLIST_FIRST(&t->popups);+	     pop != SLIST_END(&t->popups);+	     pop = nPop) {+		nPop = SLIST_NEXT(pop, popups);+		AG_ObjectDestroy(pop->menu);+		Free(pop); 	}-	for (i = 0; i < t->n; i++) {-		Free(t->cols[i].pool);++	/* Free the active cells. */+	for (i = 0; i < t->m; i++) {+		Free(t->cells[i]); 	}+	Free(t->cells);++	/* Free the backing store. */+	for (c = TAILQ_FIRST(&t->cPrevList);+	     c != TAILQ_END(&t->cPrevList);+	     c = cNext) {+		cNext = TAILQ_NEXT(c, cells_list);+		Free(c);+	}+	Free(t->cPrev);++	/* Free the columns. */ 	Free(t->cols); } - AG_WidgetClass agTableClass = { 	{ 		"Agar(Widget:Table)",Index: table.h===================================================================--- table.h	(revision 8866)+++ table.h	(working copy)@@ -8,9 +8,10 @@  #include <agar/gui/begin.h> -#define AG_TABLE_TXT_MAX 128-#define AG_TABLE_FMT_MAX 16-#define AG_TABLE_COL_NAME_MAX 48+#define AG_TABLE_TXT_MAX	128	/* Length of fixed text cells */+#define AG_TABLE_FMT_MAX	16	/* Length of cell specifier string */+#define AG_TABLE_COL_NAME_MAX	48	/* Column name string */+#define AG_TABLE_HASHBUF_MAX	64	/* Buffer used in hash function */  struct ag_table; 	@@ -84,8 +85,18 @@ 	int selected;				/* Cell is selected */ 	int surface;				/* Named of mapped surface */ 	struct ag_table *tbl;			/* Back pointer to Table */+	Uint id;				/* Optional user-specified ID */+	Uint flags;+#define AG_TABLE_CELL_NOCOMPARE	0x01		/* Ignore when comparing cells+						   against backing store. */+	AG_TAILQ_ENTRY(ag_table_cell) cells;	/* In AG_TableBucket */+	AG_TAILQ_ENTRY(ag_table_cell) cells_list; /* In AG_Table */ } AG_TableCell; +typedef struct ag_table_bucket {+	AG_TAILQ_HEAD_(ag_table_cell) cells;+} AG_TableBucket;+ typedef struct ag_table_col { 	char name[AG_TABLE_COL_NAME_MAX]; 	int (*sortFn)(const void *, const void *);@@ -101,8 +112,6 @@ 	int wPct;			/* Width (percent or -1) */ 	int x;				/* Current position */ 	int surface;			/* Text surface mapping */-	AG_TableCell *pool;		/* Backing store */-	int          mpool;		/* Number of rows in backing store */ } AG_TableCol;  typedef struct ag_table {@@ -129,8 +138,13 @@ 					   columns */ 	int xOffs;			/* Column display offset */ 	int mOffs;			/* Row offset (for poll function) */-	AG_TableCol *cols;		/* Column data */-	AG_TableCell **cells;		/* Row data */++	AG_TableCol     *cols;		/* Column data */+	AG_TableCell   **cells;		/* Current cell data (sorted rows) */+	AG_TableBucket *cPrev;		/* Saved cells (value hash) */+	Uint            nPrevBuckets;+	AG_TAILQ_HEAD_(ag_table_cell) cPrevList;	+ 	int n;				/* Number of columns */ 	int m;				/* Number of rows */ 	int mVis;			/* Maximum number of visible rows */@@ -189,19 +203,15 @@ void AG_TableSetSelectionColor(AG_Table *, Uint8, Uint8, Uint8, Uint8); void AG_TableSetColumnAction(AG_Table *, Uint); -void	  AG_TableFreeCell(AG_Table *, AG_TableCell *);-int	  AG_TablePoolAdd(AG_Table *, int, int);-void	  AG_TablePoolFree(AG_Table *, int);- void	  AG_TableClear(AG_Table *); void	  AG_TableBegin(AG_Table *); void	  AG_TableEnd(AG_Table *); void      AG_TableSort(AG_Table *);  void	  AG_TableInitCell(AG_Table *, AG_TableCell *);-#define	  AG_TableCellSelected(t,m,n) ((t)->cells[m][n].selected)-#define	  AG_TableSelectCell(t,m,n) ((t)->cells[m][n].selected = 1)-#define	  AG_TableDeselectCell(t,m,n) ((t)->cells[m][n].selected = 0)+void	  AG_TablePrintCell(const AG_TableCell *, char *, size_t);+void	  AG_TableFreeCell(AG_Table *, AG_TableCell *);+void	  AG_TableForgetCells(AG_Table *);  int	  AG_TableAddRow(AG_Table *, const char *, ...); void	  AG_TableSelectRow(AG_Table *, int);@@ -214,15 +224,53 @@ 	                 int (*)(const void *, const void *)); void	  AG_TableSelectAllCols(AG_Table *); void	  AG_TableDeselectAllCols(AG_Table *);-#define	  AG_TableSelectCol(t,n) do { (t)->cols[n].selected = 1; } while (0)-#define	  AG_TableDeselectCol(t,n) do { (t)->cols[n].selected = 0; } while (0)-#define	  AG_TableColSelected(t,n) ((t)->cols[n].selected)  void	  AG_TableRedrawCells(AG_Table *); int	  AG_TableCompareCells(const AG_TableCell *, const AG_TableCell *);  AG_MenuItem *AG_TableSetPopup(AG_Table *, int, int); int	     AG_TableSaveASCII(AG_Table *, FILE *, char);++/* Return cell at [m,n]. */+static __inline__ AG_TableCell *+AG_TableGetCell(AG_Table *t, int m, int n)+{+	return (&t->cells[m][n]);+}++/* Cell selection control */+static __inline__ int+AG_TableCellSelected(AG_Table *t, int m, int n)+{+	return (t->cells[m][n].selected);+}+static __inline__ void+AG_TableSelectCell(AG_Table *t, int m, int n)+{+	t->cells[m][n].selected = 1;+}+static __inline__ void+AG_TableDeselectCell(AG_Table *t, int m, int n)+{+	t->cells[m][n].selected = 0;+}++/* Column selection control */+static __inline__ int+AG_TableColSelected(AG_Table *t, int n)+{+	return (t->cols[n].selected);+}+static __inline__ void+AG_TableSelectCol(AG_Table *t, int n)+{+	t->cols[n].selected = 1;+}+static __inline__ void+AG_TableDeselectCol(AG_Table *t, int n)+{+	t->cols[n].selected = 0;+} __END_DECLS  #include <agar/gui/close.h>