=============================== BROWSE.CPP =============================== // File Browser #include #include #include #include "browse.h" #include "filedial.h" static void diag(char *f, ...) // diagnostic { char s[256]; wvsprintf(s, f, &f + 1); MessageBox(NULL, s, "Diagnostic Message", MB_OK); } extern "C" { long far pascal MainWindowProc(HWND, UINT, WPARAM, LPARAM); int far pascal SearchDialogProc(HWND, UINT, WPARAM, LPARAM); int far pascal ListDialogProc(HWND, UINT, WPARAM, LPARAM); int pascal WinMain(HANDLE, HANDLE, LPSTR, int); } const COLORREF BLACK = RGB(255,255,255); const unsigned BLOCK_SIZE = 4*1024; const int BROWSE_FONTS = 4; const int EOF = -1; const int INITIAL_HEIGHT = 300; const int INITIAL_WIDTH = 500; const int MAX_FILESPECS = MAX_DIR+1+MAX_FILE; const int MAX_SEARCH_TEXT = 79; const int SCROLL_RANGE = 100; const int STATUS_HEIGHT = 24; const COLORREF WHITE = RGB(0,0,0); const int MAX_FILES = 10; const int MAX_BLOCKS = MAX_FILES+40; static char title_bar[15+MAX_FILESPECS+1] = "File Browser - (no file)"; typedef struct tagWIDEHIGH { long width; long height; } WIDEHIGH; typedef struct tagPOSITION { long row; long column; long position; } POSITION; typedef struct tagBIGRECT { long left; long top; long right; long bottom; } BIGRECT; typedef struct tagSTATUS { long row; long column; long height; } STATUS; struct block; struct file { long height; POSITION corner; block *current_block; POSITION cursor; char filespecs[MAX_FILESPECS+1]; int font_number; HFILE handle; POSITION mark; BOOL marked; BOOL match_case; file *next; file *previous; WIDEHIGH text; POSITION beginning; void clip_rectangle(RECT &, BIGRECT &); void invalidate_rectangle(BIGRECT &); void center_cursor(void); void error_message(char *); void invalidate_difference(BIGRECT &, BIGRECT &); void marked_rectangle(BIGRECT &, POSITION &); HGLOBAL marked_text(void); long message(UINT, WPARAM, LPARAM); void move_mark(POSITION &); void remove_mark(POSITION &); BOOL next_row(POSITION &); void previous_row(POSITION &); int read_and_advance(POSITION &); int read_character(DWORD); void zoom(void); void scroll(int, int); void scroll_if_necessary(void); void search_again(void); void set_cursor(void); void set_scroll_bars(void); void update_status(void); void update_title(void); static HFONT font[BROWSE_FONTS]; static WIDEHIGH character[BROWSE_FONTS]; static file *current; static HWND window; static RECT display; static HWND status_bar; static STATUS display_status; static int number_of_files; static file *title; static char search_text[MAX_SEARCH_TEXT+1]; static file *dummy; static void close(void); static BOOL open(char *); }; HFONT file::font[BROWSE_FONTS]; WIDEHIGH file::character[BROWSE_FONTS]; file *file::current; file *file::title; RECT file::display; HWND file::window; HWND file::status_bar; STATUS file::display_status; int file::number_of_files; char file::search_text[MAX_SEARCH_TEXT+1]; file *file::dummy; struct block { unsigned count; unsigned char far *data; file *f; HGLOBAL hdata; block *older; long position; block *younger; ~block(void); void insert(void); void remove(void); static block *oldest; static int number_of_blocks; }; block *block::oldest; int block::number_of_blocks; static HINSTANCE application_instance; /*----------------------------------------------------------------------------- This function takes a locked block, inserts it into the age queue as the youngest block, and unlocks it. -----------------------------------------------------------------------------*/ void block::insert(void) { if (oldest == NULL) { oldest = this; younger = older = this; } else { younger = oldest; older = oldest->older; younger->older = older->younger = this; } GlobalUnlock(hdata); data = NULL; } /*----------------------------------------------------------------------------- This function removes a block from the age queue. -----------------------------------------------------------------------------*/ void block::remove(void) { if (oldest == this) oldest = oldest->younger; if (oldest == this) oldest = NULL; else { younger->older = older; older->younger = younger; } } /*----------------------------------------------------------------------------- This function removes a block from the age queue and locks it. It returns TRUE if the operation was successful. If the block had been discarded, it destroys the block and returns FALSE. -----------------------------------------------------------------------------*/ static BOOL remove_and_lock(block *b) { b->remove(); b->data = GlobalLock(b->hdata); if (b->data == NULL) { GlobalUnlock(b->hdata); GlobalFree(b->hdata); delete b; return FALSE; } return TRUE; } /*----------------------------------------------------------------------------- The destructor removes a block from the age queue, if it is in one, and releases its global memory block. -----------------------------------------------------------------------------*/ block::~block() { if (data == NULL) remove(); else GlobalUnlock(hdata); GlobalFree(hdata); number_of_blocks--; } /*----------------------------------------------------------------------------- This function gets a new block, locks it and reads a block into it, if possible. If that is not possible, it destroys the oldest block in the age queue and tries again. The process continues until a block is succesfully obtained or until the age queue is emptied, in which case it returns NULL. -----------------------------------------------------------------------------*/ static block *new_block(file *f, long position) { while (TRUE) { block *b; if (block::number_of_blocks < MAX_BLOCKS && (b = new block) != NULL) { block::number_of_blocks++; b->hdata = GlobalAlloc(GMEM_MOVEABLE | GMEM_DISCARDABLE, BLOCK_SIZE); if (b->hdata != NULL) { b->data = GlobalLock(b->hdata); b->f = f; b->position = position; b->count = _llseek(f->handle, position, 0) == position ? _lread(f->handle, b->data, BLOCK_SIZE) : 0; return b; } b->data = NULL; delete b; } if (block::oldest == NULL) return NULL; delete block::oldest; } } /*----------------------------------------------------------------------------- This function deletes all blocks in the age queue for a particular file, or for all files if f == NULL. -----------------------------------------------------------------------------*/ static void delete_all_blocks(file *f) { while (block::oldest != NULL && (f == NULL || block::oldest->f == f)) delete block::oldest; if (block::oldest != NULL) { block *b = block::oldest->older; while (b != block::oldest) { block *next = b->older; if (b->f == f) delete b; b = next; } } } /*----------------------------------------------------------------------------- This function opens a file, makes it the current file, and returns TRUE if the operation was successful. -----------------------------------------------------------------------------*/ BOOL file::open(char *specs) { file *f = new file; if (f == NULL) return FALSE; memset(f, 0, sizeof(file)); f->height = -1L; f->handle = _lopen(specs, 0); if (f->handle < 0) { delete f; return FALSE; } f->current_block = new_block(f, 0L); if (f->current_block == NULL) { _lclose(f->handle); delete f; return FALSE; } if (number_of_files == 0) f->next = f->previous = f; else { f->next = current->next; f->previous = current; current->next = current->next->previous = f; } current = f; f->font_number = 2; lstrcpy(f->filespecs, specs); number_of_files++; return TRUE; } /*----------------------------------------------------------------------------- This function closes the current file and makes the next file the current file. -----------------------------------------------------------------------------*/ void file::close(void) { _lclose(current->handle); delete current->current_block; delete_all_blocks(current); file *f = current; current = f->next; if (f == current) current = dummy; else { f->previous->next = current; current->previous = f->previous; } delete f; number_of_files--; } /*----------------------------------------------------------------------------- This function destroys and remakes the cursor in a new size. -----------------------------------------------------------------------------*/ static void remake_cursor(int font_number) { DestroyCaret(); CreateCaret(file::window, NULL, 2, file::character[font_number].height); ShowCaret(file::window); } /*----------------------------------------------------------------------------- This function invalidates the part of rectangle r1 that is not inside rectangle r2. -----------------------------------------------------------------------------*/ void file::invalidate_difference(BIGRECT &r1, BIGRECT &r2) { BIGRECT r, i; r = r1; if (r.left < r2.left) { if (r.right <= r.left) { invalidate_rectangle(r); return; } i.top = r.top; i.bottom = r.bottom; i.left = r.left; r.left = i.right = r2.left; invalidate_rectangle(i); } if (r.right > r2.right) { if (r.left >= r2.right) { invalidate_rectangle(r); return; } i.top = r.top; i.bottom = r.bottom; i.right = r.right; r.right = i.left = r2.right; invalidate_rectangle(i); } if (r.top < r2.top) { if (r.bottom <= r.top) { invalidate_rectangle(r); return; } i.left = r.left; i.right = r.right; i.top = r.top; r.top = i.bottom = r2.top; invalidate_rectangle(i); } if (r.bottom > r2.bottom) { if (r.top >= r2.bottom) { invalidate_rectangle(r); return; } i.left = r.left; i.right = r.right; i.bottom = r.bottom; r.bottom = i.top = r2.bottom; invalidate_rectangle(i); } } /*----------------------------------------------------------------------------- This function handles all messages to the main window by passing them to file::message(). -----------------------------------------------------------------------------*/ long far pascal MainWindowProc(HWND h, UINT msg, WPARAM wparm, LPARAM lparm) { file::window = h; return file::current->message(msg, wparm, lparm); } /*----------------------------------------------------------------------------- This function handles all messages to the List dialog box. -----------------------------------------------------------------------------*/ #pragma argsused int far pascal ListDialogProc(HWND h, UINT msg, WPARAM wparm, LPARAM lparm) { switch (msg) { case WM_INITDIALOG: { file *f = file::current; do { char *s = f->filespecs + strlen(f->filespecs); while (s > f->filespecs && s[-1] != '\\' && s[-1] != ':') s--; SendDlgItemMessage(h, LIST_BOX, LB_ADDSTRING, 0, (LPARAM)(LPCSTR) s); SendDlgItemMessage(h, LIST_BOX, LB_SETCURSEL, 0, 0L); f = f->next; } while (f != file::current); return 1; } case WM_COMMAND: switch (wparm) { case LIST_OK: { int i = SendDlgItemMessage(h, LIST_BOX, LB_GETCURSEL, 0, 0L); if (i == LB_ERR) i = 0; file *f = file::current; while (i-- > 0) f = f->next; EndDialog(h, (int) f); return 1; } case LIST_BOX: if (HIWORD(lparm) == LBN_DBLCLK) { int i = SendDlgItemMessage(h, LIST_BOX, LB_GETCURSEL, 0, 0L); if (i == LB_ERR) i = 0; file *f = file::current; while (i-- > 0) f = f->next; EndDialog(h, (int) f); return 1; } } break; case WM_CLOSE: EndDialog(h, (int) file::current); return 0; } return 0; } /*----------------------------------------------------------------------------- This function handles all messages to the Search dialog box. -----------------------------------------------------------------------------*/ #pragma argsused int far pascal SearchDialogProc(HWND h, UINT msg, WPARAM wparm, LPARAM lparm) { switch (msg) { case WM_INITDIALOG: { SetWindowText(h, file::current->filespecs); SendDlgItemMessage(h, SEARCH_TEXT, EM_LIMITTEXT, MAX_SEARCH_TEXT, 0L); SetDlgItemText(h, SEARCH_TEXT, file::current->search_text); SendDlgItemMessage(h, SEARCH_MATCHCASE, BM_SETCHECK, file::current->match_case ? 1 : 0, 0L); return 1; } case WM_COMMAND: switch (wparm) { case SEARCH_SEARCH: { GetDlgItemText(h, SEARCH_TEXT, file::current->search_text, MAX_SEARCH_TEXT + 1); file::current->match_case = SendDlgItemMessage(h, SEARCH_MATCHCASE, BM_GETCHECK, 0, 0L); EndDialog(h, 1); return 1; } case SEARCH_CANCEL: EndDialog(h, 0); return 1; } break; case WM_CLOSE: EndDialog(h, 0); return 0; } return 0; } /*----------------------------------------------------------------------------- This function constructs or reconstructs a font. -----------------------------------------------------------------------------*/ static HFONT construct_font(int height) { LOGFONT f; memset(&f, 0, sizeof(LOGFONT)); f.lfHeight = height; f.lfWeight = FW_MEDIUM; f.lfPitchAndFamily = FIXED_PITCH | FF_ROMAN; f.lfQuality = PROOF_QUALITY; return CreateFontIndirect(&f); } /*----------------------------------------------------------------------------- This function retrieves the character height and width of the currently selelcted font in the given display context. -----------------------------------------------------------------------------*/ static void character_height_and_width(HDC dc, long &height, long &width) { TEXTMETRIC tm; GetTextMetrics(dc, &tm); width = tm.tmAveCharWidth; height = tm.tmHeight + tm.tmExternalLeading; } /*----------------------------------------------------------------------------- This function converts a big rectangle with row and column coordinates into a rectangle in the client area, with pixel coordinates. -----------------------------------------------------------------------------*/ void file::clip_rectangle(RECT &r, BIGRECT &b) { long left, top, right, bottom; left = b.left * character[font_number].width; top = b.top * character[font_number].height; right = b.right * character[font_number].width; bottom = b.bottom * character[font_number].height; r.left = left < 0 ? 0 : left > display.right ? display.right : (int) left; r.top = top < 0 ? 0 : top > display.bottom ? display.bottom : (int) top; r.right = right < 0 ? 0 : right > display.right ? display.right : (int) right; r.bottom = bottom < 0 ? 0 : bottom > display.bottom ? display.bottom : (int) bottom; } /*----------------------------------------------------------------------------- This function invalidates the entire window and reconstitutes it so the cursor will be approximately at the center of the window. -----------------------------------------------------------------------------*/ void file::center_cursor(void) { corner = cursor; unsigned x = (display.bottom / character[font_number].height) / 2; while (corner.row > 0 && x > 0) { previous_row(corner); x--; } corner.column -= (display.right / character[font_number].width) / 2; if (corner.column < 0) corner.column = 0; InvalidateRect(window, NULL, TRUE); set_cursor(); set_scroll_bars(); } /*----------------------------------------------------------------------------- This function pops up a box containing the specified message, together with an OK button and the filespecs. -----------------------------------------------------------------------------*/ void file::error_message(char *s) { MessageBox(window, s, filespecs, MB_OK); } /*----------------------------------------------------------------------------- This function invalidates the part of the client area covered by a big rectangle. -----------------------------------------------------------------------------*/ void file::invalidate_rectangle(BIGRECT &rr) { RECT r; clip_rectangle(r, rr); InvalidateRect(window, &r, TRUE); } /*----------------------------------------------------------------------------- This function determines the relative row and column numbers of the marked area between the mark position and the given cursor position. -----------------------------------------------------------------------------*/ void file::marked_rectangle(BIGRECT &rr, POSITION &cur) { rr.left = mark.column - corner.column; rr.top = mark.row - corner.row; rr.right = cur.column - corner.column; rr.bottom = cur.row - corner.row; if (rr.left > rr.right) { long x = rr.left; rr.left = rr.right; rr.right = x; } if (rr.top > rr.bottom) { long x = rr.top; rr.top = rr.bottom; rr.bottom = x; } rr.bottom++; } /*----------------------------------------------------------------------------- This function allocates a global memory block, extracts the marked text, inserts \r\n at the end of each line, puts the result into the memory block, and returns a memory block handle, or NULL if the memory allocation failed. -----------------------------------------------------------------------------*/ HGLOBAL file::marked_text(void) { POSITION p; p.position = mark.position; p.row = mark.row; p.column = 0; long bottom = cursor.row; if (bottom < p.row) { p.position = cursor.position; p.row = cursor.row; bottom = mark.row; } long left = mark.column; long right = cursor.column; if (right < left) { right = mark.column; left = cursor.column; } DWORD size = (right - left + 2) * (bottom - p.row + 1) + 1; HGLOBAL h = size <= 10*1024 ? GlobalAlloc(GMEM_MOVEABLE, size) : NULL; if (h != NULL) { char far *s = GlobalLock(h); while (p.row <= bottom) { long col = p.column; int c = read_and_advance(p); if (c == EOF || c == '\n') { if (col < left) col = left; while (col < right) { *s++ = ' '; col++; } *s++ = '\r'; *s++ = '\n'; } else if (left <= col && col < right) *s++ = c; if (c == EOF) break; } *s = 0; GlobalUnlock(h); } return h; } /*----------------------------------------------------------------------------- This function handles all messages received by the main window. -----------------------------------------------------------------------------*/ long file::message(UINT msg, WPARAM wparm, LPARAM lparm) { static char new_filespecs[MAX_FILESPECS+1]; static HWND timer = NULL; static DWORD click_timeout = 0; static int ignore_WM_MOUSEMOVE = 0; switch (msg) { case WM_CREATE: { GetClientRect(window, &display); display.bottom -= STATUS_HEIGHT+2; } { unsigned i; for (i = 0; i < BROWSE_FONTS; i++) { font[i] = construct_font(4 << i); HDC dc = GetDC(window); HFONT old_font = SelectObject(dc, font[i]); character_height_and_width(dc, character[i].height, character[i].width); SelectObject(dc, old_font); ReleaseDC(window, dc); } } update_title(); SetScrollRange(window, SB_VERT, 0, SCROLL_RANGE-1, FALSE); SetScrollRange(window, SB_HORZ, 0, SCROLL_RANGE-1, FALSE); status_bar = CreateWindow("STATIC", "row 1, column 1", WS_CHILD | WS_VISIBLE | SS_CENTER, 0, display.bottom+2, display.right, STATUS_HEIGHT, window, -1, application_instance, NULL); display_status.height = -1; DragAcceptFiles(window, TRUE); return 0; case WM_SIZE: display.right = LOWORD(lparm); display.bottom = HIWORD(lparm) - (STATUS_HEIGHT+2); MoveWindow(status_bar, 0, display.bottom+2, display.right, STATUS_HEIGHT, TRUE); return 0; case WM_VSCROLL: if (number_of_files != 0) { switch (wparm) { case SB_LINEUP: if (corner.row > 0) { previous_row(corner); scroll(0, character[font_number].height); set_cursor(); set_scroll_bars(); } break; case SB_LINEDOWN: { if (next_row(corner)) { scroll(0, -character[font_number].height); set_cursor(); set_scroll_bars(); } break; } case SB_PAGEUP: if (corner.row > 0) { int count = display.bottom / character[font_number].height - 1; if (count <= 0) count = 1; if (corner.row < count) count = corner.row; for (int i = 0; i < count; i++) previous_row(corner); scroll(0, character[font_number].height * count); set_cursor(); set_scroll_bars(); } break; case SB_PAGEDOWN: { int count = display.bottom / character[font_number].height - 1; if (count <= 0) count = 1; int i; for (i = 0; i < count; i++) if (!next_row(corner)) break; if (i != 0) { scroll(0, -character[font_number].height * i); set_scroll_bars(); set_cursor(); } } return 0; case SB_THUMBPOSITION: { long row = LOWORD(lparm) * text.height / SCROLL_RANGE; while (corner.row > row) previous_row(corner); while (corner.row < row) next_row(corner); InvalidateRect(window, NULL, TRUE); set_scroll_bars(); set_cursor(); break; } } return 0; } case WM_HSCROLL: if (number_of_files != 0) { switch (wparm) { case SB_LINEUP: if (corner.column > 0) { corner.column--; scroll(character[font_number].width, 0); set_cursor(); set_scroll_bars(); } break; case SB_LINEDOWN: if (corner.column < text.width) { corner.column++; scroll(-character[font_number].width, 0); set_cursor(); set_scroll_bars(); } break; case SB_PAGEUP: if (corner.column > 0) { int count = display.right / character[font_number].width - 1; if (count < 0) count = 1; if (count > corner.column) count = corner.column; corner.column -= count; scroll(character[font_number].width * count, 0); set_cursor(); set_scroll_bars(); } break; case SB_PAGEDOWN: if (corner.column + 1 < text.width) { int count = display.right / character[font_number].width - 1; if (count < 0) count = 1; if (count > text.width - 1 - corner.column) count = text.width - 1 - corner.column; corner.column += count; scroll(-character[font_number].width * count, 0); set_cursor(); set_scroll_bars(); } break; case SB_THUMBPOSITION: corner.column = LOWORD(lparm) * text.width / SCROLL_RANGE; InvalidateRect(window, NULL, TRUE); set_scroll_bars(); set_cursor(); break; } } return 0; case WM_KEYDOWN: if (number_of_files != 0) { BOOL control = GetKeyState(VK_CONTROL) & 0x8000; BOOL shift = GetKeyState(VK_SHIFT) & 0x8000; POSITION old_cursor = cursor; switch (wparm) { case VK_LEFT: if (cursor.column == 0) return 0; cursor.column--; break; case VK_RIGHT: if (cursor.column >= text.width) return 0; cursor.column++; break; case VK_HOME: cursor.column = 0; if (control) { cursor.row = 0; cursor.position = 0; } break; case VK_END: if (control) { while (next_row(cursor)); } cursor.column = text.width; break; case VK_UP: if (cursor.row == 0) return 0; previous_row(cursor); break; case VK_DOWN: next_row(cursor); break; case VK_PRIOR: if (cursor.row == 0) return 0; { int count = display.bottom / character[font_number].height - 1; if (count <= 0) count = 1; while (count > 0 && cursor.row > 0) { previous_row(cursor); count--; } } break; case VK_NEXT: { int count = display.bottom / character[font_number].height - 2; if (!next_row(cursor)) return 0; while (count > 0 && next_row(cursor)) count--; break; } default: return DefWindowProc(window, msg, wparm, lparm); } if (shift) move_mark(old_cursor); else remove_mark(old_cursor); scroll_if_necessary(); } return 0; case WM_LBUTTONDOWN: if (number_of_files != 0) { int row = HIWORD(lparm) / character[font_number].height; int column = (LOWORD(lparm) + character[font_number].width/2) / character[font_number].width; remove_mark(cursor); if (column >= display.right / character[font_number].width) column--; if (row >= display.bottom / character[font_number].height) row--; cursor = corner; while (cursor.row < row + corner.row && cursor.row + 1 < text.height) next_row(cursor); while (cursor.row > row + corner.row && cursor.row > 0) previous_row(cursor); cursor.column = column + corner.column; if (cursor.column > text.width) cursor.column = text.width; set_cursor(); } return 0; case WM_MOUSEMOVE: if (ignore_WM_MOUSEMOVE == 0) { if (number_of_files != 0 && wparm & MK_LBUTTON && GetTickCount() >= click_timeout && text.height != 0) { long row = HIWORD(lparm) / character[font_number].height + corner.row; if (row > text.height - 1) row = text.height - 1; long column = (LOWORD(lparm) + character[font_number].width/2) / character[font_number].width + corner.column; if (column > text.width) column = text.width; if (row != cursor.row || column != cursor.column) { POSITION old_cursor = cursor; while (cursor.row > row) previous_row(cursor); while (cursor.row < row) next_row(cursor); cursor.column = column; move_mark(old_cursor); scroll_if_necessary(); } } } else ignore_WM_MOUSEMOVE--; return 0; case WM_PAINT: { PAINTSTRUCT paint; HDC dc = BeginPaint(window, &paint); if (number_of_files != 0) { HFONT old_font = SelectObject(dc, font[font_number]); POSITION p; p.row = corner.row; p.column = 0; p.position = corner.position; int y = 0; int c = read_and_advance(p); SetTextColor(dc, WHITE); SetBkColor(dc, BLACK); while (y < 2*display.bottom && c != EOF) { long count = corner.column; while (count-- != 0 && c != '\n' && c != EOF) c = read_and_advance(p); int x = 0; while (c != '\n' && c != EOF) { char buffer[80]; int j = 0; while (c != '\n' && c != EOF && j < sizeof(buffer)) { buffer[j++] = c; c = read_and_advance(p); } if (y /* + character[font_number].height */ < display.bottom && x < display.right) { TextOut(dc, x, y, buffer, j); } x += character[font_number].width * j; } y += character[font_number].height; if (c == '\n') c = read_and_advance(p); } // apply mark, if any if (marked) { BIGRECT rr; marked_rectangle(rr, cursor); RECT r; clip_rectangle(r, rr); InvertRect(dc, &r); } // draw line between text and status bar { HPEN old_pen = SelectObject(dc, GetStockObject(BLACK_PEN)); MoveTo(dc, 0, display.bottom+1); LineTo(dc, display.right, display.bottom+1); SelectObject(dc, old_pen); } SelectObject(dc, old_font); } EndPaint(window, &paint); // update cursor and status bar if necessary set_cursor(); // change title bar if necessary update_title(); return 0; } case WM_INITMENUPOPUP: if (HIWORD(lparm) == 0) { switch (LOWORD(lparm)) { case 0: EnableMenuItem((HMENU) wparm, MENU_OPEN, MF_BYCOMMAND | (number_of_files < MAX_FILES ? MF_ENABLED : MF_GRAYED)); EnableMenuItem((HMENU) wparm, MENU_CLOSE, MF_BYCOMMAND | (number_of_files != 0 ? MF_ENABLED : MF_GRAYED)); EnableMenuItem((HMENU) wparm, MENU_CLOSEALL, MF_BYCOMMAND | (number_of_files != 0 ? MF_ENABLED : MF_GRAYED)); EnableMenuItem((HMENU) wparm, MENU_LIST, MF_BYCOMMAND | (number_of_files > 1 ? MF_ENABLED : MF_GRAYED)); EnableMenuItem((HMENU) wparm, MENU_NEXT, MF_BYCOMMAND | (number_of_files > 1 ? MF_ENABLED : MF_GRAYED)); EnableMenuItem((HMENU) wparm, MENU_PREVIOUS, MF_BYCOMMAND | (number_of_files > 1 ? MF_ENABLED : MF_GRAYED)); break; case 1: EnableMenuItem((HMENU) wparm, MENU_BEGINNING, MF_BYCOMMAND | (number_of_files != 0 ? MF_ENABLED : MF_GRAYED)); EnableMenuItem((HMENU) wparm, MENU_END, MF_BYCOMMAND | (number_of_files != 0 && !(marked && mark.column != cursor.column) && beginning.column != cursor.column ? MF_ENABLED : MF_GRAYED)); EnableMenuItem((HMENU) wparm, MENU_CLIPBOARD, MF_BYCOMMAND | (number_of_files != 0 && marked && mark.column != cursor.column ? MF_ENABLED : MF_GRAYED)); EnableMenuItem((HMENU) wparm, MENU_PRINTER, MF_BYCOMMAND | (number_of_files != 0 && marked && mark.column != cursor.column ? MF_ENABLED : MF_GRAYED)); break; case 2: EnableMenuItem((HMENU) wparm, MENU_SEARCH, MF_BYCOMMAND | (number_of_files != 0 ? MF_ENABLED : MF_GRAYED)); EnableMenuItem((HMENU) wparm, MENU_AGAIN, MF_BYCOMMAND | (search_text[0] != 0 ? MF_ENABLED : MF_GRAYED)); EnableMenuItem((HMENU) wparm, MENU_ZOOMIN, MF_BYCOMMAND | (number_of_files != 0 && font_number < BROWSE_FONTS - 1 ? MF_ENABLED : MF_GRAYED)); EnableMenuItem((HMENU) wparm, MENU_ZOOMOUT, MF_BYCOMMAND | (number_of_files != 0 && font_number > 0 ? MF_ENABLED : MF_GRAYED)); return 0; } } break; case WM_DROPFILES: { BOOL too_many_files = FALSE; int n = DragQueryFile((HANDLE) wparm, 0xFFFF, NULL, 0); int i; char specs[MAX_FILESPECS+1]; for (i = 0; i < n; i++) { DragQueryFile((HANDLE) wparm, i, specs, sizeof(specs)); if (number_of_files < MAX_FILES) { file *f = this; AnsiUpper(new_filespecs); for (int j = 0; j < number_of_files; j++) { if (strcmp(specs, f->filespecs) == 0) { if (f != this) { current = f; remake_cursor(f->font_number); InvalidateRect(window, NULL, TRUE); } break; } f = f->next; } if (j == number_of_files) file::open(specs); } else too_many_files = TRUE; } DragFinish((HANDLE) wparm); if (number_of_files == MAX_FILES) DragAcceptFiles(window, FALSE); update_title(); if (too_many_files) error_message("Too many files"); return 0; } case WM_COMMAND: switch(wparm) { case MENU_SEARCH: { DLGPROC proc = (DLGPROC) MakeProcInstance((FARPROC) SearchDialogProc, application_instance); BOOL cancelled = !DialogBox(application_instance, "BROWSE_SEARCH", window, proc); FreeProcInstance((FARPROC) proc); if (cancelled) return 0; // else fall through to MENU_AGAIN } case MENU_AGAIN: search_again(); return 0; case MENU_LIST: { DLGPROC proc = (DLGPROC) MakeProcInstance((FARPROC) ListDialogProc, application_instance); file *f = (file *) DialogBox(application_instance, "BROWSE_LIST", window, proc); FreeProcInstance((FARPROC) proc); if (f != this) { current = f; remake_cursor(f->font_number); InvalidateRect(window, NULL, TRUE); } ignore_WM_MOUSEMOVE = 3; //click_timeout = GetTickCount() + 100; return 0; } case MENU_ZOOMIN: font_number++; zoom(); return 0; case MENU_ZOOMOUT: font_number--; zoom(); return 0; case MENU_OPEN: { if (number_of_files == 0) { GetWindowsDirectory(new_filespecs, MAX_DIR); new_filespecs[3] = 0; } else lstrcpy(new_filespecs, filespecs); unsigned n = strlen(new_filespecs); while (n > 0 && new_filespecs[n-1] != '\\' && new_filespecs[n-1] != ':') { n--; } new_filespecs[n] = 0; if (file_dialog(window, "File Browser", new_filespecs, sizeof(new_filespecs), "All Files (*.*)\0*.*\0", "", FD_OPEN + FD_MUSTEXIST)) { unsigned i; file *f = this; AnsiUpper(new_filespecs); for (i = 0; i < number_of_files; i++) { if (strcmp(new_filespecs, f->filespecs) == 0) { if (f != this) { current = f; remake_cursor(f->font_number); InvalidateRect(window, NULL, TRUE); } return 0; } f = f->next; } if (number_of_files == MAX_FILES - 1) DragAcceptFiles(window, FALSE); ignore_WM_MOUSEMOVE = 3; timer = SetTimer(window, NULL, 200, NULL); } else new_filespecs[0] = 0; return 0; } case MENU_NEXT: current = next; remake_cursor(next->font_number); InvalidateRect(window, NULL, TRUE); return 0; case MENU_PREVIOUS: current = previous; remake_cursor(previous->font_number); InvalidateRect(window, NULL, TRUE); return 0; case MENU_CLOSE: file::close(); if (number_of_files == MAX_FILES - 1) DragAcceptFiles(window, TRUE); InvalidateRect(window, NULL, TRUE); if (number_of_files == 0) update_title(); return 0; case MENU_CLOSEALL: { int n = number_of_files; while (number_of_files != 0) file::close(); if (n == MAX_FILES) DragAcceptFiles(window, TRUE); InvalidateRect(window, NULL, TRUE); update_title(); return 0; } case MENU_EXIT: DestroyWindow(window); return 0; case MENU_CLIPBOARD: { HGLOBAL h = marked_text(); if (h != NULL) { if (OpenClipboard(window)) { EmptyClipboard(); SetClipboardData(CF_TEXT, h); CloseClipboard(); } else { error_message("Clipboard access denied"); GlobalFree(h); } } else error_message("Marked block is too large"); return 0; } case MENU_PRINTER: { char info[80]; char *device = info; char *driver = NULL; char *port = NULL; GetProfileString("windows", "device", "", info, sizeof(info)); { char *s = info; while (*s != 0) { if (*s == ',') { *s++ = 0; while (*s == ' ') s++; if (driver == NULL) driver = s; else { port = s; break; } } else s++; } } HDC dc = CreateDC(driver, device, port, NULL); if (dc != NULL) { long character_height; long character_width; DOCINFO di; HFONT printer_font; // get information about default font character_height_and_width(dc, character_height, character_width); // scale height according to display font switch (font_number) { case 0: character_height >>= 2; break; case 1: character_height >>= 1; break; case 3: character_height <<= 1; break; } if (character_height < 4) character_height = 4; HFONT old_font = SelectObject(dc, construct_font(character_height)); di.cbSize = sizeof(DOCINFO); di.lpszDocName = title_bar; di.lpszOutput = NULL; character_height_and_width(dc, character_height, character_width); StartDoc(dc, &di); StartPage(dc); POSITION p; p.position = mark.position; p.row = mark.row; p.column = 0; long bottom = cursor.row; if (bottom < p.row) { p.position = cursor.position; p.row = cursor.row; bottom = mark.row; } long left = mark.column; long right = cursor.column; if (right < left) { right = mark.column; left = cursor.column; } int y = 0; while (p.row <= bottom) { char buffer[80]; int i = 0; int x = 0; int c = read_and_advance(p); while (c != EOF && c != '\n') { if (left < p.column && p.column <= right) { buffer[i++] = c; if (i == sizeof(buffer)) { TextOut(dc, x, y, buffer, sizeof(buffer)); i = 0; x += character_width * sizeof(buffer); } } c = read_and_advance(p); } if (i > 0) TextOut(dc, x, y, buffer, i); y += character_height; if (c == EOF) break; } EndPage(dc); EndDoc(dc); SelectObject(dc, old_font); DeleteDC(dc); } else error_message("Printer error"); return 0; } case MENU_BEGINNING: remove_mark(cursor); beginning = cursor; return 0; case MENU_END: { BIGRECT new_mark; mark = beginning; marked = TRUE; marked_rectangle(new_mark, cursor); invalidate_rectangle(new_mark); return 0; } case MENU_CONTENTS: WinHelp(window, "BROWSE.HLP", HELP_INDEX, 0); return 0; case MENU_HELPSEARCH: WinHelp(window, "BROWSE.HLP", HELP_PARTIALKEY, (DWORD) (char far *) ""); return 0; case MENU_ABOUT: error_message( "File Browser 1.0.\n" "Not copyrighted, no restrictions on use.\n" " from\n" "Philip J. Erdelsky\n" "pje@acm.org\n" "http://www.alumni.caltech.edu/~pje/\n" "February 21, 1997"); return 0; } break; case WM_TIMER: if (new_filespecs[0] != 0) { file::open(new_filespecs); new_filespecs[0] = 0; } else { KillTimer(window, timer); timer = NULL; remake_cursor(font_number); InvalidateRect(window, NULL, TRUE); } return 0; case WM_KILLFOCUS: DestroyCaret(); return 0; case WM_SETFOCUS: if (number_of_files == 0) { CreateCaret(window, NULL, 2, character[2].height); ShowCaret(window); SetCaretPos(-character[2].width, -character[2].height); } else { //DestroyCaret(); CreateCaret(window, NULL, 2, character[font_number].height); ShowCaret(window); set_cursor(); } InvalidateRect(window, NULL, TRUE); return 0; case WM_CLOSE: DestroyWindow(window); return 0; case WM_DESTROY: DragAcceptFiles(window, FALSE); DestroyCaret(); { unsigned i; for (i = 0; i < BROWSE_FONTS; i++) DeleteObject(font[i]); } delete_all_blocks(NULL); if (timer != NULL) KillTimer(window, timer); PostQuitMessage(0); return 0; } return DefWindowProc(window, msg, wparm, lparm); } /*----------------------------------------------------------------------------- This function moves the mark from the old position to its current position, taking care to invalidate only the parts of the client area that have actually changed in the process. -----------------------------------------------------------------------------*/ void file::move_mark(POSITION &old_cursor) { BIGRECT new_mark; marked_rectangle(new_mark, cursor); if (marked) { BIGRECT old_mark; marked_rectangle(old_mark, old_cursor); invalidate_difference(old_mark, new_mark); invalidate_difference(new_mark, old_mark); } else { mark = old_cursor; marked = TRUE; invalidate_rectangle(new_mark); } } /*----------------------------------------------------------------------------- This function starts at the beginning of a row and finds the position of the beginning of the next following row. If there is no next following row, it restores the position and returns FALSE; -----------------------------------------------------------------------------*/ BOOL file::next_row(POSITION &p) { if (read_character(p.position) != EOF) { int c; POSITION pp; pp.row = p.row; pp.position = p.position; pp.column = 0; while (TRUE) { switch (read_and_advance(pp)) { case '\n': if (read_character(pp.position) == EOF) return FALSE; p.row = pp.row; p.position = pp.position; return TRUE; case EOF: return FALSE; } } } return FALSE; } /*----------------------------------------------------------------------------- This function starts at the beginning of a row and finds the position of the beginning of the next preceding row. -----------------------------------------------------------------------------*/ void file::previous_row(POSITION &p) { if (--p.row == 0) p.position = 0; else { p.position--; while (read_character(--p.position) != '\n'); p.position++; } } /*----------------------------------------------------------------------------- This function reads a character from a file and then advances the position, row and column markers to the next following position. -----------------------------------------------------------------------------*/ int file::read_and_advance(POSITION &position) { int c; do c = read_character(position.position++); while (c == '\r'); if (c == EOF) { if (position.column != 0 && ++position.row > text.height) text.height = position.row; position.position--; height = text.height; } else if (c == '\n') { position.column = 0; if (++position.row > text.height) text.height = position.row; } else { if (++position.column > text.width) text.width = position.column; } return c; } /*----------------------------------------------------------------------------- This function reads a character from a file. -----------------------------------------------------------------------------*/ int file::read_character(DWORD pos) { block *b = current_block; DWORD index = pos - b->position; if (index < (DWORD) BLOCK_SIZE) return (unsigned) index < b->count ? b->data[(unsigned) index] : EOF; b->insert(); block *bb; for (bb = b->older; bb != b; bb = bb->older) { index = pos - bb->position; if (index < (DWORD) BLOCK_SIZE) { if (!remove_and_lock(bb)) break; current_block = bb; return (unsigned) index < bb->count ? bb->data[(unsigned) index] : EOF; } } bb = new_block(this, pos & (- (long) BLOCK_SIZE)); if (bb == NULL) diag("insufficient memory"); current_block = bb; index = pos - bb->position; return (unsigned) index < bb->count ? bb->data[(unsigned) index] : EOF; } /*----------------------------------------------------------------------------- This function removes the mark. -----------------------------------------------------------------------------*/ void file::remove_mark(POSITION &cur) { if (marked) { BIGRECT rr; marked_rectangle(rr, cur); invalidate_rectangle(rr); marked = FALSE; } } /*----------------------------------------------------------------------------- This function scrolls the window. -----------------------------------------------------------------------------*/ void file::scroll(int dx, int dy) { display.bottom++; ScrollWindow(window, dx, dy, &display, &display); display.bottom--; } /*----------------------------------------------------------------------------- This function scrolls the window, if necessary, so the cursor will remain visible. -----------------------------------------------------------------------------*/ void file::scroll_if_necessary(void) { long count = (int) cursor.row - (int) corner.row; if (count < 0) { if (count > -display.bottom / character[font_number].height) scroll(0, -count * character[font_number].height); else InvalidateRect(window, NULL, TRUE); do previous_row(corner); while (++count != 0); } else { count -= (display.bottom / character[font_number].height - 1); if (count > 0) { if (count < display.bottom / character[font_number].height) scroll(0, -count * character[font_number].height); else InvalidateRect(window, NULL, TRUE); do next_row(corner); while (--count != 0); } } count = (int) cursor.column - (int) corner.column; if (count < 0) { if (count > -display.right / character[font_number].width) scroll(-count * character[font_number].width, 0); else InvalidateRect(window, NULL, TRUE); corner.column = cursor.column; } else { count -= (display.right / character[font_number].width - 1); if (count > 0) { if (count < display.right / character[font_number].width) scroll(-count * character[font_number].width, 0); else InvalidateRect(window, NULL, TRUE); corner.column += count; } } set_cursor(); set_scroll_bars(); } /*----------------------------------------------------------------------------- This function searches for the specified text, starting at the cursor position, and repositions the cursor to the end of the text if it it is found. -----------------------------------------------------------------------------*/ void file::search_again(void) { if (search_text[0] != 0) { POSITION p = cursor; long beginning_of_line = cursor.position; char buffer[MAX_SEARCH_TEXT]; int c = read_character(p.position); unsigned length = strlen(search_text); p.column = 0; while (p.row == cursor.row && p.column < cursor.column) { c = read_and_advance(p); if (p.column == 0) beginning_of_line = p.position; } { for (unsigned i = 0; i < length; i++) { buffer[i] = c = read_and_advance(p); if (p.column == 0) beginning_of_line = p.position; } } while (c != EOF) { if (match_case && memcmp(search_text, buffer, length) == 0 || !match_case && memicmp(search_text, buffer, length) == 0) { cursor.row = p.row; cursor.column = p.column; cursor.position = beginning_of_line; if (cursor.row < corner.row || cursor.row >= corner.row + display.bottom / character[font_number].height || cursor.column < corner.column || cursor.column >= corner.column + display.right / character[font_number].width) { center_cursor(); } else set_cursor(); set_scroll_bars(); return; } for (unsigned i = 0; i < length-1; i++) buffer[i] = buffer[i+1]; buffer[i] = c = read_and_advance(p); if (p.column == 0) beginning_of_line = p.position; } error_message("Search string not found"); set_scroll_bars(); } } /*----------------------------------------------------------------------------- This function moves the cursor to the position specified in the file structure. -----------------------------------------------------------------------------*/ void file::set_cursor(void) { long row = (cursor.row - corner.row) * character[font_number].height; long column = (cursor.column - corner.column) * character[font_number].width; SetCaretPos(column < 0 ? -character[font_number].width : column > display.right ? display.right + 1 : (int) column, row < 0 ? -character[font_number].height : row > display.bottom - character[font_number].height ? display.bottom + 2 + STATUS_HEIGHT : (int) row); update_status(); } /*----------------------------------------------------------------------------- This function sets the scroll bar runners. -----------------------------------------------------------------------------*/ void file::set_scroll_bars(void) { SetScrollPos(window, SB_HORZ, text.width > 0 ? (corner.column * SCROLL_RANGE) / text.width : 0, TRUE); SetScrollPos(window, SB_VERT, text.height > 0 ? (corner.row * SCROLL_RANGE) / text.height : 0, TRUE); } /*----------------------------------------------------------------------------- This function updates the status bar if the information on it has changed. -----------------------------------------------------------------------------*/ void file::update_status(void) { if (cursor.row != display_status.row || cursor.column != display_status.column || height != display_status.height) { char caption[100]; //DestroyWindow(status_bar); if (number_of_files != 0) { wsprintf(caption, "cursor in row %ld, column %ld", cursor.row+1, cursor.column+1); if (height >= 0) wsprintf(caption + strlen(caption), " *** height = %ld, width = %ld", height, text.width); } else caption[0] = 0; SendMessage(status_bar, WM_SETTEXT, 0, (LPARAM) (char far *) caption); //status_bar = CreateWindow("STATIC", caption, // WS_CHILD | WS_VISIBLE | SS_CENTER, // 0, display.bottom+2, display.right, STATUS_HEIGHT, window, // -1, application_instance, NULL); display_status.row = cursor.row; display_status.column = cursor.column; display_status.height = height; } } void file::update_title(void) { if (title != current) { lstrcpy(title_bar + 15, current->filespecs); SetWindowText(window, title_bar); title = current; } } /*----------------------------------------------------------------------------- This function reconstructs the window after a zoom operation. The window is positioned so the cursor is as near the center of the screen as possible. -----------------------------------------------------------------------------*/ void file::zoom(void) { remake_cursor(font_number); center_cursor(); } /*----------------------------------------------------------------------------- The WinMain function accepts a file specification from the command line. If there is no file specification on the command line, it prompts the user for one. -----------------------------------------------------------------------------*/ int pascal WinMain(HANDLE inst, HANDLE prev_inst, LPSTR comline, int show) { application_instance = inst; file::current = file::dummy = new file; memset(file::current, 0, sizeof(file)); lstrcpy(file::current->filespecs, "(no file)"); file::current->font_number = 2; { char filespecs[MAX_FILESPECS+1]; int i = 0; int c; while ((c = (*comline++ & 0xFF)) != 0) { if (c != ' ' && c != '\t' && i < MAX_FILESPECS) filespecs[i++] = c; } if (i > 0) { filespecs[i] = 0; AnsiUpper(filespecs); file::open(filespecs); } } if (prev_inst == NULL) { WNDCLASS window; window.cbClsExtra = 0; window.hbrBackground = GetStockObject(WHITE_BRUSH); window.hCursor = LoadCursor(NULL, IDC_ARROW); window.hIcon = LoadIcon(inst, "BROWSE_ICON"); window.hInstance = inst; window.lpfnWndProc = MainWindowProc; window.lpszClassName = "FILEBROWSER"; window.lpszMenuName = "BROWSE_MENU"; window.style = CS_HREDRAW | CS_VREDRAW; RegisterClass(&window); } { HWND handle = CreateWindow ( "FILEBROWSER", // class name title_bar, // caption WS_OVERLAPPED | WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU, 10, // x position 10, // y position INITIAL_WIDTH, // x size INITIAL_HEIGHT, // y size NULL, // parent window NULL, // menu inst, // program instance NULL // parameters ); ShowWindow(handle, show); HANDLE accelerators = LoadAccelerators(inst, "BROWSE_ACCEL"); MSG message; while (GetMessage(&message, 0, 0, 0)) { if (!TranslateAccelerator(handle, accelerators, &message)) { TranslateMessage(&message); DispatchMessage(&message); } } return message.wParam; } } =============================== BROWSE.DEF =============================== NAME BROWSE DESCRIPTION 'File Browser' EXETYPE WINDOWS CODE PRELOAD MOVEABLE DATA PRELOAD MOVEABLE MULTIPLE STACKSIZE 8000 HEAPSIZE 16000 EXPORTS MainWindowProc ListDialogProc SearchDialogProc =============================== BROWSE.H =============================== #define MENU_OPEN 100 #define MENU_CLOSE 101 #define MENU_CLOSEALL 102 #define MENU_NEXT 103 #define MENU_PREVIOUS 104 #define MENU_LIST 105 #define MENU_EXIT 106 #define MENU_BEGINNING 107 #define MENU_END 108 #define MENU_CLIPBOARD 109 #define MENU_PRINTER 110 #define MENU_SEARCH 111 #define MENU_AGAIN 112 #define MENU_ZOOMIN 113 #define MENU_ZOOMOUT 114 #define MENU_CONTENTS 115 #define MENU_HELPSEARCH 116 #define MENU_ABOUT 117 #define SEARCH_TEXT 200 #define SEARCH_MATCHCASE 201 #define SEARCH_SEARCH 202 #define SEARCH_CANCEL 203 #define LIST_OK 300 #define LIST_BOX 301 =============================== BROWSE.HPJ =============================== [OPTIONS] TITLE=File Browser 1.0 Help COMPRESS=TRUE [FILES] browse.rtf [CONFIG] CreateButton("btn_exit", "E&xit", "Exit()") =============================== BROWSE.RC =============================== #include "browse.h" BROWSE_ICON ICON { '00 00 01 00 01 00 20 20 02 00 00 00 00 00 30 01' '00 00 16 00 00 00 28 00 00 00 20 00 00 00 40 00' '00 00 01 00 01 00 00 00 00 00 00 01 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 FF FF FF 00 00 00 00 00 7F FF FF FE 40 00' '00 02 5F FF FF FA 50 00 00 0A 57 FF FF EA 54 00' '00 2A 55 FF FF AA 55 00 00 AA 55 7F FE AA 55 40' '02 AA 55 5F FA AA 55 50 0A AA 55 57 EA AA 55 54' '2A AA 55 55 AA AA 55 55 AA AA 55 54 2A AA 55 57' 'EA AA 55 50 0A AA 55 5F FA AA 55 40 02 AA 55 7F' 'FE AA 55 00 00 AA 55 FF FF AA 54 00 00 2A 57 FF' 'FF EA 50 00 00 0A 5F FF FF FA 40 00 00 02 7F FF' 'FF FE 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00' } BROWSE_MENU MENU BEGIN POPUP "&File" BEGIN MENUITEM "&Open\tO", MENU_OPEN MENUITEM "&Close", MENU_CLOSE MENUITEM "Close &All", MENU_CLOSEALL MENUITEM SEPARATOR MENUITEM "&Next\tTab", MENU_NEXT MENUITEM "&Previous\tShift Tab", MENU_PREVIOUS MENUITEM "&List\tL", MENU_LIST MENUITEM SEPARATOR MENUITEM "E&xit", MENU_EXIT END POPUP "&Copy" BEGIN MENUITEM "&Beginning\tB", MENU_BEGINNING MENUITEM "&End\tE", MENU_END MENUITEM SEPARATOR MENUITEM "&Clipboard\tC", MENU_CLIPBOARD MENUITEM "&Printer", MENU_PRINTER END POPUP "&View" BEGIN MENUITEM "&Search\tS", MENU_SEARCH MENUITEM "&Again\tF3", MENU_AGAIN MENUITEM "Zoom &In", MENU_ZOOMIN MENUITEM "Zoom &Out", MENU_ZOOMOUT END POPUP "&Help" BEGIN MENUITEM "&Contents\tF1", MENU_CONTENTS MENUITEM "&Search\tShift F1", MENU_HELPSEARCH MENUITEM "&About", MENU_ABOUT END END BROWSE_ACCEL ACCELERATORS BEGIN VK_O, MENU_OPEN, VIRTKEY VK_L, MENU_LIST, VIRTKEY VK_B, MENU_BEGINNING, VIRTKEY VK_E, MENU_END, VIRTKEY VK_C, MENU_CLIPBOARD, VIRTKEY VK_S, MENU_SEARCH, VIRTKEY VK_TAB, MENU_NEXT, VIRTKEY VK_TAB, MENU_PREVIOUS, VIRTKEY, SHIFT VK_F1, MENU_CONTENTS, VIRTKEY VK_F1, MENU_HELPSEARCH, VIRTKEY, SHIFT VK_F3, MENU_AGAIN, VIRTKEY END BROWSE_SEARCH DIALOG 6, 15, 177, 108 STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "File Browser - Search for Text" FONT 8, "MS Sans Serif" BEGIN EDITTEXT SEARCH_TEXT, 14, 9, 149, 12 CHECKBOX "Match case", SEARCH_MATCHCASE, 21, 35, 72, 12, BS_AUTOCHECKBOX | WS_TABSTOP DEFPUSHBUTTON "Search", SEARCH_SEARCH, 117, 35, 50, 24 PUSHBUTTON "Cancel", SEARCH_CANCEL, 117, 75, 50, 24 END BROWSE_LIST DIALOG 34, 20, 66, 145 STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "File Browser" FONT 8, "MS Sans Serif" BEGIN LISTBOX LIST_BOX, 9, 9, 50, 99, LBS_NOTIFY | WS_TABSTOP | WS_VISIBLE | WS_BORDER DEFPUSHBUTTON "OK", LIST_OK, 9, 114, 50, 24 END =============================== BROWSE.RTF =============================== {\rtf1\ansi \deff0\deflang1024{\fonttbl{\f0\froman Times New Roman;}{\f1\froman Symbol;}{\f2\fswiss Arial;}{\f3\fmodern Roman 10cpi;}{\f4\fswiss Helv;}}{\colortbl;\red0\green0\blue0;\red0\green0\blue255; \red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue127;\red0\green127\blue127;\red0\green127\blue0;\red127\green0\blue127;\red127\green0\blue0; \red127\green127\blue0;\red127\green127\blue127;\red192\green192\blue192;}{\stylesheet{\s244 \f3\fs16\up6\lang1033 \sbasedon0\snext0 footnote reference;}{\s245 \f3\fs20\lang1033 \sbasedon0\snext245 footnote text;}{\f3\fs20\lang1033 \snext0 Normal;}}{\info {\author Philip Erdelsky}{\creatim\yr1993\mo12\dy17\hr11\min4}{\version1}{\edmins483}{\nofpages0}{\nofwords65536}{\nofchars65536}{\vern16433}}\paperw12240\paperh15840\margl1800\margr1800\margt1440\margb1440\gutter0 \widowctrl\ftnbj \sectd \linex0\endnhere \pard\plain \f3\fs20\lang1033 {\fs16\up6 #{\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 #} Contents}}{\fs16\up6 ${\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 $} Contents}}{\fs16\up6 K{\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 K} Contents;Table of Contents}}{\fs16\up6 +{\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 +} X:001}}{\plain \b\f4\lang1033 File}{\plain \b\f4\lang1033 Browser 1.0}{\f4 \par }{\f4 \par }\pard \li720 {\strike\f4 Overview}{\v\f4 Overview}{\f4 \par }{\strike\f4 Open a File}{\v\f4 Open}{\f4 \par }{\strike\f4 Close a File}{\v\f4 Close}{\f4 \par }{\strike\f4 Switch to Another Open File}{\v\f4 Switch}{\f4 \par }{\strike\f4 Scroll the Window}{\v\f4 Scroll}{\f4 \par }{\strike\f4 Move the Text Cursor}{\v\f4 Move}{\f4 \par }{\strike\f4 Mark a Block}{\v\f4 Mark}{\f4 \par }{\strike\f4 Copy a Block to the Clipboard}{\v\f4 Clipboard}{\f4 \par }{\strike\f4 Print a Block}{\v\f4 Printer}{\f4 \par }{\strike\f4 Search for Text}{\v\f4 Search}{\f4 \par }{\strike\f4 Zoom In or Out}{\v\f4 Zoom}{\f4 \par }{\strike\f4 Help}{\v\f4 Help}{\f4 \par }{\strike\f4 Exit}{\v\f4 Exit}{\f4 \par }{\strike\f4 Status Bar}{\v\f4 Status}{\f4 \par }\pard {\f4 \par }{\f4 \page }{\fs16\up6 #{\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 #} Overview}}{\fs16\up6 ${\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 $} Overview}}{\fs16\up6 K{\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 K} Overview;Summary}}{\f4 }{\plain \b\f4\lang1033 Overview}{\f4 \par }{\f4 \par }{\f4 This File Browser is a simple }{\f4 Windows utility that displays as many as ten text files in a single window.}{\f4 \par }{\f4 \par }\pard \fi-270\li990 {\f4 (1) You can scroll the window horizontally and vertically.}{\f4 \par }{\f4 \par }{\f4 (2) You can move the window and change its size.}{\f4 \par }{\f4 \par }{\f4 (3) You can "zoom in" to view small parts of the file with larger type.}{\f4 \par }{\f4 \par }{\f4 (4) You can "zoom out" to view large parts of the file with smaller type.}{\f4 \par }{\f4 \par }{\f4 (5) You can mark a rectangular block of text and then}{\f4 \par }{\f4 \par }\pard \fi-270\li1530 {\f4 (a) copy it to the Windows Clipboard,}{\f4 \par }{\f4 -- or --}{\f4 \par }{\f4 (b) print it on the default printer.}{\f4 \par }\pard \fi-180\li990 {\f4 \par }\pard \fi-270\li990 {\f4 (6) You can }{\f4 switch rapidly from one file to another.}{\f4 \par }{\f4 \par }{\f4 (7) You can open files for viewing by "dropping" them onto the File Browser window or icon.}{\f4 \par }\pard {\f4 \par }{\f4 \page }{\fs16\up6 #{\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 #} Open}}{\fs16\up6 ${\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 $} Open a File}}{\fs16\up6 K{\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 K} Open a File;File Open}}{\f4 }{\plain \b\f4\lang1033 Open a File}{\f4 \par }{\f4 \par }{\f4 To open a new file:}{\f4 \par }{\f4 \par }\pard \fi-270\li990 {\f4 (1) Drop the }{\b\f4 File}{\f4 menu and choose }{\b\f4 Open}{\f4 , or strike the }{\b\f4 O}{\f4 key. A standard file dialog box will appear.}{\f4 \par }{\f4 \par }{\f4 (2) Type the file specifications or choose the drive, directory and file from the boxes.}{\f4 \par }{\f4 \par }{\f4 (3) Push the }{\b\f4 OK}{\f4 button.}{\f4 \par }\pard {\f4 \par }{\f4 The new file will become the currently displayed file.}{\f4 \par }{\f4 \par }{\b\f4 Note}{\f4 : The number of open files is limited to 10. If there are already 10 open files, this menu item is disabled.}{\f4 \par }{\f4 \par }{\f4 \page }{\fs16\up6 #{\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 #} Close}}{\fs16\up6 ${\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 $} Close a File}}{\fs16\up6 K{\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 K} Close a File;File Close;File Close All}}{\f4 }{\plain \b\f4\lang1033 Close a File}{\f4 \par }{\f4 \par }{\f4 To close the currently displayed file:}{\f4 \par }{\f4 \par }\pard \fi-270\li990 {\f4 (1) Drop the }{\b\f4 File}{\f4 menu and choose }{\b\f4 Close}{\f4 .}{\f4 \par }\pard {\f4 \par }{\f4 The next file will become the currently displayed file.}{\f4 \par }{\f4 \par }{\f4 To close all files:}{\f4 \par }{\f4 \par }\pard \fi-270\li990 {\f4 (1) Drop the }{\b\f4 File}{\f4 menu and choose }{\b\f4 Close All}{\f4 .}{\f4 \par }\pard {\f4 \par }{\f4 \page }{\fs16\up6 #{\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 #} Switch}}{\fs16\up6 ${\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 $} Switch to Another Open File}}{\fs16\up6 K{\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 K} Switch to Another File;File Switch;File Next;File Previous;File List}}{\f4 }{\plain \b\f4\lang1033 Switch to Another Open File}{\f4 \par }{\f4 \par }{\f4 To switch the display to another open file:}{\f4 \par }{\f4 \par }\pard \fi-270\li990 {\f4 (1) To switch to the next file, drop the }{\b\f4 File}{\f4 menu and choose }{\b\f4 Next}{\f4 , or strike the }{\b\f4 Tab}{\f4 key.}{\f4 \par }{\f4 -- or --}{\f4 \par }{\f4 (2) To switch to the previous file, drop the }{\b\f4 File}{\f4 menu and choose }{\b\f4 Previous}{\f4 , or hold the }{\b\f4 Shift }{\f4 key down and strike the }{\b\f4 Tab}{\f4 key.}{\f4 \par }{\f4 -- or --}{\f4 \par }{\f4 (3) To switch to any open file, drop the }{\b\f4 File}{\f4 menu and choose }{\b\f4 List}{\f4 , or strike the }{\b\f4 L}{\f4 key. A list of currently open files will appear. Choose the desired file and press the }{\b\f4 OK}{\f4 button (or double-click on the desired file).}{\f4 \par }\pard {\f4 \par }{\f4 \page }{\fs16\up6 #{\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 #} Scroll}}{\fs16\up6 ${\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 $} Scroll the Window}}{\fs16\up6 K{\footnote \pard\plain \s245 \f3\fs20\lang1033 { \fs16\up6 K} Scrolling}}{\f4 }{\plain \b\f4\lang1033 Scroll the Window}{\f4 \par }{\f4 \par }{\f4 To scroll the window:}{\f4 \par }{\f4 \par }\pard \fi-270\li990 {\f4 (1) Click on an arrow at one end of a scroll bar to scroll the window one row or column in the direction of the arrow.}{\f4 \par }{\f4 \par }{\f4 (2) Click on a blank area of a scroll bar to scroll the window approximately one window height or width from the position of the runner toward the position of the mouse cursor.}{\f4 \par }{\f4 \par }{\f4 (3) Move the mouse cursor to a scroll bar runner, depress the left mouse button and hold it down. Then move the mouse cursor to the desired position on the scroll bar. The runner will follow it. When the runner reaches the desired position, release the le }{\f4 ft mouse button.}{\f4 \par }\pard {\f4 \par }{\f4 The text cursor will not be moved when the window is scro}{\f4 lled in this manner. To move the text cursor, just move the mouse cursor to the desired position on the text and tap the left mouse button.}{\f4 \par }{\f4 \par }{\b\f4 Note}{\f4 : The scroll bars will not respond to the keyboard. To scroll the window with the keyboard, }{\strike\f4 move}{\strike\f4 \~}{\strike\f4 the}{\strike\f4 \~}{\strike\f4 text}{\strike\f4 \~}{\strike\f4 cursor}{\v\f4 Move}{\f4 . The window will be scrolled to keep the text cursor in view.}{\f4 \par }{\f4 \par }{\f4 \page }{\fs16\up6 #{\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 #} Move}}{\fs16\up6 ${\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 $} Move the Text Cursor}}{\fs16\up6 K{\footnote \pard\plain \s245 \f3\fs20\lang1033 { \fs16\up6 K} Move Text Cursor;Scroll;Text Cursor}}{\f4 }{\plain \b\f4\lang1033 Move the Text Cursor}{\f4 \par }{\f4 \par }{\f4 To move the text cursor:}{\f4 \par }{\f4 \par }\pard \fi-270\li990 {\f4 (1) Strike one of the arrow keys (up, down, left, or right), to move the text cursor one row or column in the direction of the arrow.}{\f4 \par }{\f4 \par }{\f4 (2) Strike the }{\b\f4 Home}{\f4 key to move the cursor to the left end of the row.}{\f4 \par }{\f4 \par }{\f4 (3) Strike the }{\b\f4 End}{\f4 key to move the text cursor to the right end of the row.}{\f4 \par }{\f4 \par }{\f4 (4) Strike the }{\b\f4 PgUp}{\f4 key to move the text cursor up a distance approximately equal to the height of the window.}{\f4 \par }{\f4 \par }{\f4 (5) Strike the }{\b\f4 PgDn}{\f4 key to move the text cursor down a distance approximately equal to the height of the win}{\f4 dow.}{\f4 \par }{\f4 \par }{\f4 (6) Hold the }{\b\f4 Control}{\f4 key down and strike the }{\b\f4 Home}{\f4 key to move the text cursor to the left end of the first (top) row.}{\f4 \par }{\f4 \par }{\f4 (7) Hold the }{\b\f4 Control}{\f4 key down and strike the }{\b\f4 End}{\f4 key to move the text cursor to the right end of the last (bottom) row.}{\f4 \par }{\f4 \par }{\f4 (8) Move the mouse cursor to the desired position and tap the left mouse button.}{\f4 \par }{\f4 \par }{\f4 (9) }{\strike\f4 Search}{\strike\f4 \~}{\strike\f4 for}{\strike\f4 \~}{\strike\f4 text}{\v\f4 Search}{\f4 to move the text cursor to the end of a specific word, number or phrase.}{\f4 \par }\pard {\f4 \par }{\b\f4 Note}{\f4 : The window will scroll, if necessary, to keep the text cursor in vie}{\f4 w.}{\f4 \par }{\f4 \par }{\b\f4 Note}{\f4 : The text cursor cannot be moved below the last row of the file or farther right than the end of the longest row that has been read from the file.}{\f4 \par }{\f4 \par }{\f4 \page }{\fs16\up6 #{\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 #} Mark}}{\fs16\up6 ${\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 $} Mark a Block}}{\fs16\up6 K{\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 K} Mark a Block;Block Mark;Select Text;TextSelection;Copy Beginning;Copy End}}{\f4 }{\plain \b\f4\lang1033 Mark a Block}{\f4 \par }{\f4 \par }{\f4 To mark a rectangular block of text:}{\f4 \par }{\f4 \par }\pard \fi-270\li990 {\f4 (1) }{\strike\f4 Move}{\strike\f4 \~}{\strike\f4 the}{\strike\f4 \~}{\strike\f4 text}{\strike\f4 \~}{\strike\f4 cursor}{\v\f4 Move}{\f4 to one corner of the block and use the keyboard to move the text cursor to the opposite corner, while holding the }{\b\f4 Shift}{\f4 key down.}{\f4 \par }{\f4 -- or --}{\f4 \par }{\f4 (2) Move the mouse cursor to one corner of the block and use the mouse to move the mouse cursor to the opposite corner, while holding the left mouse button down.}{\f4 \par }{\f4 -- or --}{\f4 \par }{\f4 (3) Move the text cursor to one corner of the block. Drop the }{\b\f4 Copy}{\f4 menu and choose }{\b\f4 Beginning}{\f4 , or strike the }{\b\f4 B}{\f4 key. Move the text cursor to the opposite corner. Drop the }{\b\f4 Copy}{\f4 menu and choose}{ \b\f4 End}{\f4 , or strike the }{\b\f4 E}{\f4 key.}{\f4 \par }\pard {\f4 \par }{\b\f4 Note}{\f4 : To remove the mark, move the text cursor without holding down the Shift key as descr}{\f4 ibed in (1), or move the mouse cursor over the text and tap the left mouse button.}{\f4 \par }{\f4 \par }{\b\f4 Note}{\f4 : Use method (3) when you must }{\strike\f4 search}{\strike\f4 \~}{\strike\f4 for}{\strike\f4 \~}{\strike\f4 text}{\v\f4 Search}{\f4 to find the end of the block.}{\f4 \par }{\f4 \par }{\f4 \page }{\fs16\up6 #{\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 #} Clipboard}}{\fs16\up6 ${\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 $} Copy a Block to the Clipboard}}{\fs16\up6 K{\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 K} Copy to Clipboard;Clipboard Copy}}{\f4 }{\plain \b\f4\lang1033 Copy a Block to the Clipboard}{\f4 \par }{\f4 \par }{\f4 To copy a block to the Clipboard:}{\f4 \par }{\f4 \par }\pard \fi-270\li990 {\f4 (1) }{\strike\f4 Mark}{\strike\f4 \~}{\strike\f4 a}{\strike\f4 \~}{\strike\f4 block}{\v\f4 Mark}{\f4 .}{\f4 \par }{\f4 \par }{\f4 (2) Drop the }{\b\f4 Copy}{\f4 menu and choose }{\b\f4 Clipboard}{\f4 , or strike the }{\b\f4 C}{\f4 key.}{\f4 \par }\pard {\f4 \par }{\b\f4 Note}{\f4 : The File Browser will not copy a large block (more than about 10K).}{\f4 \par }{\f4 \par }{\f4 \page }{\fs16\up6 #{\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 #} Printer}}{\fs16\up6 ${\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 $} Print a Block}}{\fs16\up6 K{\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 K} Print a Block;Copy Printer}}{\f4 }{\plain \b\f4\lang1033 Print a Block}{\f4 \par }{\f4 \par }{\f4 To print a block (on the default Windows printer):}{\f4 \par }{\f4 \par }\pard \fi-270\li990 {\f4 (1) }{\strike\f4 Mark}{\strike\f4 \~}{\strike\f4 a}{\strike\f4 \~}{\strike\f4 block}{\v\f4 Mark}{\f4 .}{\f4 \par }{\f4 \par }{\f4 (2) Drop the }{\b\f4 Copy}{\f4 menu and choose }{\b\f4 Printer}{\f4 .}{\f4 \par }\pard {\f4 \par }{\b\f4 Note}{\f4 : The use of the Print Manager is suggested, since it allows you to abort the printing.}{\f4 \par }{\f4 \par }{\b\f4 Note}{\f4 : At most one page of text will be printed.}{\f4 \par }{\f4 \par }{\f4 \page }{\fs16\up6 #{\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 #} Search}}{\fs16\up6 ${\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 $} Search for Text}}{\fs16\up6 K{\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 K } Search;Find;View Search;View Again;Match Case;Case Sensitive}}{\f4 }{\plain \b\f4\lang1033 Search for Text}{\f4 \par }{\f4 \par }{\f4 To search for text:}{\f4 \par }{\f4 \par }\pard \fi-270\li990 {\f4 (1) }{\strike\f4 Move}{\strike\f4 \~}{\strike\f4 the}{\strike\f4 \~}{\strike\f4 text}{\strike\f4 \~}{\strike\f4 cursor}{\v\f4 Move}{\f4 , if necessary, so the text cursor is before (above) the text to be found.}{\f4 \par }{\f4 \par }{\f4 (2) Drop the }{\b\f4 View}{\f4 menu and choose }{\b\f4 Search}{\f4 , or strike the }{\b\f4 S}{\f4 key. A dialog box will appear.}{\f4 \par }{\f4 \par }{\f4 (3) Enter the text you want to find, and check the }{\b\f4 Match Case}{\f4 box if you want the search to be }{\f4\ul case-sensitive}{\v\f4 CaseSensitive}{\f4 .}{\f4 \par }{\f4 \par }{\f4 (4) Push the }{\b\f4 Search}{\f4 button.}{\f4 \par }\pard {\f4 \par }{\f4 The search will begin at the text cursor position and proceed forward to the end of the file. If the text is found, the text cursor will be positioned to the right of the text.}{\f4 \par }{\f4 \par }{\f4 To search for the same text again:}{\f4 \par }{\f4 \par }\pard \fi-270\li990 {\f4 (1) Drop the }{\b\f4 View}{\f4 menu and choose }{\b\f4 Again}{\f4 , or strike the }{\b\f4 F3}{\f4 key.}{\f4 \par }\pard {\f4 \par }{\f4 \page }{\fs16\up6 #{\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 #} CaseSensitive}}{\f4 }{\plain \b\f4\lang1033 Case-Sensitive}{\f4 \par }{\f4 \par }{\f4 A search is case-sensitive if an exact match is required.}{\f4 \par }{\f4 \par }{\f4 A search is case-insensitive if capital letters are not distinguished from the corresponding small letters.}{\f4 \par }{\f4 \par }{\f4 For example, "Hello" matches "hello" only if the search is case-insensitive.}{\f4 \par }{\f4 \par }{\f4 \page }{\fs16\up6 #{\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 #} Zoom}}{\fs16\up6 ${\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 $} Zoom In or Out}}{\fs16\up6 K{\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 K} Zoom;Type Size;View Zoom In;View Zoom Out}}{\f4 }{\plain \b\f4\lang1033 Zoom In or Out}{\f4 \par }{\f4 \par }{\f4 To zoom in (i.e, to see a magnified view of a smaller part of the text):}{\f4 \par }{\f4 \par }\pard \fi-270\li990 {\f4 (1) }{\strike\f4 Move}{\strike\f4 \~}{\strike\f4 the}{\strike\f4 \~}{\strike\f4 text}{\strike\f4 \~}{\strike\f4 cursor}{\v\f4 Move}{\f4 to the part of the text that you want to view more closely.}{\f4 \par }{\f4 \par }{\f4 (2) Drop the }{\b\f4 View}{\f4 menu and select }{\b\f4 Zoom}{\b\f4 \~}{\b\f4 In}{\f4 .}{\f4 \par }\pard {\f4 \par }{\f4 To zoom out (i.e., to see a less detailed view of a larger area):}{\f4 \par }{\f4 \par }\pard \fi-270\li990 {\f4 (1) }{\strike\f4 Move}{\strike\f4 \~}{\strike\f4 the}{\strike\f4 \~}{\strike\f4 text}{\strike\f4 \~}{\strike\f4 cursor}{\v\f4 Move}{\f4 to the center of part of the text that you want to view .}{\f4 \par }{\f4 \par }{\f4 (2) Drop the }{\b\f4 View}{\f4 menu and select }{\b\f4 Zoom}{\b\f4 \~}{\b\f4 Out}{\f4 .}{\f4 \par }\pard {\f4 \par }{\f4 \page }{\fs16\up6 #{\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 #} Help}}{\fs16\up6 ${\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 $} Help}}{\f4 }{\plain \b\f4\lang1033 Help}{\f4 \par }{\f4 \par }{\f4 To view the online help table of contents:}{\f4 \par }{\f4 \par }\pard \fi-270\li990 {\f4 (1) Drop the }{\b\f4 Help}{\f4 Menu and choose }{\b\f4 Contents}{\f4 , or strike the }{\b\f4 F1}{\f4 key.}{\f4 \par }\pard {\f4 \par }{\f4 To search for help by key word:}{\f4 \par }{\f4 \par }\pard \fi-270\li990 {\f4 (1) Drop the }{\b\f4 Help}{\f4 Menu and choose }{\b\f4 Search}{\f4 , or hold the }{\b\f4 Shift}{\f4 key down and strike the }{\b\f4 F1}{\f4 key.}{\f4 \par }\pard {\f4 \par }{\f4 \page }{\fs16\up6 #{\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 #} Exit}}{\fs16\up6 ${\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 $} Exit}}{\fs16\up6 K{\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 K} Exit;Quit;Close all files}}{\f4 }{\plain \b\f4\lang1033 Exit}{\f4 \par }{\f4 \par }{\f4 To close the File Browser window:}{\f4 \par }{\f4 \par }\pard \fi-270\li990 {\f4 (1) Drop the }{\b\f4 File}{\f4 menu and choose }{\b\f4 Exit}{\f4 .}{\f4 \par }{\f4 -- or --}{\f4 \par }{\f4 (2) Drop the system menu and choose }{\b\f4 Close}{\f4 .}{\f4 \par }{\f4 -- or --}{\f4 \par }{\f4 (3) Double click on the horizontal line in the extreme upper left corner of the window.}{\f4 \par }{\f4 -- or --}{\f4 \par }{\f4 (4) Bring up the task list and close}{\f4 the}{\f4 }{\b\f4 File}{\b\f4 Browse}{\b\f4 r}{\f4 .}{\f4 \par }\pard {\f4 \par }{\f4 \page }{\fs16\up6 #{\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 #} Status}}{\fs16\up6 ${\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 $} Status Bar}}{\fs16\up6 K{\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 K} Status Bar;Row Number;Column Number;Height;Width;Number of Rows and Columns}}{\f4 }{\plain \b\f4\lang1033 Status Bar}{\f4 \par }{\f4 \par }{\f4 The Status Bar at the bottom of the window shows:}{\f4 \par }{\f4 \par }\pard \fi-270\li990 {\f4 (1) The row and column that contain the text cursor. (If no file is displayed, these numbers are meaningless.)}{\f4 \par }{\f4 \par }{\f4 (2) If the file has been read to the end:}{\f4 \par }\pard {\f4 \par }\pard \fi-270\li1710 {\f4 (a) The file }{\b\f4 height}{\f4 :, i.e., the total number of rows.}{\f4 \par }{\f4 \par }{\f4 (b) The file }{\b\f4 width}{\f4 ; i.e., the number of columns in the widest row.}{\f4 \par }\pard {\f4 \par }{\f4 The first (top) row is number 1. }{\f4 \par }{\f4 The leftmost column is number 1.}{\f4 \par }{\f4 \par }{\b\f4 Note}{\f4 : To force the File Browser to display the height and width, hold the }{\b\f4 Shift}{\f4 key down and strike the }{\b\f4 End}{\f4 key to }{\strike\f4 move the text cursor}{\v\f4 Move}{\f4 to the end of the file.}{\f4 \par }{\f4 \par }} =============================== FILEDIAL.CPP =============================== // file_dialog() #include #include #include #include "filedial.h" /*----------------------------------------------------------------------------- This function pops up a file dialog box by calling on COMMDLG.DLL. file_dialog(handle, title, specs, specs_size, types, defext, flags); HWND handle handle of owner window char *title title to be put on title bar of dialog box char *specs specs to start with, including drive and directory path; if file name and extension are omitted, specs must end with \ int specs_size number of chars the specs buffer can hold char *types types of files to be displayed, see below char *defext default extension to be appended if file has no extension, or "" if none is to be appended DWORD flags flags The flags word consists of the following bits, OR'ed together as required: FD_OPEN open file (for input) FD_SAVE save files (for output) FD_OVERWRITEPROMPT when used with FD_SAVE, prompt user before returning a file that already exists FD_HELP show help button FD_MUSTEXIST when used with FD_OPEN, require that file exist FD_CREATEPROMPT when used with FD_SAVE, prompt user before returning a file that does not already exist The types string is a sequence of strings, one for each type of file to be displayed in the files box. Each string has the format \0\0. The strings are run together, and the last string is followed by two nulls (\0\0). The description does not have to contain a copy of the ambiguous file specs, although this is a common practice. For example if types = "All files (*.*)\0*.*\0" then all files in the selected directory are displayed in the box. (The compiler supplies the second terminating \0.) The return value is TRUE if a file was selected, or FALSE if the user clicked the Cancel button or closed the dialog box. The full specifications of the selected file are returned in the specs buffer, which must be large enough to hold them. If the function returns FALSE, the contents of the specs buffer are undefined. -----------------------------------------------------------------------------*/ BOOL file_dialog(HWND handle, char *title, char *specs, int specs_size, char *types, char *defext, DWORD flags) { OPENFILENAME f; char directory[MAX_DIR+1]; // directory path -> directory // name.ext -> specs { char *s; char *t = specs; char *d = directory; char *dend = directory; int c; for (s = specs; (c = *s) != 0; s++) { if (c == '\\') { t = specs; dend = d; if (dend == directory + 2) dend++; } else if (c == ':') { t = specs; dend = d+1; } else *t++ = c; *d++ = c; } *t = 0; *dend = 0; } memset(&f, 0, sizeof(f)); f.lStructSize = sizeof(f); f.hwndOwner = handle; // f.hInstance = 0; f.lpstrFilter = types; // f.lpstrCustomFilter = NULL; // f.nMaxCustFilter = 0; f.nFilterIndex = 1; f.lpstrFile = specs; f.nMaxFile = specs_size; // f.lpstrFileTitle = NULL; // f.nMaxFileTitle = 0; f.lpstrInitialDir = directory; f.lpstrTitle = title; f.Flags = flags & ~FD_SAVE | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST; // f.nFileOffset = 0; // f.nFileExtension = 0; f.lpstrDefExt = defext; // f.lCustData = 0; // f.lpfnHook = NULL; // f.lpTemplateName = NULL; BOOL fd = flags & FD_SAVE ? GetSaveFileName(&f): GetOpenFileName(&f); InvalidateRect(handle, NULL, TRUE); return fd; } =============================== FILEDIAL.H =============================== // header for file_dialog() #define FD_OVERWRITEPROMPT 0x00000002L #define FD_HELP 0x00000010L #define FD_MUSTEXIST 0x00001000L #define FD_CREATEPROMPT 0x00002000L #define FD_OPEN 0x00000000L #define FD_SAVE 0x10000000L #define MAX_DIR 143 #define MAX_FILE (8+1+3) extern int file_dialog(HWND, char *, char *, int, char *, char *, DWORD);