1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  *
3  * ***** BEGIN LICENSE BLOCK *****
4  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5  *
6  * The contents of this file are subject to the Mozilla Public License Version
7  * 1.1 (the "License"); you may not use this file except in compliance with
8  * the License. You may obtain a copy of the License at
9  * http://www.mozilla.org/MPL/
10  *
11  * Software distributed under the License is distributed on an "AS IS" basis,
12  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13  * for the specific language governing rights and limitations under the
14  * License.
15  *
16  * The Original Code is the Microline Widget Library, originally made available under the NPL by Neuron Data <http://www.neurondata.com>.
17  *
18  * The Initial Developer of the Original Code is
19  * Netscape Communications Corporation.
20  * Portions created by the Initial Developer are Copyright (C) 1998
21  * the Initial Developer. All Rights Reserved.
22  *
23  * Contributor(s):
24  *
25  * Alternatively, the contents of this file may be used under the terms of
26  * either the GNU General Public License Version 2 or later (the "GPL"), or
27  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28  * in which case the provisions of the GPL or the LGPL are applicable instead
29  * of those above. If you wish to allow use of your version of this file only
30  * under the terms of either the GPL or the LGPL, and not to allow others to
31  * use your version of this file under the terms of the MPL, indicate your
32  * decision by deleting the provisions above and replace them with the notice
33  * and other provisions required by the GPL or the LGPL. If you do not delete
34  * the provisions above, a recipient may use your version of this file under
35  * the terms of any one of the MPL, the GPL or the LGPL.
36  *
37  * In addition, as a special exception to the GNU GPL, the copyright holders
38  * give permission to link the code of this program with the Motif and Open
39  * Motif libraries (or with modified versions of these that use the same
40  * license), and distribute linked combinations including the two. You
41  * must obey the GNU General Public License in all respects for all of
42  * the code used other than linking with Motif/Open Motif. If you modify
43  * this file, you may extend this exception to your version of the file,
44  * but you are not obligated to do so. If you do not wish to do so,
45  * delete this exception statement from your version.
46  *
47  * ***** END LICENSE BLOCK ***** */
48 
49 
50 #include <Xm/Xm.h>
51 #include <Xm/Text.h>
52 #include <XmL/Grid.h>
53 
54 /* DATABASE PROTOTYPE FUNCTIONS */
55 
56 int dbTableNumRows = 14;
57 int dbTableNumColumns = 5;
58 
59 typedef enum {
60 	ID, Desc, Price, Qty, UnitPrice, Buyer
61 }
62 DbTableColumnID;
63 
64 typedef struct
65 	{
66 	DbTableColumnID id;
67 	char label[15];
68 	int width;
69 	unsigned char cellAlignment;
70 	Boolean cellEditable;
71 } DbTableColumn;
72 
73 DbTableColumn dbTableColumns[] =
74 {
75 	{ Desc,      "Description", 16, XmALIGNMENT_LEFT,  True  },
76 	{ Price,     "Price",       9,  XmALIGNMENT_LEFT, True  },
77 	{ Qty,       "Qty",         5,  XmALIGNMENT_LEFT, True  },
78 	{ UnitPrice, "Unit Prc",    9,  XmALIGNMENT_LEFT, False },
79 	{ Buyer,     "Buyer",       15, XmALIGNMENT_LEFT,  True  },
80 };
81 
82 typedef struct
83 	{
84 	char key[10];
85 	char desc[20];
86 	float price;
87 	int qty;
88 	char buyer[20];
89 } DbTableRow;
90 
91 DbTableRow dbTableRows[] =
92 {
93 	{ "key01", "Staples",        1.32, 100, "Tim Pick"   },
94 	{ "key02", "Notebooks",      1.11,   4, "Mary Miner" },
95 	{ "key03", "3-Ring Binders", 2.59,   2, "Mary Miner" },
96 	{ "key04", "Pads",           1.23,   3, "Tim Pick"   },
97 	{ "key05", "Scissors",       4.41,   1, "Mary Miner" },
98 	{ "key06", "Pens",            .29,   4, "Mary Miner" },
99 	{ "key07", "Pencils",         .10,   5, "Tim Pick"   },
100 	{ "key08", "Markers",         .95,   3, "Mary Miner" },
101 	{ "key09", "Fax Paper",      3.89, 100, "Bob Coal"   },
102 	{ "key10", "3.5\" Disks",   15.23,  30, "Tim Pick"   },
103 	{ "key11", "8mm Tape",      32.22,   2, "Bob Coal"   },
104 	{ "key12", "Toner",         35.69,   1, "Tim Pick"   },
105 	{ "key13", "Paper Cups",     4.25,   3, "Bob Coal"   },
106 	{ "key14", "Paper Clips",    2.09,   3, "Tim Pick"   },
107 };
108 
dbFindRow(rowKey)109 DbTableRow *dbFindRow(rowKey)
110 char *rowKey;
111 {
112 	int i;
113 
114 	for (i = 0; i < dbTableNumRows; i++)
115 		if (!strcmp(rowKey, dbTableRows[i].key))
116 			return &dbTableRows[i];
117 	return 0;
118 }
119 
dbCompareRowKeys(userData,l,r)120 int dbCompareRowKeys(userData, l, r)
121 void *userData;
122 void *l;
123 void *r;
124 {
125 	DbTableRow *dbRow1, *dbRow2;
126 	float u1, u2;
127 
128 	dbRow1 = dbFindRow(*(char **)l);
129 	dbRow2 = dbFindRow(*(char **)r);
130 	switch ((int)userData)
131 	{
132 	case Desc:
133 		return strcmp(dbRow1->desc, dbRow2->desc);
134 	case Price:
135 		u1 = dbRow1->price - dbRow2->price;
136 		if (u1 < 0)
137 			return -1;
138 		else if (u1 == 0)
139 			return 0;
140 		return 1;
141 	case Qty:
142 		return dbRow1->qty - dbRow2->qty;
143 	case UnitPrice:
144 		u1 = dbRow1->price / (float)dbRow1->qty;
145 		u2 = dbRow2->price / (float)dbRow2->qty;
146 		if (u1 < u2)
147 			return -1;
148 		else if (u1 == u2)
149 			return 0;
150 		else
151 			return 1;
152 	case Buyer:
153 		return strcmp(dbRow1->buyer, dbRow2->buyer);
154 	}
155 	return (int)(dbRow1 - dbRow2);
156 }
157 
dbGetRowKeysSorted(sortColumnID)158 char **dbGetRowKeysSorted(sortColumnID)
159 int sortColumnID;
160 {
161 	char **keys;
162 	int i;
163 
164 	keys = (char **)malloc(sizeof(char *) * dbTableNumRows);
165 	for (i = 0; i < dbTableNumRows; i++)
166 		keys[i] = dbTableRows[i].key;
167 	XmLSort(keys, dbTableNumRows, sizeof(char *),
168 		dbCompareRowKeys, (void *)sortColumnID);
169 	return keys;
170 }
171 
172 /* GRID FUNCTIONS */
173 
setRowKeysInGridSorted(grid,sortColumnID)174 void setRowKeysInGridSorted(grid, sortColumnID)
175 Widget grid;
176 int sortColumnID;
177 {
178 	char **keys;
179 	int i;
180 
181 	keys = dbGetRowKeysSorted(sortColumnID);
182 	/* Place a pointer to each row key in each rows userData */
183 	for (i = 0; i < dbTableNumRows; i++)
184 		XtVaSetValues(grid,
185 			XmNrow, i,
186 			XmNrowUserData, (XtPointer)keys[i],
187 			NULL);
188 	free((char *)keys);
189 }
190 
cellSelect(w,clientData,callData)191 void cellSelect(w, clientData, callData)
192 Widget w;
193 XtPointer clientData;
194 XtPointer callData;
195 {
196 	XmLGridCallbackStruct *cbs;
197 	XmLGridColumn column;
198 	XtPointer columnUserData;
199 
200 	cbs = (XmLGridCallbackStruct *)callData;
201 
202 	if (cbs->rowType != XmHEADING)
203 		return;
204 
205 	/* Cancel any edits in progress */
206 	XmLGridEditCancel(w);
207 
208 	column = XmLGridGetColumn(w, cbs->columnType, cbs->column);
209 	XtVaGetValues(w,
210 		XmNcolumnPtr, column,
211 		XmNcolumnUserData, &columnUserData,
212 		NULL);
213 	XtVaSetValues(w,
214 		XmNcolumn, cbs->column,
215 		XmNcolumnSortType, XmSORT_ASCENDING,
216     	NULL);
217 	setRowKeysInGridSorted(w, (DbTableColumnID)columnUserData);
218 	XmLGridRedrawAll(w);
219 }
220 
cellDraw(w,clientData,callData)221 void cellDraw(w, clientData, callData)
222 Widget w;
223 XtPointer clientData;
224 XtPointer callData;
225 {
226 	XmLGridCallbackStruct *cbs;
227 	XmLGridDrawStruct *ds;
228 	XmLGridRow row;
229 	XmLGridColumn column;
230 	XtPointer rowUserData, columnUserData;
231 	DbTableRow *dbRow;
232 	XRectangle cellRect;
233 	int horizMargin, vertMargin;
234 	XmString str;
235 	char buf[50];
236 
237 	cbs = (XmLGridCallbackStruct *)callData;
238 	if (cbs->rowType != XmCONTENT)
239 		return;
240 
241 	ds = cbs->drawInfo;
242 
243 	/* Retrieve userData from the cells row */
244 	row = XmLGridGetRow(w, cbs->rowType, cbs->row);
245 	XtVaGetValues(w,
246 		XmNrowPtr, row,
247 		XmNrowUserData, &rowUserData,
248 		NULL);
249 
250 	/* Retrieve userData from cells column */
251 	column = XmLGridGetColumn(w, cbs->columnType, cbs->column);
252 	XtVaGetValues(w,
253 		XmNcolumnPtr, column,
254 		XmNcolumnUserData, &columnUserData,
255 		NULL);
256 
257 	/* Retrieve the cells value from the database */
258 	dbRow = dbFindRow((char *)rowUserData);
259 	switch ((DbTableColumnID)columnUserData)
260 	{
261 	case Desc:
262 		sprintf(buf, "%s", dbRow->desc);
263 		break;
264 	case Price:
265 		sprintf(buf, "$%4.2f", dbRow->price);
266 		break;
267 	case Qty:
268 		sprintf(buf, "%d", dbRow->qty);
269 		break;
270 	case UnitPrice:
271 		sprintf(buf, "$%4.2f", dbRow->price / (float)dbRow->qty);
272 		break;
273 	case Buyer:
274 		sprintf(buf, "%s", dbRow->buyer);
275 		break;
276 	}
277 
278 	/* Compensate for cell margins */
279 	cellRect = *ds->cellRect;
280 	horizMargin = ds->leftMargin + ds->rightMargin;
281 	vertMargin = ds->topMargin + ds->bottomMargin;
282 	if (horizMargin >= (int)cellRect.width ||
283 		vertMargin >= (int)cellRect.height)
284 		return;
285 	cellRect.x += ds->leftMargin;
286 	cellRect.y += ds->topMargin;
287 	cellRect.width -= horizMargin;
288 	cellRect.height -= vertMargin;
289 
290 	/* Draw the string */
291 	str = XmStringCreateSimple(buf);
292 	if (ds->drawSelected == True)
293 		XSetForeground(XtDisplay(w), ds->gc, ds->selectForeground);
294 	else
295 		XSetForeground(XtDisplay(w), ds->gc, ds->foreground);
296 	XmLStringDraw(w, str, ds->stringDirection, ds->fontList,
297 		ds->alignment, ds->gc, &cellRect, cbs->clipRect);
298 	XmStringFree(str);
299 }
300 
cellEdit(w,clientData,callData)301 void cellEdit(w, clientData, callData)
302 Widget w;
303 XtPointer clientData;
304 XtPointer callData;
305 {
306 	XmLGridCallbackStruct *cbs;
307 	XmLGridRow row;
308 	XmLGridColumn column;
309 	XtPointer rowUserData, columnUserData;
310 	DbTableRow *dbRow;
311 	Widget text;
312 	float f;
313 	int i;
314 	char *value;
315 	Boolean redrawRow;
316 
317 	cbs = (XmLGridCallbackStruct *)callData;
318 
319 	/* For a production version, this function should also
320 	   handle XmCR_EDIT_INSERT by retrieving the current value
321 	   from the database and performing an XmTextSetString on
322 	   the text widget in the grid with that value.  This allows
323 	   a user to hit insert or F2 to modify an existing cell value */
324 
325 	if (cbs->reason != XmCR_EDIT_COMPLETE)
326 		return;
327 
328 	/* Get the value the user just typed in */
329 	XtVaGetValues(w,
330 		XmNtextWidget, &text,
331 		NULL);
332 	value = XmTextGetString(text);
333 	if (!value)
334 		return;
335 
336 	/* Retrieve userData from the cells row */
337 	row = XmLGridGetRow(w, cbs->rowType, cbs->row);
338 	XtVaGetValues(w,
339 		XmNrowPtr, row,
340 		XmNrowUserData, &rowUserData,
341 		NULL);
342 
343 	/* Retrieve userData from cells column */
344 	column = XmLGridGetColumn(w, cbs->columnType, cbs->column);
345 	XtVaGetValues(w,
346 		XmNcolumnPtr, column,
347 		XmNcolumnUserData, &columnUserData,
348 		NULL);
349 
350 	/* Set new value in the database */
351 	redrawRow = False;
352 	dbRow = dbFindRow((char *)rowUserData);
353 	switch ((DbTableColumnID)columnUserData)
354 	{
355 	case Desc:
356 		if ((int)strlen(value) < 20)
357 			strcpy(dbRow->desc, value);
358 		break;
359 	case Price:
360 		if (sscanf(value, "%f", &f) == 1)
361 		{
362 			dbRow->price = f;
363 			redrawRow = True;
364 		}
365 		break;
366 	case Qty:
367 		if (sscanf(value, "%d", &i) == 1)
368 		{
369 			dbRow->qty = i;
370 			redrawRow = True;
371 		}
372 		break;
373 	case Buyer:
374 		if ((int)strlen(value) < 20)
375 			strcpy(dbRow->buyer, value);
376 		break;
377 	}
378 
379 	/* Redraw the row if we need to redisplay unit price */
380 	if (redrawRow == True)
381 		XmLGridRedrawRow(w, cbs->rowType, cbs->row);
382 
383 	/* Set the cellString to NULL - its is set to the value the
384 		   user typed in the text widget at this point */
385 	XtVaSetValues(w,
386 		XmNrow, cbs->row,
387 		XmNcolumn, cbs->column,
388 		XmNcellString, NULL,
389 		NULL);
390 
391 	XtFree(value);
392 }
393 
main(argc,argv)394 main(argc, argv)
395 int argc;
396 char *argv[];
397 {
398 	XtAppContext app;
399 	Widget shell, grid;
400 	XmString str;
401 	int i;
402 
403 	shell =  XtAppInitialize(&app, "Grid6", NULL, 0,
404 		&argc, argv, NULL, NULL, 0);
405 
406 	grid = XtVaCreateManagedWidget("grid",
407 		xmlGridWidgetClass, shell,
408 		XmNhorizontalSizePolicy, XmVARIABLE,
409 		XmNvisibleRows, 10,
410 		XmNvsbDisplayPolicy, XmSTATIC,
411 		XmNselectionPolicy, XmSELECT_NONE,
412 		XmNshadowThickness, 0,
413 		XtVaTypedArg, XmNbackground, XmRString, "#C0C0C0", 8,
414 		XtVaTypedArg, XmNforeground, XmRString, "black", 6,
415 		NULL);
416 	XtAddCallback(grid, XmNcellDrawCallback, cellDraw, NULL);
417 	XtAddCallback(grid, XmNeditCallback, cellEdit, NULL);
418 	XtAddCallback(grid, XmNselectCallback, cellSelect, NULL);
419 
420 	XtVaSetValues(grid,
421 		XmNlayoutFrozen, True,
422 		NULL);
423 
424 	XmLGridAddColumns(grid, XmCONTENT, -1, dbTableNumColumns);
425 
426 	/* Setup columns and column cell defaults based on */
427 	/* database description */
428 	for (i = 0; i < dbTableNumColumns; i++)
429 	{
430 		/* Set the width and the id on the column */
431 		XtVaSetValues(grid,
432 			XmNcolumn, i,
433 			XmNcolumnUserData, (XtPointer)dbTableColumns[i].id,
434 			XmNcolumnWidth, dbTableColumns[i].width,
435 			NULL);
436 
437 		/* Set the default cell alignment and editibility for */
438 		/* cells in the column */
439 		XtVaSetValues(grid,
440 			XmNcellDefaults, True,
441 			XmNcolumn, i,
442 			XmNcellAlignment, dbTableColumns[i].cellAlignment,
443 			XmNcellEditable, dbTableColumns[i].cellEditable,
444 			NULL);
445 	}
446 
447 	/* Add the heading row */
448 	XmLGridAddRows(grid, XmHEADING, -1, 1);
449 
450 	/* Set the column headings */
451 	for (i = 0; i < dbTableNumColumns; i++)
452 	{
453 		/* Set the column heading label */
454 		str = XmStringCreateSimple(dbTableColumns[i].label);
455 		XtVaSetValues(grid,
456 			XmNrowType, XmHEADING,
457 			XmNrow, 0,
458 			XmNcolumn, i,
459 			XmNcellString, str,
460 			NULL);
461 		XmStringFree(str);
462 	}
463 
464 	/* Set cell defaults for content rows */
465 	XtVaSetValues(grid,
466 		XmNcellDefaults, True,
467 		XtVaTypedArg, XmNcellBackground, XmRString, "white", 6,
468 		XmNcellLeftBorderType, XmBORDER_NONE,
469 		XmNcellRightBorderType, XmBORDER_NONE,
470 		XmNcellTopBorderType, XmBORDER_NONE,
471 		XmNcellBottomBorderType, XmBORDER_NONE,
472 		XmNcellMarginLeft, 1,
473 		XmNcellMarginRight, 1,
474 		NULL);
475 
476 	XmLGridAddRows(grid, XmCONTENT, -1, dbTableNumRows);
477 
478 	XtVaSetValues(grid,
479 		XmNlayoutFrozen, False,
480 		NULL);
481 
482 	/* Set the row keys in the rows */
483 	setRowKeysInGridSorted(grid, Desc);
484 
485 	XtRealizeWidget(shell);
486 	XtAppMainLoop(app);
487 }
488