UNIXworkcode

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 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 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 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 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 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 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 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 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