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