/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* CHKBK.CPP Alan C. Partis */ /* Copyright (C) AP MicroSystems, 1991 */ /* */ /* Description: Contains the client code for the use of Field, Window, */ /* and Check objects to operate a checkbook program. */ /* */ /* Routines: EditField() */ /* FillRegisterWin() */ /* FillXactWin() */ /* GetRegister() */ /* Page() */ /* ReleaseRegister() */ /* RestoreWin() */ /* SaveWin() */ /* StatementBalance() */ /* */ /* Date Initials Comments */ /* ----------------------------------------------------------------------- */ /* 05-14-91 ACP initial creation */ /* 06-09-91 ACP added file I/O and command line parameters */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include #include #include #include #include #include #include #include "chkbk.h" #include #include #include "check.h" // // ----- global variables // word record = 0; // current number of checks in system // updated when register is read from // file and when new checks created void main(int argc, char *argv[]) { byte checkhole; // flag for check record renumbering int done = NO; int key; // input key stroke int paged = FALSE; // flags a Page key processing Field *curfield; // pointer to the active field int rc; XACT xact; char filename[SZ_PATH]; char msgstr[FIELD_MAX]; // scratch space for output string void *winsave; // pointer to saved text window // // Fields in the Help Window // #define SZ_HELP_LIST 18 charField h1 (2, 2, 70, "F1 - View this HELP window. ", FG_HELP, BG_HELP, DISPLAY, (Field *)NULL); charField h2 (2, 3, 70, "F2 - SAVE new work and return for more entries. ", FG_HELP, BG_HELP, DISPLAY, &h1); charField h3 (2, 4, 70, "F3 - QUIT this session WITHOUT saving new entries. ", FG_HELP, BG_HELP, DISPLAY, &h2); charField h4 (2, 5, 70, "F4 - SAVE new work and QUIT to DOS. You are DONE. ", FG_HELP, BG_HELP, DISPLAY, &h3); charField h5 (2, 6, 70, "F5 - reserved for future use. ", FG_HELP, BG_HELP, DISPLAY, &h4); charField h6 (2, 7, 70, "F6 - Calculate the balance as it should appear on your bank statement", FG_HELP, BG_HELP, DISPLAY, &h5); charField h7 (2, 8, 70, "F7 - Retrieve the check currently highlighted in the Browse window. ", FG_HELP, BG_HELP, DISPLAY, &h6); charField h8 (2, 9, 70, "F8 - Delete the current transaction from the check book. ", FG_HELP, BG_HELP, DISPLAY, &h7); charField h9 (2, 10, 70, "F9 - Toggle the \"cleared\" flag for the current transaction. ", FG_HELP, BG_HELP, DISPLAY, &h8); charField h10(2, 11, 70, "F10 - Start a new check transaction. ", FG_HELP, BG_HELP, DISPLAY, &h9); charField h11(2, 12, 70, "Page Up - Previous page in Browse window. ", FG_HELP, BG_HELP, DISPLAY, &h10); charField h12(2, 13, 70, "Page Down - Next page in Browse window. ", FG_HELP, BG_HELP, DISPLAY, &h11); charField h13(2, 14, 70, "Up - Previous check transaction. ", FG_HELP, BG_HELP, DISPLAY, &h12); charField h14(2, 15, 70, "Down - Next check transaction. ", FG_HELP, BG_HELP, DISPLAY, &h13); charField h15(2, 16, 70, "Ctrl Right- Update 'Date' field with previous date. ", FG_HELP, BG_HELP, DISPLAY, &h14); charField h16(2, 17, 70, "Ctrl Left - Update 'Date' field with next date. ", FG_HELP, BG_HELP, DISPLAY, &h15); charField h17(2, 18, 70, " ", FG_HELP, BG_HELP, DISPLAY, &h16); charField h18(2, 19, 70, " press 'Esc' to continue . . . ", FG_HELP, BG_HELP, DISPLAY, &h17); // // initialize 'register' and 'help' listWindows // win_reg.WinInit(4, 17, 77, 23, SZ_REG_ITEMS, FG_REG, BG_REG, &Lr1); win_help.WinInit(5, 4, 76, 23, SZ_HELP_LIST, FG_HELP, BG_HELP, &h1); // //declare client code pointers for Checks // Check *Head = (Check *)NULL, *Browse = (Check *)NULL, *Current = (Check *)NULL; Check *tmpcheck= (Check *)NULL; // // check for proper command line // and print help if necessary // if ((argc == 1) || !(strcmp(argv[1],"?"))) { cout << "\n\tChkBk - computerized check book register\n"; cout << "\tCopyright (C) 1991\n"; cout << "\tby AP MicroSystems\n"; cout << "\n\tUsage: CHKBK \n"; cout << "\n\twhere\t is the name of your check book file\n"; cout << "\n"; exit(1); } else { // // retrieve filename and remove extension if any // strcpy(filename, argv[1]); strtok(filename, "."); // replaces '.' with NULL if it exists } // // display the shareware banner // ShareWareBanner(); win_desk.Display(); gotoxy(12,25); textbackground(BLUE); textcolor(WHITE); cprintf("Copyright (C) AP MicroSystems, 1991. All rights reserved"); GetRegister(filename, Head, Current); // will assign 'Head' and 'Current' // // if input file could not be opened, then Head will // not be assigned. Do not continue if that is // the case. // if (!Head) { exit(1); } // // fill in the display fields and set // initial windows and pointers // FillXactWin(Current, &xact); win_mess.Display(); Browse = Current; FillRegisterWin(Browse, &r1); win_reg.Display(); // // Update the Message window // sprintf(msgstr, "%d", Current->WhoRecord()); MessRec.Update(msgstr); sprintf(msgstr, "%d", record); MessRecs.Update(msgstr); sprintf(msgstr, "%d", Browse->WhoRecord()); MessBrws.Update(msgstr); win_mess.DisplayFields(); win_xact.Display(); curfield = &Number; curfield->Hilight(FG_HILIGHT, BG_HILIGHT); // // process keystrokes // key = EditField(Current, curfield); while (!done) { switch (key) { case PG_UP_KEY: paged = Page(UP, Browse); if (!paged) BEEP(); paged = TRUE; break; case PG_DOWN_KEY: paged = Page(DOWN, Browse); if (!paged) BEEP(); paged = TRUE; break; case TAB_KEY: case ENTER_KEY: curfield->Display(); do { curfield = curfield->Next(); rc = curfield->Hilight(FG_HILIGHT, BG_HILIGHT); } while (rc != PASS); break; case STAB_KEY: curfield->Display(); do { curfield = curfield->Prev(); rc = curfield->Hilight(FG_HILIGHT, BG_HILIGHT); } while (rc != PASS); break; case UP_KEY: tmpcheck = Current->Prev(); if (tmpcheck != NULL) Current = tmpcheck; break; case DOWN_KEY: tmpcheck = Current->Next(); if (tmpcheck != NULL) Current = tmpcheck; break; case F1_KEY: // Help panel // // first, save the window area we are about to // overwrite, display the help window, and // restore the desktop afterward. // _setcursortype(_NOCURSOR); winsave = SaveWin(&win_help); win_help.Display(); key = getch(); while (key != ESC_KEY) { key = getch(); } RestoreWin(&win_help, winsave); _setcursortype(_NORMALCURSOR); break; case F2_KEY: // Save work sprintf(msgstr, PLS_WAIT); MessMess.Update(msgstr); win_mess.DisplayFields(); ReleaseRegister(filename, Head); GetRegister(filename, Head, Current); sprintf(msgstr, ""); MessMess.Update(msgstr); win_mess.DisplayFields(); break; case F3_KEY: // Quit without saving done = YES; break; case F4_KEY: // Save and Quit sprintf(msgstr, PLS_WAIT); MessMess.Update(msgstr); win_mess.DisplayFields(); ReleaseRegister(filename, Head); done = YES; break; case F6_KEY: // Show statement balance StatementBalance(Head, &MessMess); win_mess.DisplayFields(); break; case F7_KEY: // Retrieve Browsed check Current = Browse; break; case F8_KEY: // Delete Check if (record > 1) { // // set tmpcheck to the next check // in the list. If the current check // is the end of the list, then go back. // tmpcheck = Current->Next(); checkhole = TRUE; if (tmpcheck == NULL) { tmpcheck = Current->Prev(); checkhole = FALSE; } // // get rid of the current check and // point to the proper one. // record--; Current->Remove(); delete Current; Current = tmpcheck; // // renumber the record numbers to remove // the "checkhole" - tmpcheck already is // set to equal Current // if (checkhole) { tmpcheck->NewRecord(tmpcheck->WhoRecord() - 1); while ((tmpcheck = tmpcheck->Next()) != NULL) { tmpcheck->NewRecord(tmpcheck->WhoRecord() - 1); } } } break; case F9_KEY: // Toggle check "cleared" flag Current->Clear(); break; case F10_KEY: // New Check // // go to the end of the line and then create // a new check // while ((tmpcheck = Current->Next()) != NULL) Current = tmpcheck; tmpcheck = new Check; tmpcheck->Add(0, NULL_STR, 0.0, 0.0, Current); Current = tmpcheck; Current->NewRecord(++record); curfield = &Number; break; default: BEEP(); } // // Update the register window // if (!paged) Browse = Current; else paged = FALSE; FillRegisterWin(Browse, &r1); win_reg.Update(); // // Update the Message window // sprintf(msgstr, "%d", Current->WhoRecord()); MessRec.Update(msgstr); sprintf(msgstr, "%d", record); MessRecs.Update(msgstr); sprintf(msgstr, "%d", Browse->WhoRecord()); MessBrws.Update(msgstr); win_mess.DisplayFields(); // // Fill the transaction window with the // Current check's numbers // FillXactWin(Current, &xact); win_xact.DisplayFields(); curfield->Hilight(FG_HILIGHT, BG_HILIGHT); if (!done) key = EditField(Current, curfield); } // end of while not done // // reset screen and cursor - then exit // _setcursortype(_NORMALCURSOR); window(1,1,80,25); textbackground(BLUE); clrscr(); } /* end of function main() */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* EditField() Alan C. Partis */ /* Copyright (C) AP MicroSystems, 1991 */ /* */ /* Description: Handles keyboard i/o for the given field. Controls cursor*/ /* location and properly updates the Field data. This function returns*/ /* the extended keystroke scan code that caused an end to the editing */ /* for this field. */ /* */ /* Input: current pointer to current xact check */ /* curfield pointer to current field in xact */ /* */ /* Output: key key that caused end to editing */ /* */ /* Date Initials Comments */ /* ----------------------------------------------------------------------- */ /* 06-30-91 ACP initial creation */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ int EditField(Check *current, Field *curfield) { int key; XACT xact; char buffer[FIELD_MAX]; current->WhoCheck(&xact); key = curfield->Edit(); switch (curfield->WhoID()) { case FID_NUMBER: ((charField *)curfield)->WhoData(buffer); xact.number = atoi(buffer); break; case FID_DATE: ((dateField *)curfield)->WhoData((void *)&(xact.date)); break; case FID_DESC: ((charField *)curfield)->WhoData(xact.desc); wstrunc(xact.desc); // remove trailing white space break; case FID_CHECK: curfield->WhoData((void *)&(xact.check)); break; case FID_DEPOSIT: curfield->WhoData((void *)&(xact.deposit)); break; } current->Edit(xact.number, &(xact.date), xact.desc, xact.check, xact.deposit); return(key); } /* end of function EditField() */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* FillRegisterWin() Alan C. Partis */ /* Copyright (C) AP MicroSystems, 1991 */ /* */ /* Description: Gets the xaction info for a list of checks and updates */ /* the given list window with the data. */ /* */ /* Input: check pointer to targeted check */ /* */ /* Output: headfield head of field list to be updated */ /* */ /* Date Initials Comments */ /* ----------------------------------------------------------------------- */ /* 09-18-91 ACP initial creation */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void FillRegisterWin(Check *check, charField *headfield) { Check *tmp_check; XACT xact; char str[73]; charField *field; int i; int target; // // first we must determine that we start with the earliest check // that will guarentee to fill the entire window. // target = SZ_REG_LIST - 1; tmp_check = check; for (i = 0; i < SZ_REG_LIST; i++) { if (tmp_check->Prev() == NULL) { target = i; i = SZ_REG_LIST; } else { if (i < (SZ_REG_LIST - 1)) tmp_check = tmp_check->Prev(); } } field = headfield; tmp_check->WhoCheck(&xact); for (i = 0; i < SZ_REG_LIST; i++) { // // set the browse strip // if (i == target) { field->NewFgnd(FG_LIST_BROWSE); field->NewBgnd(BG_LIST_BROWSE); } else { field->NewFgnd(FG_LIST); field->NewBgnd(BG_LIST); } sprintf(str, "%-4d³%.2d-%.2d-%.2d³%-32.32s³%7.2f³%7.2f³%7.2f³%1d", xact.number, xact.date.da_mon, xact.date.da_day, xact.date.da_year % 100, xact.desc, xact.check, xact.deposit, xact.balance, xact.cleared); field->Update(str); field = (charField *)(field->Next()); tmp_check = tmp_check->Next(); if (tmp_check != NULL) tmp_check->WhoCheck(&xact); else i = SZ_REG_LIST; } } /* end of function FillRegisterWin() */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* FillXactWin() Alan C. Partis */ /* Copyright (C) AP MicroSystems, 1991 */ /* */ /* Description: Gets the xaction info for the given current Check and */ /* fills the proper fields with updated data. */ /* */ /* Input: Current pointer to current xact check */ /* xact pointer to xact work space */ /* */ /* Output: none */ /* */ /* Date Initials Comments */ /* ----------------------------------------------------------------------- */ /* 05-26-91 ACP initial creation */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void FillXactWin(Check *Current, XACT *xact) { char tmpstr[80]; Current->WhoCheck(xact); if (xact->number == 0) sprintf(tmpstr,""); else sprintf(tmpstr,"%4d",xact->number); Number.Update(tmpstr); Date.Update(&(xact->date)); Desc.Update(xact->desc); Chk.Update((void *)&(xact->check)); Dep.Update((void *)&(xact->deposit)); Bal.Update((void *)&(xact->balance)); if (xact->cleared) Clr.Update("Yes"); else Clr.Update("No"); } /* end of function FillXactWin() */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* GetRegister() Alan C. Partis */ /* Copyright (C) AP MicroSystems, 1991 */ /* */ /* Description: Takes appropriate measures (opens a file) to obtain the */ /* current checkbook register. To do this, new check objects are */ /* created as needed. The Head pointer is assigned to the first check */ /* in the list and the Current pointer is assigned the last. */ /* */ /* Input: none */ /* */ /* Output: Head pointer to first check in list */ /* Current pointer to final check in list */ /* */ /* Date Initials Comments */ /* ----------------------------------------------------------------------- */ /* 06-08-91 ACP initial creation */ /* 11-12-91 ACP update record counter */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void GetRegister(char *filename, Check*& head, Check*& current) { Check *check1; Check *check2; char file[128]; char ch; // temp holder to help check for eof _Date date; strcpy(file, filename); strcat(file, ".chk"); ifstream fp(file); // declare/open input file if (fp) { // // read record from file and create // new check object instance // and assign the next unique // global record number // check1 = new Check; check1->Read(fp, (Check *)NULL); check1->NewRecord(++record); head = check1; fp.get(ch); while (!fp.eof()) { fp.putback(ch); // if not at eof, then put char back check2 = new Check; check2->Read(fp, check1); check1 = check2; check1->NewRecord(++record); fp.get(ch); } current = check1; } else { check1 = new Check; check1->NewRecord(++record); getdate(&date); check1->Edit(0, &date, "", 0.0, 0.0); current = head = check1; } } /* end of function GetRegister() */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Page() Alan C. Partis */ /* Copyright (C) AP MicroSystems, 1991 */ /* */ /* Description: Takes the current Browse pointer and shifts it up or */ /* down depending upon the flag input. If successful in any shifting */ /* TRUE is returned, otherwise FALSE is returned. */ /* */ /* Input: flag indicates Page UP or Page DOWN */ /* */ /* Output: Browse pointer to new Browse pointer */ /* rc TRUE if shifted, FALSE otherwise */ /* */ /* Date Initials Comments */ /* ----------------------------------------------------------------------- */ /* 10-20-91 ACP initial creation */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ int Page(int flag, Check*& Browse) { int i; int rc; rc = FALSE; for (i = 0; i < SZ_REG_LIST; i++) { if (flag == UP) { if (Browse->Prev() != NULL) { Browse = Browse->Prev(); rc = TRUE; } } else { if (Browse->Next() != NULL) { Browse = Browse->Next(); rc = TRUE; } } } return(rc); } /* end of function Page() */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* ReleaseRegister() Alan C. Partis */ /* Copyright (C) AP MicroSystems, 1991 */ /* */ /* Description: Starts with the beginning of the given list, writes each */ /* object to a file, and deallocates their memory. Leaves Head */ /* pointing to NULL. */ /* */ /* Input: Head pointer to beginning of list */ /* */ /* Output: none */ /* */ /* Date Initials Comments */ /* ----------------------------------------------------------------------- */ /* 06-08-91 ACP initial creation */ /* 11-12-91 ACP update record counter */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void ReleaseRegister(char *filename, Check*& head) { Check *tmp1, *tmp2; char file[128]; strcpy(file, filename); strcat(file, ".chk"); ofstream fp(file); // declare/open output file if (fp) { tmp1 = head; head = NULL; // // walk register list, releasing memory // and writing records as we go // while (tmp1->Next() != NULL) { tmp2 = tmp1->Next(); tmp1->Remove(); *tmp1 >> fp; record--; delete tmp1; tmp1 = tmp2; } // // now take care of last one // tmp1->Remove(); *tmp1 >> fp; record--; delete tmp1; } else cout << "Could not find " << file << " for output\n"; } /* end of function ReleaseRegister() */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* RestoreWin() Alan C. Partis */ /* Copyright (C) AP MicroSystems, 1991 */ /* */ /* Description: Restores a given display buffer to the screen at the */ /* coordinates of the given window. Frees memory used by buffer. */ /* */ /* Input: buffer pointer to saved info */ /* win pointer to window for coordinates */ /* */ /* Output: none */ /* */ /* Date Initials Comments */ /* ----------------------------------------------------------------------- */ /* 12-02-91 ACP initial creation */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void RestoreWin(Window *win, void *buffer) { int x1, x2, y1, y2; // // get the coordinates for the window in question, then // restore the buffer // x1 = win->WhoX1(); x2 = win->WhoX2(); y1 = win->WhoY1(); y2 = win->WhoY2(); puttext(x1, y1, x2+2, y2+1, buffer); free(buffer); } /* end of function RestoreWin() */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* SaveWin() Alan C. Partis */ /* Copyright (C) AP MicroSystems, 1991 */ /* */ /* Description: Saves a portion of the Text mode screen into a dynamically*/ /* allocated buffer. Coordinates for the area to be saved are taken */ /* from the given window. */ /* */ /* Input: win pointer to window for coordinates */ /* */ /* Output: buffer pointer to saved info */ /* */ /* Date Initials Comments */ /* ----------------------------------------------------------------------- */ /* 12-02-91 ACP initial creation */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void *SaveWin(Window *win) { int x1, x2, y1, y2; void *buffer; // // get the coordinates for the window in question, then // allocate the buffer // x1 = win->WhoX1(); x2 = win->WhoX2(); y1 = win->WhoY1(); y2 = win->WhoY2(); buffer = malloc(((x2+2) - x1) * ((y2+1) - y1) * 2); gettext(x1, y1, x2+2, y2+1, buffer); return (buffer); } /* end of function SaveWin() */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* ShareWareBanner() Alan C. Partis */ /* Copyright (C) AP MicroSystems, 1991 */ /* */ /* Description: Displays a full screen message about the purpose of this */ /* shareware product. */ /* */ /* Input: none */ /* */ /* Output: none */ /* */ /* Date Initials Comments */ /* ----------------------------------------------------------------------- */ /* 12-06-91 ACP initial creation */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void ShareWareBanner() { textbackground(BLUE); textcolor(LIGHTRED); _setcursortype(_NOCURSOR); clrscr(); cprintf("\r\n"); cprintf(" CheckBook\r\n"); textcolor(WHITE); cprintf("\r\n"); cprintf(" Copyright(C) 1991, AP MicroSystems\r\n"); cprintf("\r\n"); cprintf("\r\n"); cprintf("\r\n"); cprintf(" This product is published as shareware and as such, you are\r\n"); cprintf(" authorized to freely distribute it for evaluation purposes\r\n"); cprintf(" only. If you find that "); textcolor(LIGHTRED); cprintf("CheckBook"); textcolor(WHITE); cprintf(" suits your needs please send\r\n"); cprintf(" $10 to the address below for the latest registered version.\r\n"); cprintf(" This will also entitle you to complete documentation, further\r\n"); cprintf(" upgrade notices, and future product discounts.\r\n"); cprintf("\r\n"); cprintf(" Please help keep the spirit of shareware alive.\r\n"); cprintf("\r\n"); cprintf("\r\n"); cprintf("\r\n"); cprintf(" AP MicroSystems\r\n"); cprintf(" 526 Jaeger Drive\r\n"); cprintf(" Delray Beach, FL 33444\r\n"); cprintf("\r\n"); cprintf("\r\n"); cprintf("\r\n"); cprintf(" press any key to continue . . ."); // // pause to wait for a key to continue // getch(); _setcursortype(_NORMALCURSOR); clrscr(); } /* end of function ShareWareBanner() */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* StatementBalance() Alan C. Partis */ /* Copyright (C) AP MicroSystems, 1991 */ /* */ /* Description: Traverses the list of given checks to derive a current */ /* statement balance given the currently "cleared" and yet to be */ /* "cleared" entries. The result is placed in the given field. */ /* */ /* Input: head pointer to beginning of list */ /* output pointer to field for output string */ /* */ /* Output: none */ /* */ /* Date Initials Comments */ /* ----------------------------------------------------------------------- */ /* 08-23-91 ACP initial creation */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void StatementBalance(Check *head, charField *output) { Check *current; // pointer travelling through list float balance; // running statement balance XACT xact; // current check's transaction info char str[FIELD_MAX]; // scratch space for output string balance = 0.0; // // Must start by getting the current pointer to the tail of the // list to retrieve the current checkbook balance. // current = head; while ((current->Next()) != NULL) current = current->Next(); // // initialize running balance // if (current != NULL) balance = current->Balance(); // // gather data to determine proper balance // while (current != NULL) { current->WhoCheck(&xact); if (xact.cleared == NO) { balance += xact.check; balance -= xact.deposit; } current = current->Prev(); } // // generate output string // sprintf(str, "%s %.2f", STMT_BAL, balance); output->Update(str); } /* end of function StatementBalance() */
Copyright © Thundernet Development Group Inc., 2003. All rights reserved. |