=============================== CALENDAR.CPP =============================== // Calendar Control #include #include #include #include "calendar.h" static char DAY_NAMES[] = "Su Mo Tu We Th Fr Sa"; char *MONTH_NAME[13] = { "???", "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" }; extern "C" { long far pascal _export CalendarProc(HWND, UINT, WPARAM, LPARAM); } /*----------------------------------------------------------------------------- This function registers the CALENDAR class. It is called automatically when the program starts up. The external variables _hPrev and _hInstance duplicate the arguments hPrevInstance and hInstance, which are passed to WinMain(). If the startup code does not supply these external variables, you must pass the arguments to this function and call it explicitly before invoking any CALENDAR control. -----------------------------------------------------------------------------*/ extern HINSTANCE _hPrev, _hInstance; static void register_calendar(void) { if (!_hPrev) { WNDCLASS w; w.cbClsExtra = 0; w.cbWndExtra = sizeof(void *); w.hbrBackground = (HBRUSH) COLOR_WINDOW + 1; w.hCursor = LoadCursor(NULL, IDC_ARROW); w.hIcon = NULL; w.hInstance = _hInstance; w.lpfnWndProc = CalendarProc; w.lpszClassName = "CALENDAR"; w.lpszMenuName = NULL; w.style = 0; RegisterClass(&w); } } #pragma startup register_calendar const int JAN = 31; const int FEB = 28; const int MAR = 31; const int APR = 30; const int MAY = 31; const int JUN = 30; const int JUL = 31; const int AUG = 31; const int SEP = 30; const int OCT = 31; const int NOV = 30; const int DEC = 31; const int DAYS_PER_WEEK = 7; union cal_date { DWORD dword; struct { BYTE day; BYTE month; WORD year; } x; }; static BOOL leap_year(union cal_date d) { return d.x.year%4 == 0 && !(d.x.year%100 == 0 && d.x.year%400 != 0); } static unsigned days_in_month(union cal_date d) { static BYTE DAYS_IN_MONTH[13] = { 0, JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC }; return DAYS_IN_MONTH[d.x.month] + (d.x.month == 2 && leap_year(d)); } static unsigned day_of_week(union cal_date d) { static WORD DAYS_BEFORE_MONTH[13] = { 0, 0, JAN, JAN+FEB, JAN+FEB+MAR, JAN+FEB+MAR+APR, JAN+FEB+MAR+APR+MAY, JAN+FEB+MAR+APR+MAY+JUN, JAN+FEB+MAR+APR+MAY+JUN+JUL, JAN+FEB+MAR+APR+MAY+JUN+JUL+AUG, JAN+FEB+MAR+APR+MAY+JUN+JUL+AUG+SEP, JAN+FEB+MAR+APR+MAY+JUN+JUL+AUG+SEP+OCT, JAN+FEB+MAR+APR+MAY+JUN+JUL+AUG+SEP+OCT+NOV }; unsigned y = d.x.year - 1; return (y + y/4 - y/100 + y/400 + DAYS_BEFORE_MONTH[d.x.month] + d.x.day + (leap_year(d) && d.x.month > 2)) % DAYS_PER_WEEK; } unsigned day_of_week(DWORD d) { union cal_date dd; dd.dword = d; return day_of_week(dd); } static unsigned spaces_at_beginning_of_month(union cal_date d) { return (day_of_week(d) - d.x.day + (5*DAYS_PER_WEEK+1)) % DAYS_PER_WEEK; } static cal_date next_day(union cal_date d) { if (++d.x.day > days_in_month(d)) { d.x.day = 1; if (++d.x.month > 12) { d.x.month = 1; d.x.year++; } } return d; } static cal_date previous_day(union cal_date d) { if (--d.x.day < 1 ) { if (--d.x.month < 1) { d.x.month = 12; d.x.year--; } d.x.day = days_in_month(d); } return d; } struct calendar { union cal_date current; union cal_date first; union cal_date last; HFONT font; WORD height; WORD width; WORD character_width; WORD character_height; BOOL disabled : 1; BOOL focus : 1; }; /*----------------------------------------------------------------------------- This function computes the rectangle containing a specified day and invalidates it. -----------------------------------------------------------------------------*/ static void invalidate_day(HWND handle, calendar *p, unsigned day) { RECT r; unsigned n = day - 1 + spaces_at_beginning_of_month(p->current); r.left = 3 + (n%DAYS_PER_WEEK) * 3 * p->character_width; r.right = r.left + 2 * p->character_width + 2; r.top = 3 + (2 + n/DAYS_PER_WEEK) * p->character_height; r.bottom = r.top + p->character_height; InvalidateRect(handle, &r, TRUE); } /*----------------------------------------------------------------------------- This function changes the date. -----------------------------------------------------------------------------*/ static void change_date(HWND handle, calendar *p, union cal_date new_date, BOOL notify) { if (new_date.dword < p->first.dword) new_date = p->first; else if (p->last.dword < new_date.dword) new_date = p->last; if (new_date.dword != p->current.dword) { if (new_date.x.year == p->current.x.year && new_date.x.month == p->current.x.month) { invalidate_day(handle, p, p->current.x.day); invalidate_day(handle, p, new_date.x.day); } else InvalidateRect(handle, NULL, TRUE); p->current = new_date; if (notify) PostMessage(GetParent(handle), WM_COMMAND, GetMenu(handle), MAKELONG(handle, CAL_CHANGE)); } } /*----------------------------------------------------------------------------- This function outputs the specified string, using the specified character width as the character spacing. -----------------------------------------------------------------------------*/ static void text_out(HDC dc, int x, int y, int width, char *s) { while (*s != 0) { TextOut(dc, x, y, s++, 1); x += width; } } /*----------------------------------------------------------------------------- This function receives all messages directed to the control. -----------------------------------------------------------------------------*/ long far pascal _export CalendarProc(HWND handle, UINT message, WPARAM wParam, LPARAM lParam) { calendar *p = (calendar *) #if sizeof(void *) == sizeof(short) GetWindowWord(handle, 0); #else GetWindowLong(handle, 0); #endif switch (message) { case WM_CREATE: { p = new calendar; if (p == NULL) return -1; { LOGFONT f; memset(&f, 0, sizeof(LOGFONT)); f.lfHeight = (((CREATESTRUCT far *) lParam)->cy - 6) / 8; f.lfWidth = (((CREATESTRUCT far *) lParam)->cx - 6) / 20; f.lfWeight = FW_MEDIUM; f.lfPitchAndFamily = FIXED_PITCH | FF_ROMAN; f.lfQuality = PROOF_QUALITY; p->font = CreateFontIndirect(&f); if (p->font == NULL) { delete p; return -1; } } { HDC dc = GetDC(handle); HFONT old_font = SelectObject(dc, p->font); TEXTMETRIC tm; GetTextMetrics(dc, &tm); p->character_width = tm.tmAveCharWidth; p->character_height = tm.tmHeight + tm.tmExternalLeading; SelectObject(dc, old_font); ReleaseDC(handle, dc); } p->height = ((CREATESTRUCT far *) lParam)->cy; p->width = ((CREATESTRUCT far *) lParam)->cx; p->focus = FALSE; p->disabled = (((CREATESTRUCT far *) lParam)->style & WS_DISABLED) != 0; p->first.dword = MAKEDATE(1, 1, 1800); p->last.dword = MAKEDATE(12, 31, 2099); p->current.dword = 0L; #if sizeof(void *) == sizeof(short) SetWindowWord(handle, 0, (WORD) p); #else SetWindowLong(handle, 0, (DWORD) p); #endif return 0; } case WM_PAINT: { COLORREF gray_color = GetSysColor(COLOR_GRAYTEXT); COLORREF normal_text_color = p->disabled ? gray_color : GetSysColor(COLOR_WINDOWTEXT); COLORREF normal_background_color = GetSysColor(COLOR_WINDOW); COLORREF selected_text_color = GetSysColor(COLOR_HIGHLIGHTTEXT); COLORREF selected_background_color = GetSysColor(COLOR_HIGHLIGHT); PAINTSTRUCT paint; HDC dc = BeginPaint(handle, &paint); HPEN pen = CreatePen(PS_SOLID, 1, normal_text_color); HPEN old_pen = SelectObject(dc, pen); HBRUSH brush = CreateSolidBrush(normal_background_color); HBRUSH old_brush = SelectObject(dc, brush); HFONT old_font = SelectObject(dc, p->font); union cal_date date; date = p->current; Rectangle(dc, 0, 0, p->width, p->height); if (p->focus) Rectangle(dc, 1, 1, p->width - 1, p->height - 1); if (date.dword != 0L) { SetTextColor(dc, normal_text_color); SetBkColor(dc, normal_background_color); { char s[8]; strcpy(s, (date.dword & 0xFFFFFF00L) > (p->first.dword & 0xFFFFFF00L) ? "<-" : " "); strcpy(s+2, MONTH_NAME[date.x.month]); strcpy(s+5, (date.dword & 0xFFFFFF00L) < (p->last.dword & 0xFFFFFF00L) ? "->" : " "); text_out(dc, 3, 3, p->character_width, s); } { unsigned y = date.x.year; char s[9]; strcpy(s, y > p->first.x.year ? "<-": " "); s[5] = y%10 + '0'; y /= 10; s[4] = y%10 + '0'; y /= 10; s[3] = y%10 + '0'; s[2] = y/10 + '0'; strcpy(s+6, y < p->last.x.year ? "->" : " "); text_out(dc, 3 + 12 * p->character_width, 3, p->character_width, s); } text_out(dc, 3, 3 + p->character_height, p->character_width, DAY_NAMES); { unsigned horizontal_offset = spaces_at_beginning_of_month(date); int y = 3 + 2 * p->character_height; unsigned days_in_this_month = days_in_month(date); union cal_date d; for (d.dword = (date.dword & 0xFFFFFF00L) + 1; d.x.day <= days_in_this_month; d.x.day++) { char s[2]; s[0] = d.x.day < 10 ? ' ' : d.x.day/10 + '0'; s[1] = d.x.day%10 + '0'; if (d.dword == date.dword) { SetTextColor(dc, selected_text_color); SetBkColor(dc, selected_background_color); } else if (d.dword < p->first.dword || p->last.dword < d.dword) { SetTextColor(dc, gray_color); SetBkColor(dc, normal_background_color); } else { SetTextColor(dc, normal_text_color); SetBkColor(dc, normal_background_color); } TextOut(dc, 3 + p->character_width * 3 * horizontal_offset, y, s, 2); if (++horizontal_offset == DAYS_PER_WEEK) { horizontal_offset = 0; y += p->character_height; } } } } SelectObject(dc, old_font); SelectObject(dc, old_pen); DeleteObject(pen); SelectObject(dc, old_brush); DeleteObject(brush); EndPaint(handle, &paint); return 0; } case CAL_GETDATE: return p->current.dword; case CAL_SETDATE: change_date(handle, p, *(union cal_date *)(&lParam), FALSE); return 0; case CAL_FIRSTDATE: p->first.dword = lParam; return 0; case CAL_LASTDATE: p->last.dword = lParam; return 0; case WM_KEYDOWN: { union cal_date new_date; new_date = p->current; if (wParam == VK_LEFT) new_date = previous_day(new_date); else if (wParam == VK_RIGHT) new_date = next_day(new_date); else if (wParam == VK_UP) { unsigned n = DAYS_PER_WEEK; do new_date = previous_day(new_date); while (--n != 0); } else if (wParam == VK_DOWN) { unsigned n = DAYS_PER_WEEK; do new_date = next_day(new_date); while (--n != 0); } else if (wParam == VK_PRIOR) { if (GetKeyState(VK_CONTROL) & 0x8000) { if (new_date.x.month == 1 && new_date.x.day == 1) new_date.x.year--; else new_date.x.month = new_date.x.day = 1; } else do new_date = previous_day(new_date); while (new_date.x.day != 1); } else if (wParam == VK_NEXT) { if (GetKeyState(VK_CONTROL) & 0x8000) { new_date.x.year++; new_date.x.month = new_date.x.day = 1; } else do new_date = next_day(new_date); while (new_date.x.day != 1); } change_date(handle, p, new_date, TRUE); return 0; } case WM_ENABLE: if (wParam && p->disabled || !wParam && !(p->disabled)) { p->disabled = !p->disabled; InvalidateRect(handle, NULL, TRUE); } return 0; case WM_LBUTTONDOWN: if (3 <= HIWORD(lParam) && HIWORD(lParam) <= 8 * p->character_height + 3 && 3 <= LOWORD(lParam) && LOWORD(lParam) <= 20 * p->character_width + 3) { unsigned i = (HIWORD(lParam) - 3) / p->character_height; union cal_date new_date; new_date = p->current; if (i == 0) { unsigned j = (LOWORD(lParam) - 3) / p->character_width; if (j == 0 || j == 1) { do new_date = previous_day(new_date); while (new_date.x.day != 1); } else if (j == 5 || j == 6) { do new_date = next_day(new_date); while (new_date.x.day != 1); } else if (j == 12 || j == 13) { if (new_date.x.month == 1 && new_date.x.day == 1) new_date.x.year--; else new_date.x.month = new_date.x.day = 1; } else if (j == 18 || j == 19) { new_date.x.year++; new_date.x.month = new_date.x.day = 1; } } else if (i >= 2) { unsigned j = (LOWORD(lParam) - 3 + p->character_width/2) / (3*p->character_width); if (j < DAYS_PER_WEEK) { int d = 1 + (i - 2) * DAYS_PER_WEEK + j - spaces_at_beginning_of_month(p->current); if (1 <= d && d <= days_in_month(p->current)) new_date.x.day = d; } } SetFocus(handle); change_date(handle, p, new_date, TRUE); return 0; } break; case WM_SETFOCUS: p->focus = TRUE; InvalidateRect(handle, NULL, TRUE); return 0; case WM_KILLFOCUS: p->focus = FALSE; InvalidateRect(handle, NULL, TRUE); PostMessage(GetParent(handle), WM_COMMAND, GetMenu(handle), MAKELONG(handle, CAL_KILLFOCUS)); return 0; case WM_GETDLGCODE: return DLGC_WANTARROWS; case WM_ERASEBKGND: return 0; case WM_DESTROY: DeleteObject(p->font); delete p; return 0; } return DefWindowProc(handle, message, wParam, lParam); } =============================== CALENDAR.H =============================== #define CAL_GETDATE (WM_USER+0) #define CAL_SETDATE (WM_USER+1) #define CAL_FIRSTDATE (WM_USER+2) #define CAL_LASTDATE (WM_USER+3) #define CAL_CHANGE EN_CHANGE #define CAL_KILLFOCUS EN_KILLFOCUS inline DWORD MAKEDATE(unsigned month, unsigned day, unsigned year) { return (DWORD) year << 16 | month << 8 | day; } inline unsigned GETDAY(DWORD d) {return d & 0xFF;} inline unsigned GETMONTH(DWORD d) {return d >> 8 & 0xFF;} inline unsigned GETYEAR(DWORD d) {return d >> 16;} extern unsigned day_of_week(DWORD d); extern char *MONTH_NAME[13]; =============================== CALTEST.CPP =============================== // Calendar Control Test #include #include "calendar.h" #include "caltest.h" extern "C" { long far pascal _export WndProc(HWND, UINT, WORD, LONG); int pascal WinMain(HINSTANCE, HINSTANCE, LPSTR, int); } static char *DAY_NAME[7] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; long far pascal _export WndProc(HWND hwnd, UINT message, WORD wParam, LONG lParam) { switch (message) { case WM_CREATE: return 0; case WM_COMMAND: switch (wParam) { case ENABLE: { static BOOL enabled = TRUE; enabled = !enabled; EnableWindow(GetDlgItem(hwnd, CALENDAR2), enabled); return 0; } case CALENDAR1: { if (HIWORD(lParam) == CAL_CHANGE) { char s[35]; DWORD d = SendDlgItemMessage(hwnd, CALENDAR1, CAL_GETDATE, 0, 0L); wsprintf(s, "%s, %s %d, %d", (char far *) DAY_NAME[day_of_week(d)], (char far *) MONTH_NAME[GETMONTH(d)], GETDAY(d), GETYEAR(d)); SetDlgItemText(hwnd, TEXT1, s); } return 0; } case CALENDAR2: { if (HIWORD(lParam) == CAL_KILLFOCUS) { char s[35]; DWORD d = SendDlgItemMessage(hwnd, CALENDAR2, CAL_GETDATE, 0, 0L); wsprintf(s, "%s, %s %d, %d", (char far *) DAY_NAME[day_of_week(d)], (char far *) MONTH_NAME[GETMONTH(d)], GETDAY(d), GETYEAR(d)); SetDlgItemText(hwnd, TEXT2, s); } return 0; } case CALENDAR3: { return 0; } default: break; } break; case WM_CLOSE: DestroyWindow(hwnd); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefDlgProc (hwnd, message, wParam, lParam); } #pragma argsused int pascal WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { HWND hwnd; MSG msg; if (!hPrevInstance) { WNDCLASS wndclass; wndclass.style = 0; wndclass.lpfnWndProc = (WNDPROC) WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = DLGWINDOWEXTRA; wndclass.hInstance = hInstance; wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndclass.hCursor = LoadCursor (NULL, IDC_ARROW); wndclass.hbrBackground = NULL; wndclass.lpszMenuName = NULL; wndclass.lpszClassName = "CALENDARTEST"; RegisterClass (&wndclass); } hwnd = CreateDialog(hInstance, "CALENDARTEST", 0, NULL); SendDlgItemMessage(hwnd, CALENDAR1, CAL_SETDATE, 0, MAKEDATE(1, 15, 1994)); SendDlgItemMessage(hwnd, CALENDAR1, CAL_FIRSTDATE, 0, MAKEDATE(1, 10, 1980)); SendDlgItemMessage(hwnd, CALENDAR1, CAL_LASTDATE, 0, MAKEDATE(1, 10, 2000)); SendDlgItemMessage(hwnd, CALENDAR2, CAL_SETDATE, 0, MAKEDATE(12, 7, 1941)); SendDlgItemMessage(hwnd, CALENDAR3, CAL_SETDATE, 0, MAKEDATE(1, 15, 1994)); SetFocus(GetDlgItem(hwnd, CALENDAR2)); ShowWindow(hwnd, nCmdShow == SW_SHOWMAXIMIZED ? SW_SHOW : nCmdShow); UpdateWindow(hwnd); while (GetMessage(&msg, NULL, 0, 0)) if (!IsDialogMessage (hwnd, &msg)) DispatchMessage (&msg); return msg.wParam; } =============================== CALTEST.H =============================== #define CALENDAR1 100 #define CALENDAR2 101 #define CALENDAR3 102 #define ENABLE 103 #define TEXT1 104 #define TEXT2 105 =============================== CALTEST.RC =============================== #include "caltest.h" CALENDARTEST DIALOG 15, 15, 264, 228 STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CLASS "CALENDARTEST" CAPTION "Calendar Test" FONT 8, "MS Sans Serif" BEGIN CONTROL 0, CALENDAR1, "CALENDAR", 0 | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 20, 20, 95, 70 CTEXT "", TEXT1, 20, 96, 95, 8 CTEXT "Calendar 1", -1, 20, 4, 95, 8 DEFPUSHBUTTON "Enable/Disable 2", ENABLE, 21, 135, 66, 24 CONTROL "", CALENDAR2, "CALENDAR", 0 | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 125, 114, 120, 90 CTEXT "", TEXT2, 125, 210, 120, 8 CTEXT "Calendar 2", -1, 125, 99, 120, 8 CONTROL "", CALENDAR3, "CALENDAR", 0 | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 159, 21, 61, 46 CTEXT "Calendar 3", -1, 159, 6, 61, 8 END =============================== CHECKBOX.CPP =============================== // Windows Checkbox Control // compilation parameter const int BORDER = 4; // pixels between focus rectangle and box #include extern "C" { long far pascal _export CheckBoxProc(HWND, UINT, WPARAM, LPARAM); } /*----------------------------------------------------------------------------- This function registers the CHECKBOX class. It is called automatically when the program starts up. The external variables _hPrev and _hInstance duplicate the arguments hPrevInstance and hInstance, which are passed to WinMain(). If the startup code does not supply these external variables, you must pass the arguments to this function and call it explicitly before invoking any CHECKBOX control. -----------------------------------------------------------------------------*/ extern HINSTANCE _hPrev, _hInstance; static void register_check_box(void) { if (!_hPrev) { WNDCLASS w; w.cbClsExtra = 0; w.cbWndExtra = 2*sizeof(WORD); w.hbrBackground = (HBRUSH) COLOR_WINDOW + 1; w.hCursor = LoadCursor(NULL, IDC_ARROW); w.hIcon = NULL; w.hInstance = _hInstance; w.lpfnWndProc = CheckBoxProc; w.lpszClassName = "CHECKBOX"; w.lpszMenuName = NULL; w.style = 0; RegisterClass(&w); } } #pragma startup register_check_box // status bits bit is set if this control -- const int CHECKED = 1<<0; // is checked const int HIGHLIGHTED = 1<<2; // is highlighted const int FOCUS = 1<<3; // has the input focus const int CAPTURED = 1<<4; // has captured the mouse const int DISABLED = 1<<5; // is disabled /*----------------------------------------------------------------------------- This function receives all messages directed to the control. -----------------------------------------------------------------------------*/ long far pascal _export CheckBoxProc(HWND handle, UINT message, WPARAM wParam, LPARAM lParam) { WORD status = GetWindowWord(handle, 0); WORD size = GetWindowWord(handle, 2); switch (message) { case WM_CREATE: { size = ((CREATESTRUCT far *) lParam)->cy; if (((CREATESTRUCT far *) lParam)->cx < size) size = ((CREATESTRUCT far *) lParam)->cx; SetWindowWord(handle, 2, size); SetWindowWord(handle, 0, ((CREATESTRUCT far *) lParam)->style & WS_DISABLED ? DISABLED : 0); return 0; } case WM_PAINT: { PAINTSTRUCT paint; HDC dc = BeginPaint(handle, &paint); HPEN pen = CreatePen(PS_SOLID, 1, GetSysColor(status & DISABLED ? COLOR_GRAYTEXT : COLOR_WINDOWTEXT)); HPEN old_pen = SelectObject(dc, pen); HBRUSH brush = CreateSolidBrush(GetSysColor(COLOR_WINDOW)); HBRUSH old_brush = SelectObject(dc, brush); // draw box Rectangle(dc, BORDER, BORDER, size - BORDER, size - BORDER); // highlight by drawing a second box inside the first if (status & HIGHLIGHTED) Rectangle(dc, BORDER + 1, BORDER + 1, size - (BORDER + 1), size - (BORDER + 1)); // check by drawing an X inside the box if (status & CHECKED) { MoveTo(dc, BORDER, BORDER); LineTo(dc, size - (BORDER + 1), size - (BORDER + 1)); MoveTo(dc, BORDER, size - (BORDER + 1)); LineTo(dc, size - (BORDER + 1), BORDER); } // draw a focus rectangle around the box if (status & FOCUS) { RECT r; r.left = r.top = 0; r.right = r.bottom = size; DrawFocusRect(dc, &r); } SelectObject(dc, old_pen); DeleteObject(pen); SelectObject(dc, old_brush); DeleteObject(brush); EndPaint(handle, &paint); return 0; } case WM_ENABLE: SetWindowWord(handle, 0, wParam ? status & ~DISABLED : status | DISABLED); InvalidateRect(handle, NULL, TRUE); return 0; case WM_LBUTTONDOWN: SetWindowWord(handle, 0, status | HIGHLIGHTED + CAPTURED); InvalidateRect(handle, NULL, TRUE); SetCapture(handle); SetFocus(handle); return 0; case WM_LBUTTONUP: if (!(status & CAPTURED) || LOWORD(lParam) < size && HIWORD(lParam) < size) { status ^= CHECKED; } SetWindowWord(handle, 0, status & ~(HIGHLIGHTED + CAPTURED)); InvalidateRect(handle, NULL, TRUE); ReleaseCapture(); PostMessage(GetParent(handle), WM_COMMAND, GetMenu(handle), MAKELONG(handle, BN_CLICKED)); return 0; case BM_GETSTATE: return status & CHECKED + HIGHLIGHTED + FOCUS; case BM_SETSTATE: SetWindowWord(handle, 0, wParam ? status | HIGHLIGHTED : status & ~HIGHLIGHTED); InvalidateRect(handle, NULL, TRUE); return 0; case WM_MOUSEMOVE: if (status & CAPTURED) { WORD old_status = status; if (LOWORD(lParam) < size && HIWORD(lParam) < size) status |= HIGHLIGHTED; else status &= ~HIGHLIGHTED; if (status != old_status) { SetWindowWord(handle, 0, status); InvalidateRect(handle, NULL, TRUE); return 0; } } break; case WM_SETFOCUS: SetWindowWord(handle, 0, status | FOCUS); InvalidateRect(handle, NULL, TRUE); return 0; case WM_KILLFOCUS: SetWindowWord(handle, 0, status & ~FOCUS); InvalidateRect(handle, NULL, TRUE); return 0; case WM_GETDLGCODE: return DLGC_BUTTON; case WM_KEYDOWN: if (wParam == ' ' && !(status & CAPTURED)) { SetWindowWord(handle, 0, status | HIGHLIGHTED); InvalidateRect(handle, NULL, TRUE); return 0; } break; case WM_KEYUP: if (wParam == ' ' && !(status & CAPTURED)) { SetWindowWord(handle, 0, status & ~HIGHLIGHTED ^ CHECKED); InvalidateRect(handle, NULL, TRUE); PostMessage(GetParent(handle), WM_COMMAND, GetMenu(handle), MAKELONG(handle, BN_CLICKED)); return 0; } break; case BM_SETCHECK: SetWindowWord(handle, 0, wParam ? status | CHECKED : status & ~CHECKED); InvalidateRect(handle, NULL, TRUE); return 0; case BM_GETCHECK: return status & CHECKED; } return DefWindowProc(handle, message, wParam, lParam); } =============================== CHECTEST.CPP =============================== // Windows Checkbox Control Test Program #include extern "C" { long far pascal _export WndProc(HWND, UINT, WORD, LONG); int pascal WinMain(HINSTANCE, HINSTANCE, LPSTR, int); } long far pascal _export WndProc(HWND hwnd, UINT message, WORD wParam, LONG lParam) { switch (message) { case WM_CREATE: return 0; case WM_CLOSE: DestroyWindow(hwnd); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefDlgProc (hwnd, message, wParam, lParam); } #pragma argsused int pascal WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { HWND hwnd; MSG msg; if (!hPrevInstance) { WNDCLASS wndclass; wndclass.style = 0; wndclass.lpfnWndProc = (WNDPROC) WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = DLGWINDOWEXTRA; wndclass.hInstance = hInstance; wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = NULL; wndclass.lpszMenuName = NULL; wndclass.lpszClassName = "CHECKBOXTEST"; RegisterClass (&wndclass); } hwnd = CreateDialog(hInstance, "CHECKBOXTEST", 0, NULL); ShowWindow(hwnd, nCmdShow == SW_SHOWMAXIMIZED ? SW_SHOW : nCmdShow); UpdateWindow(hwnd); while (GetMessage(&msg, NULL, 0, 0)) if (!IsDialogMessage (hwnd, &msg)) DispatchMessage (&msg); return msg.wParam; } =============================== CHECTEST.RC =============================== CHECKBOXTEST DIALOG 6, 15, 206, 60 STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CLASS "CHECKBOXTEST" CAPTION "Checkbox Test Program" FONT 8, "MS Sans Serif" { CONTROL "Check", 101, "CHECKBOX", 2 | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 18, 12, 10, 10 CONTROL "Check", 102, "CHECKBOX", 2 | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 51, 12, 12, 12 CONTROL "Check", 103, "CHECKBOX", 2 | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 96, 12, 15, 15 CONTROL "Check", 104, "CHECKBOX", 2 | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 144, 12, 20, 20 } =============================== LINEIN.CPP =============================== // Line Input #include // compilation parameters const unsigned OVERFLOW_BAR = 3 ; // pixels const unsigned TOP_OFFSET = 3; // pixels const unsigned SIDE_OFFSET = 1; // pixels const unsigned MAX_CAPACITY = 127; // characters const unsigned SCROLLING_INTERVAL = 200; // milliseconds const unsigned CARET_WIDTH = 1; // pixels #include "linein.h" extern "C" { long far pascal _export LineInputProc(HWND, UINT, WPARAM, LPARAM); } /*----------------------------------------------------------------------------- This function registers the LINEINPUT class. It is called automatically when the program starts up. The external variables _hPrev and _hInstance duplicate the arguments hPrevInstance and hInstance, which are passed to WinMain(). If the startup code does not supply these external variables, you must pass the arguments to this function and call it explicitly before invoking any LINEINPUT control. -----------------------------------------------------------------------------*/ extern HINSTANCE _hPrev, _hInstance; static void register_line_input(void) { if (!_hPrev) { WNDCLASS w; w.cbClsExtra = 0; w.cbWndExtra = sizeof(HGLOBAL); w.hbrBackground = (HBRUSH) COLOR_WINDOW + 1; w.hCursor = LoadCursor(NULL, IDC_IBEAM); w.hIcon = NULL; w.hInstance = _hInstance; w.lpfnWndProc = LineInputProc; w.lpszClassName = "LINEINPUT"; w.lpszMenuName = NULL; w.style = CS_DBLCLKS; RegisterClass(&w); } } #pragma startup register_line_input const unsigned LEFT_OFFSET = SIDE_OFFSET + OVERFLOW_BAR + SIDE_OFFSET; const unsigned RIGHT_OFFSET = LEFT_OFFSET; // values of scrolling member const unsigned NO_SCROLLING = 0; const unsigned SCROLLING_LEFT = 1; const unsigned SCROLLING_RIGHT = 2; struct LINEINPUT { BOOL disabled : 1; BOOL focus : 1; BOOL read_only : 1; BOOL selection : 1; BOOL typeover : 1; BOOL change : 1; BOOL modified : 1; BOOL uppercase : 1; BOOL invalidate : 1; BOOL noselectall : 1; BOOL captured : 1; unsigned scrolling : 2; unsigned unused : 3; unsigned height; unsigned width; HFONT font; unsigned character_height; unsigned capacity; unsigned select; unsigned timer; WORD invalid_chars[6]; struct { unsigned hidden; unsigned caret; unsigned length; unsigned displayed_length; unsigned position[MAX_CAPACITY + 1]; char text[MAX_CAPACITY + 1]; WORD select[(MAX_CAPACITY+15)/16]; } layout, old_layout; }; /*----------------------------------------------------------------------------- These functions set and get individual bits in a WORD array. (There is no need for a function that clears individual bits; they are always cleared en masse.) -----------------------------------------------------------------------------*/ static void set_bit(WORD far *b, unsigned n) { b[n>>4] |= 1 << (n&0xF); } static int get_bit(WORD far *b, unsigned n) { return b[n>>4] >> (n&0xF) & 1; } /*----------------------------------------------------------------------------- This function deletes the selection, if any. -----------------------------------------------------------------------------*/ static void delete_selection(LINEINPUT far *p) { if (p->selection && p->select != p->layout.caret) { unsigned start = p->select; unsigned end = p->layout.caret; if (start > end) { start = p->layout.caret; end = p->select; } lstrcpy(p->layout.text + start, p->layout.text + end); p->layout.length -= end - start; p->layout.caret = start; if (p->layout.hidden > p->layout.length) p->layout.hidden = p->layout.length; p->change = TRUE; p->modified = TRUE; } p->selection = FALSE; } /*----------------------------------------------------------------------------- This function gets the caret location corresponding to the specified horizontal cursor position. -----------------------------------------------------------------------------*/ static unsigned get_caret_location(LINEINPUT far *p, int position) { unsigned i = p->layout.hidden; while (i < p->layout.displayed_length && position > LEFT_OFFSET + (p->layout.position[i] + p->layout.position[i+1])/2 - p->layout.position[p->layout.hidden]) { i++; } return i; } /*----------------------------------------------------------------------------- This function returns TRUE if the character is not among those specified as invalid. -----------------------------------------------------------------------------*/ static BOOL valid_character(LINEINPUT far *p, int character) { return ' ' <= character && character <= '~' && get_bit(p->invalid_chars, character - ' ') == 0; } /*----------------------------------------------------------------------------- This function inserts the specified character at the caret location. -----------------------------------------------------------------------------*/ static void insert_character(LINEINPUT far *p, int character) { if (valid_character(p, character)) { unsigned i; for (i = p->layout.length; i > p->layout.caret; i--) p->layout.text[i] = p->layout.text[i-1]; p->layout.text[i] = character; p->layout.caret++; p->layout.text[++p->layout.length] = 0; p->change = TRUE; p->modified = TRUE; } } /*----------------------------------------------------------------------------- This function determines the display layout, invalidates the changed region, if any, and moves the caret. -----------------------------------------------------------------------------*/ static void new_layout(HWND handle, LINEINPUT far *p) { HDC dc = GetDC(handle); HFONT old_font; if (p->font != NULL) old_font = SelectObject(dc, p->font); // if caret has moved past the left end, scroll text right if (p->layout.caret < p->layout.hidden) p->layout.hidden = p->layout.caret; // determine positions of all displayed characters { unsigned i; p->layout.position[0] = 0; for (i = 0; i < p->layout.length; i++) { p->layout.position[i+1] = p->layout.position[i] + (WORD) GetTextExtent(dc, p->layout.text + i, 1) - 1; } } // mark all selected characters { unsigned i; for (i = 0; i < (MAX_CAPACITY+15)/16; i++) p->layout.select[i] = 0; if (p->selection && p->select != p->layout.caret) { unsigned start = p->select; unsigned end = p->layout.caret; if (start > end) { start = p->layout.caret; end = p->select; } for (; start < end; start++) set_bit(p->layout.select, start); } } // if caret is past end of text, hide some characters while (LEFT_OFFSET + p->layout.position[p->layout.caret] - p->layout.position[p->layout.hidden] >= p->width - RIGHT_OFFSET) { p->layout.hidden++; } // find displayed length p->layout.displayed_length = p->layout.length; while (LEFT_OFFSET + p->layout.position[p->layout.displayed_length] - p->layout.position[p->layout.hidden] >= p->width - RIGHT_OFFSET) { p->layout.displayed_length--; } if (p->font != NULL) SelectObject(dc, old_font); ReleaseDC(handle, dc); // invalidate places that have changed if (p->invalidate) InvalidateRect(handle, NULL, TRUE); else { RECT r; unsigned x; r.top = TOP_OFFSET; r.bottom = TOP_OFFSET + p->character_height; // invalidate places occupied by changed characters { unsigned i, j; for (i = p->layout.hidden, j = p->old_layout.hidden; i < p->layout.displayed_length && j < p->old_layout.displayed_length && p->layout.text[i] == p->old_layout.text[j]; i++, j++) { if (get_bit(p->layout.select, i) != get_bit(p->old_layout.select, j)) { x = LEFT_OFFSET + p->layout.position[i] - p->layout.position[p->layout.hidden]; if (x < r.left) r.left = x; x = LEFT_OFFSET + p->layout.position[i+1] - p->layout.position[p->layout.hidden]; if (x > r.right) r.right = x; } } if (i < p->layout.displayed_length || j < p->old_layout.displayed_length) { x = LEFT_OFFSET + p->layout.position[i] - p->layout.position[p->layout.hidden]; if (x < r.left) r.left = x; x = LEFT_OFFSET + p->layout.position[p->layout.displayed_length] - p->layout.position[p->layout.hidden]; if (x > r.right) r.right = x; x = LEFT_OFFSET + p->old_layout.position[p->old_layout.displayed_length] - p->old_layout.position[p->old_layout.hidden]; if (x > r.right) r.right = x; } } // invalidate overflow indicator regions if necessary if (p->layout.hidden == 0 && p->old_layout.hidden != 0 || p->layout.hidden != 0 && p->old_layout.hidden == 0) { r.top = 0; r.bottom = p->height; r.left = 1; if (1 + OVERFLOW_BAR > r.right) r.right = 1 + OVERFLOW_BAR; } if (p->layout.displayed_length != p->layout.length && p->old_layout.displayed_length == p->old_layout.length || p->layout.displayed_length == p->layout.length && p->old_layout.displayed_length != p->old_layout.length) { r.top = 0; r.bottom = p->height; x = p->width - 1 - OVERFLOW_BAR; if (x < r.left) r.left = x; r.right = p->width - 1; } if (r.left < r.right) { r.right++; InvalidateRect(handle, &r, TRUE); } } if (p->focus) SetCaretPos(LEFT_OFFSET + p->layout.position[p->layout.caret] - p->layout.position[p->layout.hidden], TOP_OFFSET); } /*----------------------------------------------------------------------------- This function gets the character height for the currently selected font. -----------------------------------------------------------------------------*/ static int get_character_height(HDC dc) { TEXTMETRIC t; GetTextMetrics(dc, &t); return t.tmHeight + t.tmExternalLeading; } /*----------------------------------------------------------------------------- This function stops the scrolling, if any. -----------------------------------------------------------------------------*/ static void stop_scrolling(HWND handle, LINEINPUT far *p) { if (p->scrolling != NO_SCROLLING) { KillTimer(handle, p->timer); p->scrolling = NO_SCROLLING; } } /*----------------------------------------------------------------------------- This function copies or cuts the selected text to the Windows Clipboard. -----------------------------------------------------------------------------*/ static void copy_or_cut(HWND handle, LINEINPUT far *p, BOOL cut) { if (!(cut && p->read_only) && p->selection && p->select != p->layout.caret) { unsigned start = p->select; unsigned end = p->layout.caret; if (start > end) { start = p->layout.caret; end = p->select; } HGLOBAL h = GlobalAlloc(GMEM_MOVEABLE, end - start + 1); if (h != NULL) { char far *t = GlobalLock(h); while (start < end) *t++ = p->layout.text[start++]; *t = 0; GlobalUnlock(h); if (OpenClipboard(handle)) { EmptyClipboard(); SetClipboardData(CF_TEXT, h); CloseClipboard(); if (cut) delete_selection(p); } else GlobalFree(h); } new_layout(handle, p); } } /*----------------------------------------------------------------------------- The function pastes text from the Windows Clipboard into the control. -----------------------------------------------------------------------------*/ static void paste(HWND handle, LINEINPUT far *p) { if (!p->read_only) { if (OpenClipboard(handle)) { HGLOBAL h = GetClipboardData(CF_TEXT); if (h != NULL) { char far *t = GlobalLock(h); delete_selection(p); while (*t != 0 && p->layout.length < p->capacity) insert_character(p, *t++); GlobalUnlock(h); } CloseClipboard(); } new_layout(handle, p); } } /*----------------------------------------------------------------------------- This function receives all messages directed to the control. -----------------------------------------------------------------------------*/ long far pascal _export LineInputProc(HWND handle, UINT message, WPARAM wParam, LPARAM lParam) { LINEINPUT far *p; HGLOBAL hp = GetWindowWord(handle, 0); if (hp != NULL) p = (LINEINPUT far *) GlobalLock(hp); switch (message) { case WM_CREATE: { hp = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof(LINEINPUT)); if (hp == NULL) return -1L; p = (LINEINPUT far *) GlobalLock(hp); { DWORD style = ((CREATESTRUCT far *) lParam)->style; if (style & WS_DISABLED) p->disabled = TRUE; if (style & ES_READONLY) p->read_only = TRUE; if (style & ES_UPPERCASE) p->uppercase = TRUE; if (style & ES_TYPEOVER) p->typeover = TRUE; if (style & ES_NOSELECTALL) p->noselectall = TRUE; } p->invalidate = TRUE; p->height = ((CREATESTRUCT far *) lParam)->cy; p->width = ((CREATESTRUCT far *) lParam)->cx; p->capacity = MAX_CAPACITY; { HDC dc = GetDC(handle); p->character_height = get_character_height(dc); ReleaseDC(handle, dc); } { const char far *t = ((CREATESTRUCT far *) lParam)->lpszName; while (t != NULL && *t != 0 && p->layout.length < MAX_CAPACITY) p->layout.text[p->layout.length++] = *t++; } if (p->uppercase) AnsiUpper(p->layout.text); SetWindowWord(handle, 0, (WORD) hp); GlobalUnlock(hp); return 0; } case WM_SETTEXT: { int c; p->layout.length = 0; while ((c = *((char far *) lParam)++) != 0 && p->layout.length < p->capacity) { if (p->uppercase) c = (long) AnsiUpper((char far *)(long) c); if (valid_character(p, c)) p->layout.text[p->layout.length++] = c; } p->layout.text[p->layout.length] = 0; p->layout.caret = 0; p->selection = FALSE; new_layout(handle, p); GlobalUnlock(hp); return 0; } case EM_LIMITTEXT: if (wParam > MAX_CAPACITY) wParam = MAX_CAPACITY; p->capacity = wParam; if (wParam < p->layout.length) { p->layout.length = wParam; p->layout.text[wParam] = 0; } p->layout.caret = 0; p->selection = FALSE; new_layout(handle, p); GlobalUnlock(hp); return 0; case EM_SETREADONLY: p->read_only = wParam != 0; GlobalUnlock(hp); return 0; case EM_SETTYPEOVER: p->typeover = wParam != 0; GlobalUnlock(hp); return 0; case EM_SETNOSELECTALL: p->noselectall = wParam != 0; GlobalUnlock(hp); return 0; case EM_SETINVALID: { unsigned i, j; for (i = 0; i < 6; i++) p->invalid_chars[i] = ((WORD far *) lParam)[i]; for (i = j = 0; j < p->layout.length; j++) { if (valid_character(p, p->layout.text[j])) p->layout.text[i++] = p->layout.text[j]; } p->layout.text[i] = 0; p->layout.length = i; p->layout.caret = 0; p->selection = FALSE; new_layout(handle, p); GlobalUnlock(hp); return 0; } case EM_SETUPPERCASE: if (wParam != 0 && !p->uppercase) { AnsiUpper(p->layout.text); new_layout(handle, p); } p->uppercase = wParam != 0; GlobalUnlock(hp); return 0; case EM_SETMODIFY: p->modified = wParam != 0; GlobalUnlock(hp); return 0; case EM_GETMODIFY: GlobalUnlock(hp); return p->modified; case EM_GETSEL: { LRESULT result = 0L; if (p->selection && p->select != p->layout.caret) { unsigned start = p->select; unsigned end = p->layout.caret; if (start > end) { start = p->layout.caret; end = p->select; } result = MAKELRESULT(start, end); } GlobalUnlock(hp); return result; } case WM_SETFONT: { HFONT old_font; HDC dc = GetDC(handle); p->font = (HFONT) wParam; if ((HFONT) wParam != NULL) old_font = SelectObject(dc, (HFONT) wParam); p->character_height = get_character_height(dc); if ((HFONT) wParam != NULL) SelectObject(dc, old_font); ReleaseDC(handle, dc); if (p->focus) { DestroyCaret(); CreateCaret(handle, NULL, CARET_WIDTH, p->character_height + 1); ShowCaret(handle); } p->invalidate = TRUE; new_layout(handle, p); GlobalUnlock(hp); return 0; } case WM_GETFONT: GlobalUnlock(hp); return (long) (WORD) p->font; case WM_PAINT: { HFONT old_font; PAINTSTRUCT paint; HDC dc = BeginPaint(handle, &paint); COLORREF normal_text_color = p->disabled ? GetSysColor(COLOR_GRAYTEXT) : GetSysColor(COLOR_WINDOWTEXT); COLORREF normal_background_color = GetSysColor(COLOR_WINDOW); COLORREF selected_text_color = GetSysColor(COLOR_HIGHLIGHTTEXT); COLORREF selected_background_color = GetSysColor(COLOR_HIGHLIGHT); HPEN pen = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_WINDOWFRAME)); HPEN old_pen = SelectObject(dc, pen); HBRUSH brush = CreateSolidBrush(GetSysColor(COLOR_WINDOW)); HBRUSH old_brush = SelectObject(dc, brush); if (p->font != NULL) old_font = SelectObject(dc, p->font); SetBkColor(dc, normal_background_color); // draw border Rectangle(dc, 0, 0, p->width, p->height); // indicate if there is text off either end if (p->layout.hidden != 0) { unsigned i; for (i = 0; i < OVERFLOW_BAR; i++) { MoveTo(dc, 1 + i, 1); LineTo(dc, 1 + i, p->height - 1); } } if (p->layout.displayed_length < p->layout.length) { unsigned i; for (i = 0; i < OVERFLOW_BAR; i++) { MoveTo(dc, p->width - 2 - i, 1); LineTo(dc, p->width - 2 - i, p->height - 1); } } // display text { unsigned i; for (i = p->layout.hidden; i < p->layout.displayed_length; i++) { if (get_bit(p->layout.select, i)) { SetTextColor(dc, selected_text_color); SetBkColor(dc, selected_background_color); } else { SetTextColor(dc, normal_text_color); SetBkColor(dc, normal_background_color); } TextOut(dc, LEFT_OFFSET + p->layout.position[i] - p->layout.position[p->layout.hidden], TOP_OFFSET, p->layout.text + i, 1); } } if (p->font != NULL) SelectObject(dc, old_font); SelectObject(dc, old_pen); DeleteObject(pen); SelectObject(dc, old_brush); DeleteObject(brush); EndPaint(handle, &paint); if (p->change) { p->change = FALSE; PostMessage(GetParent(handle), WM_COMMAND, GetMenu(handle), MAKELONG(handle, EN_CHANGE)); } p->old_layout = p->layout; p->invalidate = FALSE; GlobalUnlock(hp); return 0; } case WM_KEYDOWN: { stop_scrolling(handle, p); BOOL shift = GetKeyState(VK_SHIFT) & 0x8000; BOOL control = GetKeyState(VK_CONTROL) & 0x8000; BOOL capslock = GetKeyState(VK_CAPITAL) & 1; unsigned old_caret = p->layout.caret; if (control && (wParam == 'C' || wParam == 'X')) { copy_or_cut(handle, p, wParam == 'X'); GlobalUnlock(hp); return 0; } else if (control && wParam == 'S') { p->select = 0; p->layout.caret = p->layout.length; p->selection = TRUE; new_layout(handle, p); GlobalUnlock(hp); return 0; } else if (control && wParam == 'V') { paste(handle, p); GlobalUnlock(hp); return 0; } else if (wParam == VK_LEFT) { if (p->layout.caret != 0) p->layout.caret--; } else if (wParam == VK_RIGHT) { if (p->layout.caret < p->layout.length) p->layout.caret++; } else if (wParam == VK_HOME) { p->layout.caret = 0; } else if (wParam == VK_END) { p->layout.caret = p->layout.length; } else if (wParam == VK_BACK || wParam == VK_DELETE) { if (!p->read_only) { if (p->selection && p->select != p->layout.caret) delete_selection(p); else { p->selection = FALSE; if (wParam == VK_DELETE && control && p->layout.caret != p->layout.length) { p->layout.length = p->layout.caret; p->layout.text[p->layout.length] = 0; p->change = TRUE; p->modified = TRUE; } else if (wParam == VK_BACK && p->layout.caret != 0 || wParam == VK_DELETE && p->layout.caret != p->layout.length) { if (wParam == VK_BACK) p->layout.caret--; lstrcpy(p->layout.text + p->layout.caret, p->layout.text + p->layout.caret + 1); p->layout.length--; p->change = TRUE; p->modified = TRUE; } } new_layout(handle, p); } GlobalUnlock(hp); return 0; } else { if (control || p->read_only) wParam = 0; else if ('A' <= wParam && wParam <= 'Z') { if (!(p->uppercase || shift || capslock)) wParam += 'a' - 'A'; } else if ('0' <= wParam && wParam <= '9') { if (shift && !(lParam & 1<<24)) wParam = ")!@#$%^&*("[wParam - '0']; } else if (wParam == VK_SPACE) wParam = ' '; else if (0x60 <= wParam && wParam <= 0x69) wParam -= 0x60 - '0'; else if (wParam == 0x6A) wParam = '*'; else if (wParam == 0x6B) wParam = '+'; else if (wParam == 0x6D) wParam = '-'; else if (wParam == 0x6E) wParam = '.'; else if (wParam == 0x6F) wParam = '/'; else if (wParam == 0xBA) wParam = shift ? ':' : ';'; else if (wParam == 0xBB) wParam = shift ? '+' : '='; else if (wParam == 0xBC) wParam = shift ? '<' : ','; else if (wParam == 0xBD) wParam = shift ? '_' : '-'; else if (wParam == 0xBE) wParam = shift ? '>' : '.'; else if (wParam == 0xBF) wParam = shift ? '?' : '/'; else if (wParam == 0xC0) wParam = shift ? '~' : '`'; else if (wParam == 0xDB) wParam = shift ? '}' : ']'; else if (wParam == 0xDC) wParam = shift ? '|' : '\\'; else if (wParam == 0xDD) wParam = shift ? '{' : '['; else if (wParam == 0xDE) wParam = shift ? '"' : '\''; else wParam = 0; if (wParam != 0 && !valid_character(p, wParam)) wParam = 0; if (wParam != 0) { if (p->typeover) { if (p->layout.caret < p->layout.length) p->layout.text[p->layout.caret++] = wParam; else if (p->layout.length < p->capacity) { p->layout.text[p->layout.length++] = wParam; p->layout.text[p->layout.length] = 0; p->layout.caret++; p->change = TRUE; p->modified = TRUE; } else wParam = 0; } else { delete_selection(p); if (p->layout.length < p->capacity) insert_character(p, wParam); else wParam = 0; } } if (wParam != 0) { p->layout.text[p->layout.length] = 0; p->selection = FALSE; } new_layout(handle, p); GlobalUnlock(hp); return 0; } if (shift) { if (!p->selection) p->select = old_caret; p->selection = TRUE; } else p->selection = FALSE; new_layout(handle, p); GlobalUnlock(hp); return 0; } case WM_CUT: copy_or_cut(handle, p, TRUE); GlobalUnlock(hp); return 0; case WM_COPY: copy_or_cut(handle, p, FALSE); GlobalUnlock(hp); return 0; case WM_PASTE: paste(handle, p); GlobalUnlock(hp); return 0; case WM_CLEAR: if (!p->read_only && p->selection && p->select != p->layout.caret) { delete_selection(p); new_layout(handle, p); } GlobalUnlock(hp); return 0; case WM_TIMER: { int caret_change = 0; if (GetKeyState(VK_LBUTTON) & 0x8000) { if (p->scrolling == SCROLLING_LEFT && p->layout.caret != 0) caret_change = -1; else if (p->scrolling == SCROLLING_RIGHT && p->layout.caret < p->layout.length) { caret_change = 1; } } if (caret_change == 0) stop_scrolling(handle, p); else { p->layout.caret += caret_change; new_layout(handle, p); } GlobalUnlock(hp); return 0; } case WM_MOUSEMOVE: if (wParam & MK_LBUTTON && p->captured && LOWORD(lParam) < p->width && HIWORD(lParam) < p->height) { if (p->selection && (p->layout.hidden != 0 && LOWORD(lParam) < LEFT_OFFSET && p->layout.caret == p->layout.hidden & p->layout.caret < p->select || p->layout.displayed_length < p->layout.length && LOWORD(lParam) >= p->width - RIGHT_OFFSET && p->layout.caret == p->layout.displayed_length && p->layout.caret > p->select)) { if (p->scrolling == NO_SCROLLING) { p->timer = SetTimer(handle, NULL, SCROLLING_INTERVAL, NULL); p->scrolling = LOWORD(lParam) < LEFT_OFFSET ? SCROLLING_LEFT : SCROLLING_RIGHT; } } else { stop_scrolling(handle, p); unsigned i = get_caret_location(p, LOWORD(lParam)); if (i != p->layout.caret) { p->layout.caret = i; p->selection = TRUE; new_layout(handle, p); } } GlobalUnlock(hp); return 0; } break; case WM_ENABLE: stop_scrolling(handle, p); p->disabled = wParam == 0; p->invalidate = TRUE; new_layout(handle, p); GlobalUnlock(hp); return 0; case WM_LBUTTONDBLCLK: stop_scrolling(handle, p); if (p->captured) { ReleaseCapture(); p->captured = FALSE; } p->select = 0; p->layout.caret = p->layout.length; new_layout(handle, p); GlobalUnlock(hp); return 0; case WM_LBUTTONDOWN: stop_scrolling(handle, p); SetCapture(handle); p->captured = TRUE; if (!p->focus) { SetFocus(handle); p->layout.hidden = 0; } p->layout.caret = get_caret_location(p, LOWORD(lParam)); p->select = p->layout.caret; p->selection = TRUE; new_layout(handle, p); GlobalUnlock(hp); return 0; case WM_LBUTTONUP: if (p->captured) { ReleaseCapture(); p->captured = FALSE; } GlobalUnlock(hp); return 0; case WM_SETFOCUS: p->focus = TRUE; CreateCaret(handle, NULL, CARET_WIDTH, p->character_height); ShowCaret(handle); if (p->noselectall) { p->layout.caret = 0; p->selection = FALSE; } else { p->select = 0; p->layout.caret = p->layout.length; p->selection = TRUE; } new_layout(handle, p); PostMessage(GetParent(handle), WM_COMMAND, GetMenu(handle), MAKELONG(handle, EN_SETFOCUS)); GlobalUnlock(hp); return 0; case WM_KILLFOCUS: stop_scrolling(handle, p); DestroyCaret(); p->layout.caret = p->layout.hidden = 0; p->selection = FALSE; new_layout(handle, p); p->focus = FALSE; PostMessage(GetParent(handle), WM_COMMAND, GetMenu(handle), MAKELONG(handle, EN_KILLFOCUS)); GlobalUnlock(hp); return 0; case WM_GETDLGCODE: GlobalUnlock(hp); return DLGC_WANTCHARS + DLGC_WANTARROWS; case WM_GETTEXT: { unsigned i; for (i = 0; i < p->layout.length && i + 1 < wParam; i++) ((char far *) lParam)[i] = p->layout.text[i]; ((char far *) lParam)[i++] = 0; GlobalUnlock(hp); return i; } case WM_GETTEXTLENGTH: { unsigned length = p->layout.length; GlobalUnlock(hp); return length; } case WM_DESTROY: stop_scrolling(handle, p); DestroyCaret(); GlobalUnlock(hp); GlobalFree(hp); return 0; case WM_ERASEBKGND: p->invalidate = TRUE; GlobalUnlock(hp); return 0; } GlobalUnlock(hp); return DefWindowProc(handle, message, wParam, lParam); } =============================== LINEIN.H =============================== #define EM_SETTYPEOVER (WM_USER+35) #define EM_SETUPPERCASE (WM_USER+36) #define EM_SETINVALID (WM_USER+37) #define EM_SETNOSELECTALL (WM_USER+38) #define ES_TYPEOVER 0x00002000L #define ES_NOSELECTALL 0x00004000L =============================== LINETEST.CPP =============================== // Line Input Control test program #include #include "linetest.h" #include "linein.h" extern "C" { long far pascal _export WndProc(HWND, UINT, WORD, LONG); int pascal WinMain(HINSTANCE, HINSTANCE, LPSTR, int); } static BYTE ZEROS[12]; static BYTE DIGITS_ONLY[12] = {0xFF, 0xFF, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; long far pascal _export WndProc(HWND hwnd, UINT message, WORD wParam, LONG lParam) { switch (message) { case WM_CREATE: return 0; case WM_COMMAND: switch (wParam) { case COPY12: { char s[130]; GetDlgItemText(hwnd, LINEINPUT1, s, sizeof(s)); SetDlgItemText(hwnd, LINEINPUT2, s); return 0; } case ENABLE1: EnableWindow(GetDlgItem(hwnd, LINEINPUT1), SendDlgItemMessage(hwnd, ENABLE1, BM_GETCHECK, 0, 0L)); return 0; case UPPERCASE1: SendDlgItemMessage(hwnd, LINEINPUT1, EM_SETUPPERCASE, SendDlgItemMessage(hwnd, UPPERCASE1, BM_GETCHECK, 0, 0L), 0L); return 0; case DIGITSONLY1: SendDlgItemMessage(hwnd, LINEINPUT1, EM_SETINVALID, 0, (long) (char far *) (SendDlgItemMessage(hwnd, DIGITSONLY1, BM_GETCHECK, 0, 0L) ? DIGITS_ONLY : ZEROS)); return 0; case TYPEOVER1: SendDlgItemMessage(hwnd, LINEINPUT1, EM_SETTYPEOVER, SendDlgItemMessage(hwnd, TYPEOVER1, BM_GETCHECK, 0, 0L), 0L); return 0; case READONLY1: SendDlgItemMessage(hwnd, LINEINPUT1, EM_SETREADONLY, SendDlgItemMessage(hwnd, READONLY1, BM_GETCHECK, 0, 0L), 0L); return 0; case SELECTALL1: SendDlgItemMessage(hwnd, LINEINPUT1, EM_SETNOSELECTALL, !SendDlgItemMessage(hwnd, SELECTALL1, BM_GETCHECK, 0, 0L), 0L); return 0; case COPY21: { char s[130]; GetDlgItemText(hwnd, LINEINPUT2, s, sizeof(s)); SetDlgItemText(hwnd, LINEINPUT1, s); return 0; } case ENABLE2: EnableWindow(GetDlgItem(hwnd, LINEINPUT2), SendDlgItemMessage(hwnd, ENABLE2, BM_GETCHECK, 0, 0L)); return 0; case UPPERCASE2: SendDlgItemMessage(hwnd, LINEINPUT2, EM_SETUPPERCASE, SendDlgItemMessage(hwnd, UPPERCASE2, BM_GETCHECK, 0, 0L), 0L); return 0; case DIGITSONLY2: SendDlgItemMessage(hwnd, LINEINPUT2, EM_SETINVALID, 0, (long) (char far *) (SendDlgItemMessage(hwnd, DIGITSONLY2, BM_GETCHECK, 0, 0L) ? DIGITS_ONLY : ZEROS)); return 0; case TYPEOVER2: SendDlgItemMessage(hwnd, LINEINPUT2, EM_SETTYPEOVER, SendDlgItemMessage(hwnd, TYPEOVER2, BM_GETCHECK, 0, 0L), 0L); return 0; case READONLY2: SendDlgItemMessage(hwnd, LINEINPUT2, EM_SETREADONLY, SendDlgItemMessage(hwnd, READONLY2, BM_GETCHECK, 0, 0L), 0L); return 0; case SELECTALL2: SendDlgItemMessage(hwnd, LINEINPUT2, EM_SETNOSELECTALL, !SendDlgItemMessage(hwnd, SELECTALL2, BM_GETCHECK, 0, 0L), 0L); return 0; case CUT: { HWND focus = GetFocus(); if (focus == GetDlgItem(hwnd, LINEINPUT1)) SendDlgItemMessage(hwnd, LINEINPUT1, WM_CUT, 0, 0L); else if (focus == GetDlgItem(hwnd, LINEINPUT2)) SendDlgItemMessage(hwnd, LINEINPUT2, WM_CUT, 0, 0L); return 0; } case COPY: { HWND focus = GetFocus(); if (focus == GetDlgItem(hwnd, LINEINPUT1)) SendDlgItemMessage(hwnd, LINEINPUT1, WM_COPY, 0, 0L); else if (focus == GetDlgItem(hwnd, LINEINPUT2)) SendDlgItemMessage(hwnd, LINEINPUT2, WM_COPY, 0, 0L); return 0; } case PASTE: { HWND focus = GetFocus(); if (focus == GetDlgItem(hwnd, LINEINPUT1)) SendDlgItemMessage(hwnd, LINEINPUT1, WM_PASTE, 0, 0L); else if (focus == GetDlgItem(hwnd, LINEINPUT2)) SendDlgItemMessage(hwnd, LINEINPUT2, WM_PASTE, 0, 0L); return 0; } case CLEAR: { HWND focus = GetFocus(); if (focus == GetDlgItem(hwnd, LINEINPUT1)) SendDlgItemMessage(hwnd, LINEINPUT1, WM_CLEAR, 0, 0L); else if (focus == GetDlgItem(hwnd, LINEINPUT2)) SendDlgItemMessage(hwnd, LINEINPUT2, WM_CLEAR, 0, 0L); return 0; } } break; case WM_INITMENUPOPUP: if (HIWORD(lParam) == 0) { int control; int cut_copy_action; HWND focus = GetFocus(); if (focus == GetDlgItem(hwnd, LINEINPUT1)) control = LINEINPUT1; else if (focus == GetDlgItem(hwnd, LINEINPUT2)) control = LINEINPUT2; else control = 0; if (control != 0 && SendDlgItemMessage(hwnd, control, EM_GETSEL, 0, 0L) != 0L) cut_copy_action = MF_BYPOSITION | MF_ENABLED; else cut_copy_action = MF_BYPOSITION | MF_GRAYED; EnableMenuItem((HMENU) wParam, 0, cut_copy_action); EnableMenuItem((HMENU) wParam, 1, cut_copy_action); EnableMenuItem((HMENU) wParam, 2, (control != 0 && IsClipboardFormatAvailable(CF_TEXT) ? MF_BYPOSITION | MF_ENABLED : MF_BYPOSITION | MF_GRAYED)); EnableMenuItem((HMENU) wParam, 3, cut_copy_action); } return 0; case WM_CLOSE: DestroyWindow(hwnd); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefDlgProc (hwnd, message, wParam, lParam); } #pragma argsused int pascal WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { HWND hwnd; MSG msg; if (!hPrevInstance) { WNDCLASS wndclass; wndclass.style = 0; wndclass.lpfnWndProc = (WNDPROC) WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = DLGWINDOWEXTRA; wndclass.hInstance = hInstance; wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndclass.hCursor = LoadCursor (NULL, IDC_ARROW); wndclass.hbrBackground = NULL; wndclass.lpszMenuName = "TEST_MENU"; wndclass.lpszClassName = "LINETEST"; RegisterClass (&wndclass); } hwnd = CreateDialog(hInstance, "LINETEST", 0, NULL); SendDlgItemMessage(hwnd, ENABLE1, BM_SETCHECK, 1, 0L); SendDlgItemMessage(hwnd, ENABLE2, BM_SETCHECK, 1, 0L); SendDlgItemMessage(hwnd, SELECTALL1, BM_SETCHECK, 1, 0L); SendDlgItemMessage(hwnd, SELECTALL2, BM_SETCHECK, 1, 0L); ShowWindow(hwnd, nCmdShow == SW_SHOWMAXIMIZED ? SW_SHOW : nCmdShow); UpdateWindow(hwnd); while (GetMessage(&msg, NULL, 0, 0)) if (!IsDialogMessage (hwnd, &msg)) DispatchMessage (&msg); return msg.wParam; } =============================== LINETEST.H =============================== #define LINEINPUT1 101 #define COPY12 102 #define ENABLE1 103 #define UPPERCASE1 104 #define DIGITSONLY1 105 #define TYPEOVER1 106 #define READONLY1 107 #define SELECTALL1 108 #define LINEINPUT2 201 #define COPY21 202 #define ENABLE2 203 #define UPPERCASE2 204 #define DIGITSONLY2 205 #define TYPEOVER2 206 #define READONLY2 207 #define SELECTALL2 208 #define CUT 300 #define COPY 301 #define PASTE 302 #define CLEAR 303 =============================== LINETEST.RC =============================== #include "linetest.h" LINETEST DIALOG 6, 15, 241, 200 STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CLASS "LINETEST" CAPTION "Line Input Control Test" FONT 8, "MS Sans Serif" { CONTROL "", LINEINPUT1, "LINEINPUT", 0 | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 13, 16, 96, 12 PUSHBUTTON "Copy ->", COPY12, 34, 36, 50, 14 CHECKBOX "Enable", ENABLE1, 30, 62, 60, 12, BS_AUTOCHECKBOX | WS_TABSTOP CHECKBOX "Upper Case", UPPERCASE1, 30, 82, 60, 12, BS_AUTOCHECKBOX | WS_TABSTOP CHECKBOX "Digits Only", DIGITSONLY1, 30, 102, 60, 12, BS_AUTOCHECKBOX | WS_TABSTOP CHECKBOX "Typeover", TYPEOVER1, 30, 122, 60, 12, BS_AUTOCHECKBOX | WS_TABSTOP CHECKBOX "Read Only", READONLY1, 30, 142, 60, 12, BS_AUTOCHECKBOX | WS_TABSTOP CHECKBOX "Select All", SELECTALL1, 30, 162, 60, 12, BS_AUTOCHECKBOX | WS_TABSTOP CONTROL "", LINEINPUT2, "LINEINPUT", 0 | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 131, 16, 96, 12 PUSHBUTTON "<- Copy", COPY21, 154, 36, 50, 14 CHECKBOX "Enable", ENABLE2, 148, 62, 62, 12, BS_AUTOCHECKBOX | WS_TABSTOP CHECKBOX "Upper Case", UPPERCASE2, 148, 82, 60, 12, BS_AUTOCHECKBOX | WS_TABSTOP CHECKBOX "Digits Only", DIGITSONLY2, 148, 102, 60, 12, BS_AUTOCHECKBOX | WS_TABSTOP CHECKBOX "Typeover", TYPEOVER2, 148, 122, 60, 12, BS_AUTOCHECKBOX | WS_TABSTOP CHECKBOX "Read Only", READONLY2, 148, 142, 60, 12, BS_AUTOCHECKBOX | WS_TABSTOP CHECKBOX "Select All", SELECTALL2, 148, 162, 60, 12, BS_AUTOCHECKBOX | WS_TABSTOP } TEST_MENU MENU BEGIN POPUP "&Edit" BEGIN MENUITEM "Cu&t", CUT MENUITEM "&Copy", COPY MENUITEM "&Paste", PASTE MENUITEM "&Delete", CLEAR END END =============================== MARQTEST.CPP =============================== // Windows Marquee Control Test Program #include #include "marquee.h" #include "marqtest.h" extern "C" { long far pascal _export WndProc(HWND, UINT, WORD, LONG); int pascal WinMain(HINSTANCE, HINSTANCE, LPSTR, int); } long far pascal _export WndProc(HWND hwnd, UINT message, WORD wParam, LONG lParam) { switch (message) { case WM_CREATE: return 0; case WM_COMMAND: switch (wParam) { case INCREASE: SendDlgItemMessage(hwnd, MARQUEE, MA_SETSPEED, (int) SendDlgItemMessage(hwnd, MARQUEE, MA_GETSPEED, 0, 0L) + 1, 0L); SetDlgItemInt(hwnd, SPEED, (int) SendDlgItemMessage(hwnd, MARQUEE, MA_GETSPEED, 0, 0L), TRUE); return 0; case DECREASE: SendDlgItemMessage(hwnd, MARQUEE, MA_SETSPEED, (int) SendDlgItemMessage(hwnd, MARQUEE, MA_GETSPEED, 0, 0L) - 1, 0L); SetDlgItemInt(hwnd, SPEED, (int) SendDlgItemMessage(hwnd, MARQUEE, MA_GETSPEED, 0, 0L), TRUE); return 0; } break; case WM_CLOSE: DestroyWindow(hwnd); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefDlgProc (hwnd, message, wParam, lParam); } #pragma argsused int pascal WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { HWND hwnd; MSG msg; if (!hPrevInstance) { WNDCLASS wndclass; wndclass.style = 0; wndclass.lpfnWndProc = (WNDPROC) WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = DLGWINDOWEXTRA; wndclass.hInstance = hInstance; wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = NULL; wndclass.lpszMenuName = NULL; wndclass.lpszClassName = "MARQUEETEST"; RegisterClass (&wndclass); } hwnd = CreateDialog(hInstance, "MARQUEETEST", 0, NULL); SetDlgItemInt(hwnd, SPEED, (int) SendDlgItemMessage(hwnd, MARQUEE, MA_GETSPEED, 0, 0L), TRUE); ShowWindow(hwnd, nCmdShow == SW_SHOWMAXIMIZED ? SW_SHOW : nCmdShow); UpdateWindow(hwnd); while (GetMessage(&msg, NULL, 0, 0)) if (!IsDialogMessage (hwnd, &msg)) DispatchMessage (&msg); return msg.wParam; } =============================== MARQTEST.H =============================== #define MARQUEE 101 #define INCREASE 102 #define DECREASE 103 #define SPEED 104 =============================== MARQTEST.RC =============================== #include "marqtest.h" MARQUEETEST DIALOG 6, 15, 176, 84 STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CLASS "MARQUEETEST" CAPTION "Marquee Test Program" FONT 8, "MS Sans Serif" { CONTROL "Now showing\nMARQUEE control", MARQUEE, "MARQUEE", 2 | WS_CHILD | WS_VISIBLE, 12, 30, 75, 32 PUSHBUTTON "Increase", INCREASE, 112, 16, 50, 14 CTEXT "speed", -1, 113, 36, 48, 10 CTEXT "", SPEED, 123, 45, 28, 10, SS_CENTER | WS_BORDER | WS_GROUP PUSHBUTTON "Decrease", DECREASE, 112, 60, 50, 14 } =============================== MARQUEE.CPP =============================== // Marquee Control #include // compilation parameters const unsigned TIMER_INTERVAL = 100; // milliseconds const unsigned BORDER_WIDTH = 6; // pixels const int BRUSH_SIZE = 8; // pixels const int SCALE_FACTOR = 10; #include "marquee.h" extern "C" { long far pascal _export MarqueeProc(HWND, UINT, WPARAM, LPARAM); } /*----------------------------------------------------------------------------- This function registers the MARQUEE class. It is called automatically when the program starts up. The external variables _hPrev and _hInstance duplicate the arguments hPrevInstance and hInstance, which are passed to WinMain(). If the startup code does not supply these external variables, you must pass the arguments to this function and call it explicitly before invoking any MARQUEE control. -----------------------------------------------------------------------------*/ extern HINSTANCE _hPrev, _hInstance; static void register_marquee(void) { if (!_hPrev) { WNDCLASS w; w.cbClsExtra = 0; w.cbWndExtra = sizeof(HGLOBAL); w.hbrBackground = (HBRUSH) COLOR_WINDOW + 1; w.hCursor = LoadCursor(NULL, IDC_ARROW); w.hIcon = NULL; w.hInstance = _hInstance; w.lpfnWndProc = MarqueeProc; w.lpszClassName = "MARQUEE"; w.lpszMenuName = NULL; w.style = CS_DBLCLKS; RegisterClass(&w); } } #pragma startup register_marquee struct MARQUEE { int position; int speed; unsigned timer; RECT top; RECT bottom; RECT left; RECT right; HFONT font; char text[1]; }; /*----------------------------------------------------------------------------- This function receives all messages directed to the control. -----------------------------------------------------------------------------*/ long far pascal _export MarqueeProc(HWND handle, UINT message, WPARAM wParam, LPARAM lParam) { MARQUEE far *p; HGLOBAL hp = GetWindowWord(handle, 0); if (hp != NULL) p = (MARQUEE far *) GlobalLock(hp); switch (message) { case WM_CREATE: { hp = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof(MARQUEE) + lstrlen(((CREATESTRUCT far *) lParam)->lpszName)); if (hp == NULL) return -1L; p = (MARQUEE far *) GlobalLock(hp); p->speed = 20; { unsigned height = ((CREATESTRUCT far *) lParam)->cy; unsigned width = ((CREATESTRUCT far *) lParam)->cx; p->top.top = 0; p->top.bottom = BORDER_WIDTH-1; p->top.left = 0; p->top.right = width; p->bottom.top = height - (BORDER_WIDTH-1); p->bottom.bottom = height; p->bottom.left = 0; p->bottom.right = width; p->left.top = BORDER_WIDTH-2; p->left.bottom = height - (BORDER_WIDTH-2); p->left.left = 0; p->left.right = BORDER_WIDTH-1; p->right.top = BORDER_WIDTH-2; p->right.bottom = height - (BORDER_WIDTH-2); p->right.left = width - (BORDER_WIDTH-1); p->right.right = width; } p->timer = SetTimer(handle, 1, TIMER_INTERVAL, NULL); lstrcpy(p->text, ((CREATESTRUCT far *) lParam)->lpszName); SetWindowWord(handle, 0, (WORD) hp); GlobalUnlock(hp); return 0; } case WM_SETTEXT: { HGLOBAL new_hp = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof(MARQUEE) + lstrlen((char far *) lParam)); if (new_hp != NULL) { MARQUEE far *new_p = (MARQUEE far *) GlobalLock(new_hp); *new_p = *p; lstrcpy(new_p->text, (char far *) lParam); InvalidateRect(handle, NULL, TRUE); GlobalUnlock(hp); GlobalFree(hp); hp = new_hp; } SetWindowWord(handle, 0, (WORD) hp); GlobalUnlock(hp); return 0; } case WM_SETFONT: { p->font = (HFONT) wParam; InvalidateRect(handle, NULL, TRUE); GlobalUnlock(hp); return 0; } case WM_GETFONT: GlobalUnlock(hp); return (long) (WORD) p->font; case WM_PAINT: { PAINTSTRUCT paint; HDC dc = BeginPaint(handle, &paint); // display text { HFONT old_font; if (p->font != NULL) old_font = SelectObject(dc, p->font); SetTextColor(dc, GetSysColor(COLOR_WINDOWTEXT)); SetBkColor(dc, GetSysColor(COLOR_WINDOW)); TEXTMETRIC tm; GetTextMetrics(dc, &tm); char far *text = p->text; int y = BORDER_WIDTH+1; while (*text != 0) { unsigned length = 0; while (text[length] != 0 && text[length] != '\n') length++; TextOut(dc, BORDER_WIDTH+1, y, text, length); text += length; if (*text == '\n') text++; y += tm.tmHeight; } if (p->font != NULL) SelectObject(dc, old_font); } // draw border { HPEN old_pen = SelectObject(dc, GetStockObject(NULL_PEN)); int HORIZONTAL_PATTERN[] = {0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F}; HBITMAP horizontal_bitmap = CreateBitmap(BRUSH_SIZE, BRUSH_SIZE, 1, 1, HORIZONTAL_PATTERN); HBRUSH horizontal_brush = CreatePatternBrush(horizontal_bitmap); int VERTICAL_PATTERN[] = {0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF}; HBITMAP vertical_bitmap = CreateBitmap(BRUSH_SIZE, BRUSH_SIZE, 1, 1, VERTICAL_PATTERN); HBRUSH vertical_brush = CreatePatternBrush(vertical_bitmap); HBRUSH old_brush; unsigned offset = p->position/SCALE_FACTOR; UnrealizeObject(horizontal_brush); SetBrushOrg(dc, offset, 0); old_brush = SelectObject(dc, horizontal_brush); Rectangle(dc, p->top.left, p->top.top, p->top.right, p->top.bottom); SelectObject(dc, old_brush); UnrealizeObject(vertical_brush); SetBrushOrg(dc, 0, offset); SelectObject(dc, vertical_brush); Rectangle(dc, p->right.left, p->right.top, p->right.right, p->right.bottom); SelectObject(dc, old_brush); if (offset != 0) offset = BRUSH_SIZE - offset; UnrealizeObject(horizontal_brush); SetBrushOrg(dc, offset, 0); SelectObject(dc, horizontal_brush); Rectangle(dc, p->bottom.left, p->bottom.top, p->bottom.right, p->bottom.bottom); SelectObject(dc, old_brush); UnrealizeObject(vertical_brush); SetBrushOrg(dc, 0, offset); SelectObject(dc, vertical_brush); Rectangle(dc, p->left.left, p->left.top, p->left.right, p->left.bottom); SelectObject(dc, old_brush); SelectObject(dc, old_pen); DeleteObject(vertical_brush); DeleteObject(vertical_bitmap); DeleteObject(horizontal_brush); DeleteObject(horizontal_bitmap); } EndPaint(handle, &paint); GlobalUnlock(hp); return 0; } case MA_GETSPEED: { long n = p->speed; GlobalUnlock(hp); return n; } case MA_SETSPEED: if ((short) wParam > SCALE_FACTOR*BRUSH_SIZE - 1) wParam = SCALE_FACTOR*BRUSH_SIZE - 1; else if ((short) wParam < 1 - SCALE_FACTOR*BRUSH_SIZE) wParam = 1 - SCALE_FACTOR*BRUSH_SIZE; p->speed = (short) wParam; if (p->timer != NULL) { KillTimer(handle, p->timer); p->timer = NULL; } if (p->speed != 0) p->timer = SetTimer(handle, 1, TIMER_INTERVAL, NULL); // fall through to case WM_TIMER case WM_TIMER: { p->position += p->speed; while (p->position < 0) p->position += SCALE_FACTOR*BRUSH_SIZE; while (p->position >= SCALE_FACTOR*BRUSH_SIZE) p->position -= SCALE_FACTOR*BRUSH_SIZE; InvalidateRect(handle, &p->top, TRUE); InvalidateRect(handle, &p->bottom, TRUE); InvalidateRect(handle, &p->left, TRUE); InvalidateRect(handle, &p->right, TRUE); GlobalUnlock(hp); return 0; } case WM_GETTEXT: { unsigned i; for (i = 0; p->text[i] != 0 && i + 1 < wParam; i++) ((char far *) lParam)[i] = p->text[i]; ((char far *) lParam)[i++] = 0; GlobalUnlock(hp); return i; } case WM_GETTEXTLENGTH: { unsigned length = lstrlen(p->text); GlobalUnlock(hp); return length; } case WM_DESTROY: if (p->timer != NULL) KillTimer(handle, p->timer); GlobalUnlock(hp); GlobalFree(hp); return 0; //case WM_ERASEBKGND: // p->invalidate = TRUE; // GlobalUnlock(hp); // return 0; } GlobalUnlock(hp); return DefWindowProc(handle, message, wParam, lParam); } =============================== MARQUEE.H =============================== #define MA_SETSPEED (WM_USER+0) #define MA_GETSPEED (WM_USER+1) =============================== PBUTTEST.CPP =============================== // Windows Picture Button Control Test Program #define OEMRESOURCE 1 #include extern "C" { long far pascal _export WndProc(HWND, UINT, WORD, LONG); int pascal WinMain(HINSTANCE, HINSTANCE, LPSTR, int); } static struct { long number; char *name; } OBM[] = { {OBM_CLOSE, "OBM_CLOSE"}, {OBM_UPARROW, "OBM_UPARROW"}, {OBM_DNARROW, "OBM_DNARROW"}, {OBM_RGARROW, "OBM_RGARROW"}, {OBM_LFARROW, "OBM_LFARROW"}, {OBM_REDUCE, "OBM_REDUCE"}, {OBM_ZOOM, "OBM_ZOOM"}, {OBM_RESTORE, "OBM_RESTORE"}, {OBM_REDUCED, "OBM_REDUCED"}, {OBM_ZOOMD, "OBM_ZOOMD"}, {OBM_RESTORED, "OBM_RESTORED"}, {OBM_UPARROWD, "OBM_UPARROWD"}, {OBM_DNARROWD, "OBM_DNARROWD"}, {OBM_RGARROWD, "OBM_RGARROWD"}, {OBM_LFARROWD, "OBM_LFARROWD"}, {OBM_MNARROW, "OBM_MNARROW"}, {OBM_COMBO, "OBM_COMBO"}, {OBM_UPARROWI, "OBM_UPARROWI"}, {OBM_DNARROWI, "OBM_DNARROWI"}, {OBM_RGARROWI, "OBM_RGARROWI"}, {OBM_LFARROWI, "OBM_LFARROWI"}, {OBM_OLD_CLOSE, "OBM_OLD_CLOSE"}, {OBM_SIZE, "OBM_SIZE"}, {OBM_OLD_UPARROW, "OBM_OLD_UPARROW"}, {OBM_OLD_DNARROW, "OBM_OLD_DNARROW"}, {OBM_OLD_RGARROW, "OBM_OLD_RGARROW"}, {OBM_OLD_LFARROW, "OBM_OLD_LFARROW"}, {OBM_BTSIZE, "OBM_BTSIZE"}, {OBM_CHECK, "OBM_CHECK"}, {OBM_CHECKBOXES, "OBM_CHECKBOXES"}, {OBM_BTNCORNERS, "OBM_BTNCORNERS"}, {OBM_OLD_REDUCE, "OBM_OLD_REDUCE"}, {OBM_OLD_ZOOM, "OBM_OLD_ZOOM"}, {OBM_OLD_RESTORE, "OBM_OLD_RESTORE",} }; long far pascal _export WndProc(HWND hwnd, UINT message, WORD wParam, LONG lParam) { switch (message) { case WM_CREATE: return 0; case WM_COMMAND: switch(wParam) { case 101: DestroyWindow(hwnd); return 0; case 102: { static unsigned i; HBITMAP bitmap = LoadBitmap(NULL, (const char far *) OBM[i].number); SendDlgItemMessage(hwnd, 101, BM_SETSTYLE, 0, MAKELPARAM(bitmap, bitmap)); SetDlgItemText(hwnd, 106, OBM[i].name); if (++i == sizeof(OBM)/sizeof(OBM[0])) i = 0; return 0; } case 105: { static BOOL enabled = TRUE; enabled = !enabled; EnableWindow(GetDlgItem(hwnd, 104), enabled); return 0; } } break; case WM_CLOSE: DestroyWindow(hwnd); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefDlgProc (hwnd, message, wParam, lParam); } #pragma argsused int pascal WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { HWND hwnd; MSG msg; if (!hPrevInstance) { WNDCLASS wndclass; wndclass.style = 0; wndclass.lpfnWndProc = (WNDPROC) WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = DLGWINDOWEXTRA; wndclass.hInstance = hInstance; wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = NULL; wndclass.lpszMenuName = NULL; wndclass.lpszClassName = "PBUTTONTEST"; RegisterClass (&wndclass); } hwnd = CreateDialog(hInstance, "PBUTTONTEST", 0, NULL); SetFocus(GetDlgItem(hwnd, 102)); SendDlgItemMessage(hwnd, 101, BM_SETSTATE, (WPARAM) GetDlgItem(hwnd, 107), (LPARAM)(char far *) "Terminate test"); SendDlgItemMessage(hwnd, 102, BM_SETSTATE, (WPARAM) GetDlgItem(hwnd, 107), (LPARAM)(char far *) "Show OBM bitmaps"); SendDlgItemMessage(hwnd, 103, BM_SETSTATE, (WPARAM) GetDlgItem(hwnd, 107), (LPARAM)(char far *) "Has no tabstop"); SendDlgItemMessage(hwnd, 104, BM_SETSTATE, (WPARAM) GetDlgItem(hwnd, 107), (LPARAM)(char far *) "Enabled or disabled by button 5"); SendDlgItemMessage(hwnd, 105, BM_SETSTATE, (WPARAM) GetDlgItem(hwnd, 107), (LPARAM)(char far *) "Enable or disable button 4"); ShowWindow(hwnd, nCmdShow == SW_SHOWMAXIMIZED ? SW_SHOW : nCmdShow); UpdateWindow(hwnd); while (GetMessage(&msg, NULL, 0, 0)) if (!IsDialogMessage (hwnd, &msg)) DispatchMessage (&msg); return msg.wParam; } =============================== PBUTTEST.RC =============================== PBUTTONTEST DIALOG 29, 69, 264, 70 STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CLASS "PBUTTONTEST" CAPTION "Picture Button Test Program" FONT 8, "MS Sans Serif" { CONTROL "NONEXISTENT", 101, "PICTUREBUTTON", 2 | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 18, 12, 60, 36 CONTROL "", 106, STATIC, WS_CHILD | WS_VISIBLE | WS_BORDER | SS_LEFT, 12, 54, 90, 10 CONTROL "CHECKMARK", 102, "PICTUREBUTTON", 2 | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 90, 21, 27, 21 CONTROL "CHECKMARK", 103, "PICTUREBUTTON", 0 | WS_CHILD | WS_VISIBLE, 132, 15, 20, 32 CONTROL "SAFE", 104, "PICTUREBUTTON", 2 | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 165, 18, 33, 27 CONTROL "SAFE", 105, "PICTUREBUTTON", 2 | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 210, 12, 42, 36 CONTROL "", 107, STATIC, WS_CHILD | WS_VISIBLE | WS_BORDER | SS_CENTER, 116, 53, 138, 10 } CHECKMARK BITMAP { '42 4D C6 00 00 00 00 00 00 00 76 00 00 00 28 00' '00 00 0A 00 00 00 0A 00 00 00 01 00 04 00 00 00' '00 00 50 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 10 00 00 00 00 00 00 00 00 00 80 00 00 80' '00 00 00 80 80 00 80 00 00 00 80 00 80 00 80 80' '00 00 80 80 80 00 C0 C0 C0 00 00 00 FF 00 00 FF' '00 00 00 FF FF 00 FF 00 00 00 FF 00 FF 00 FF FF' '00 00 FF FF FF 00 88 81 88 88 88 00 00 00 88 11' '18 88 88 00 00 00 81 11 11 88 88 00 00 00 11 18' '81 88 88 00 00 00 11 88 81 18 88 00 00 00 88 88' '88 18 88 00 10 11 88 88 88 11 88 00 14 14 88 88' '88 81 88 00 14 14 88 88 88 81 18 00 04 05 88 88' '88 88 18 00 00 41' } CHECKMARKX BITMAP { '42 4D C6 00 00 00 00 00 00 00 76 00 00 00 28 00' '00 00 0A 00 00 00 0A 00 00 00 01 00 04 00 00 00' '00 00 50 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 10 00 00 00 00 00 00 00 00 00 80 00 00 80' '00 00 00 80 80 00 80 00 00 00 80 00 80 00 80 80' '00 00 80 80 80 00 C0 C0 C0 00 00 00 FF 00 00 FF' '00 00 00 FF FF 00 FF 00 00 00 FF 00 FF 00 FF FF' '00 00 FF FF FF 00 88 80 88 88 88 00 00 00 88 88' '88 88 88 00 00 00 88 08 08 88 88 00 00 00 80 88' '80 88 88 00 00 00 88 88 88 88 88 00 00 00 08 88' '88 08 88 00 10 11 88 88 88 88 88 00 14 14 88 88' '88 80 88 00 14 14 88 88 88 88 08 00 04 05 88 88' '88 88 88 00 00 41' } SAFEX BITMAP BEGIN '42 4D BE 00 00 00 00 00 00 00 3E 00 00 00 28 00' '00 00 20 00 00 00 20 00 00 00 01 00 01 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 FF FF FF 00 E3 FF' 'FC 7F D1 FF FA 3F C0 00 00 3F BF FF FF BF 7F FF' 'FF C7 7F FF FF C3 78 00 03 D3 77 FF FD CB 63 FF' 'FD D5 6B F0 FD C9 6B EF 7D D5 6B D1 BD C9 63 CC' 'BD D5 77 CC BD C9 77 D1 BD D5 77 EF 7D C9 77 F0' 'FD D5 63 FF FD C9 6B E0 0D D5 6B DF F5 C9 6B E0' '35 D5 63 FF CD C9 77 FF FD D5 78 00 03 C9 7F FF' 'FF D5 7F FF FF C9 BF FF FF 95 C0 00 00 29 EF FF' 'FF D5 F7 FF FF E9 FB FF FF F3 FC 00 00 07' END SAFE BITMAP BEGIN '42 4D 76 02 00 00 00 00 00 00 76 00 00 00 28 00' '00 00 20 00 00 00 20 00 00 00 01 00 04 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 80 00 00 80' '00 00 00 80 80 00 80 00 00 00 80 00 80 00 80 80' '00 00 80 80 80 00 C0 C0 C0 00 00 00 FF 00 00 FF' '00 00 00 FF FF 00 FF 00 00 00 FF 00 FF 00 FF FF' '00 00 FF FF FF 00 88 80 00 88 88 88 88 88 88 88' '88 00 08 88 88 88 88 0F 77 08 88 88 88 88 88 88' '80 F7 70 88 88 88 88 00 00 00 00 00 00 00 00 00' '00 00 00 88 88 88 80 77 77 77 77 77 77 77 77 77' '77 77 70 88 88 88 0F 78 88 88 88 88 88 88 88 88' '88 88 77 00 08 88 0F 88 88 88 88 88 88 88 88 88' '88 88 87 70 70 88 0F 88 80 00 00 00 00 00 00 00' '00 88 88 07 00 88 0F 88 07 77 77 77 77 77 77 77' '77 08 88 07 70 88 0F 80 00 78 88 88 87 77 78 88' '87 08 88 07 77 08 0F 87 80 78 88 88 00 00 77 88' '87 08 88 07 77 08 0F 87 80 78 88 80 FF FF 07 78' '87 08 88 07 77 08 0F 87 80 78 88 0F 00 0F F0 78' '87 08 88 07 77 08 0F 87 70 88 88 00 DD 00 F0 78' '87 08 88 07 77 08 0F 88 0F 88 88 00 FD 00 F0 78' '87 08 88 07 77 08 0F 88 0F 88 88 0F 00 0F F0 88' '87 08 88 07 77 08 0F 88 0F 88 88 80 FF FF 08 88' '87 08 88 07 77 08 0F 88 07 78 88 88 00 00 88 88' '87 08 88 07 77 08 0F 80 00 78 88 88 77 77 77 77' '77 08 88 07 77 08 0F 87 80 78 88 80 00 00 00 00' '77 08 88 07 77 08 0F 87 80 78 88 0E EE EE EE 66' '07 08 88 07 77 08 0F 87 80 78 88 80 00 00 00 E6' '07 08 88 07 77 08 0F 87 70 88 88 88 88 88 88 00' '87 08 88 07 77 08 0F 88 0F FF FF FF FF FF FF FF' 'F7 08 88 07 77 08 0F 88 80 00 00 00 00 00 00 00' '00 88 88 07 77 08 0F 88 88 88 88 88 88 88 88 88' '88 88 88 07 77 08 0F F8 88 88 88 88 88 88 88 88' '88 88 87 77 77 08 80 FF 88 88 88 88 88 88 88 88' '88 88 F7 87 77 08 88 0F FF FF FF FF FF FF FF FF' 'FF FF 78 78 77 08 88 80 88 88 88 88 88 88 88 88' '88 88 87 87 87 08 88 88 08 88 88 88 88 88 88 88' '88 88 88 78 78 08 88 88 80 88 88 88 88 88 88 88' '88 88 88 87 80 88 88 88 88 00 00 00 00 00 00 00' '00 00 00 00 08 88' END =============================== PBUTTON.CPP =============================== // Windows Picture Button #include const unsigned MAX_NAME_LENGTH = 31; const unsigned FOCUS_BORDER_WIDTH = 2; extern "C" { long far pascal _export PictureButtonProc(HWND, UINT, WPARAM, LPARAM); } /*----------------------------------------------------------------------------- This function registers the PICTUREBUTTON class. It is called automatically when the program starts up. The external variables _hPrev and _hInstance duplicate the arguments hPrevInstance and hInstance, which are passed to WinMain(). If the startup code does not supply these external variables, you must pass the arguments to this function and call it explicitly before invoking any PICTUREBUTTON control. -----------------------------------------------------------------------------*/ extern HINSTANCE _hPrev, _hInstance; static void register_picture_button(void) { if (!_hPrev) { WNDCLASS w; w.cbClsExtra = 0; w.cbWndExtra = sizeof(HGLOBAL); w.hbrBackground = (HBRUSH) COLOR_WINDOW + 1; w.hCursor = LoadCursor(NULL, IDC_ARROW); w.hIcon = NULL; w.hInstance = _hInstance; w.lpfnWndProc = PictureButtonProc; w.lpszClassName = "PICTUREBUTTON"; w.lpszMenuName = NULL; w.style = 0; RegisterClass(&w); } } #pragma startup register_picture_button struct PICTUREBUTTON { BOOL disabled : 1; BOOL focus : 1; BOOL depressed : 1; BOOL captured : 1; BOOL tabstop : 1; unsigned unused : 11; unsigned height; unsigned width; HBITMAP picture, disabled_picture; HWND status_bar; char far *status_message; }; /*----------------------------------------------------------------------------- This function receives all messages directed to the control. -----------------------------------------------------------------------------*/ long far pascal _export PictureButtonProc(HWND handle, UINT message, WPARAM wParam, LPARAM lParam) { PICTUREBUTTON far *p; HGLOBAL hp = GetWindowWord(handle, 0); if (hp != NULL) p = (PICTUREBUTTON far *) GlobalLock(hp); switch (message) { case WM_CREATE: { unsigned width = ((CREATESTRUCT far *) lParam)->cx; unsigned height = ((CREATESTRUCT far *) lParam)->cy; hp = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof(PICTUREBUTTON)); // + (height * width) / 2); if (hp == NULL) return -1L; p = (PICTUREBUTTON far *) GlobalLock(hp); p->height = height; p->width = width; if (((CREATESTRUCT far *) lParam)->style & WS_DISABLED) p->disabled = TRUE; if (((CREATESTRUCT far *) lParam)->style & WS_TABSTOP) p->tabstop = TRUE; char name[MAX_NAME_LENGTH + 2]; { int c; for (unsigned i = 0; i < sizeof(name) - 2 && (c = ((CREATESTRUCT far *) lParam)->lpszName[i]) != 0; i++) { name[i] = c; } name[i] = 0; } p->picture = LoadBitmap((HINSTANCE) GetWindowWord(handle, GWW_HINSTANCE), name); lstrcat(name, "X"); p->disabled_picture = LoadBitmap((HINSTANCE) GetWindowWord(handle, GWW_HINSTANCE), name); SetWindowWord(handle, 0, (WORD) hp); GlobalUnlock(hp); return 0; } case BM_SETSTYLE: if (p->picture != NULL) DeleteObject(p->picture); if (p->disabled_picture != NULL && p->disabled_picture != p->picture) DeleteObject(p->disabled_picture); p->picture = (HBITMAP) LOWORD(lParam); p->disabled_picture = (HBITMAP) HIWORD(lParam); InvalidateRect(handle, NULL, TRUE); GlobalUnlock(hp); return 0; case BM_SETSTATE: //MessageBox(handle, "BM_SETSTATE", "", MB_OK); p->status_bar = (HWND) wParam; p->status_message = (char far *) lParam; GlobalUnlock(hp); return 0; case WM_PAINT: { PAINTSTRUCT paint; HDC dc = BeginPaint(handle, &paint); int height = p->height; int width = p->width; HPEN old_pen = SelectObject(dc, GetStockObject(BLACK_PEN)); HBRUSH old_brush = SelectObject(dc, GetStockObject(LTGRAY_BRUSH)); Rectangle(dc, 0, 0, width, height); SelectObject(dc, GetStockObject(p->depressed ? BLACK_PEN : WHITE_PEN)); MoveTo(dc, 1, 1); LineTo(dc, width - 3, 1); LineTo(dc, width - 4, 2); LineTo(dc, 2, 2); LineTo(dc, 2, height - 4); LineTo(dc, 1, height - 3); LineTo(dc, 1, 1); SelectObject(dc, GetStockObject(p->depressed ? WHITE_PEN : BLACK_PEN)); MoveTo(dc, width - 3, height - 3); LineTo(dc, width - 3, 3); LineTo(dc, width - 2, 2); LineTo(dc, width - 2, height - 2); LineTo(dc, 2, height - 2); LineTo(dc, 3, height - 3); LineTo(dc, width - 3, height - 3); HBITMAP picture = p->disabled ? p->disabled_picture : p->picture; { HDC memory = CreateCompatibleDC(dc); HBITMAP old_bitmap; BITMAP bm; int x, y; SetBkColor(dc, RGB(192, 192, 192)); SetTextColor(dc, RGB(0, 0, 0)); if (picture != NULL) { GetObject(picture, sizeof(BITMAP), &bm); old_bitmap = SelectObject(memory, picture); x = (width - bm.bmWidth) / 2; if (x < 5) x = 5; y = (height - bm.bmHeight) / 2; if (y < 5) y = 5; if (p->depressed) { x++; y++; } BitBlt(dc, x, y, bm.bmWidth, bm.bmHeight, memory, 0, 0, SRCCOPY); SelectObject(memory, old_bitmap); DeleteDC(memory); } else { x = y = 5 + FOCUS_BORDER_WIDTH; bm.bmWidth = width - 2*x; bm.bmHeight = height - 2*y; } if (p->tabstop && p->focus) { RECT r; r.left = x - FOCUS_BORDER_WIDTH; r.right = x + bm.bmWidth + FOCUS_BORDER_WIDTH; r.top = y - FOCUS_BORDER_WIDTH; r.bottom = y + bm.bmHeight + FOCUS_BORDER_WIDTH; DrawFocusRect(dc, &r); } } SelectObject(dc, old_pen); SelectObject(dc, old_brush); EndPaint(handle, &paint); GlobalUnlock(hp); return 0; } case WM_ENABLE: if (p->disabled && wParam || !p->disabled && !wParam) { p->disabled = !p->disabled; InvalidateRect(handle, NULL, TRUE); } GlobalUnlock(hp); return 0; case WM_LBUTTONDOWN: p->captured = TRUE; p->depressed = TRUE; InvalidateRect(handle, NULL, TRUE); SetCapture(handle); if (p->tabstop) SetFocus(handle); if (p->status_bar != NULL && p->status_message != NULL) SetWindowText(p->status_bar, p->status_message); GlobalUnlock(hp); return 0; case WM_LBUTTONUP: { BOOL notify = !p->captured || LOWORD(lParam) < p->width && HIWORD(lParam) < p->height; p->depressed = FALSE; p->captured = FALSE; InvalidateRect(handle, NULL, TRUE); ReleaseCapture(); if (p->status_bar != NULL && p->status_message != NULL) SetWindowText(p->status_bar, ""); if (notify) { PostMessage(GetParent(handle), WM_COMMAND, GetMenu(handle), MAKELONG(handle, BN_CLICKED)); } GlobalUnlock(hp); return 0; } case WM_MOUSEMOVE: if (p->captured) { BOOL old_depressed = p->depressed; p->depressed = LOWORD(lParam) < p->width && HIWORD(lParam) < p->height; if (p->depressed != old_depressed) { InvalidateRect(handle, NULL, TRUE); GlobalUnlock(hp); return 0; } } break; case WM_SETFOCUS: p->focus = TRUE; InvalidateRect(handle, NULL, TRUE); GlobalUnlock(hp); return 0; case WM_KILLFOCUS: p->focus = FALSE; InvalidateRect(handle, NULL, TRUE); GlobalUnlock(hp); return 0; case WM_GETDLGCODE: GlobalUnlock(hp); return DLGC_BUTTON; case WM_KEYDOWN: if (wParam == ' ' && !p->captured && !p->depressed) { p->depressed = TRUE; InvalidateRect(handle, NULL, TRUE); GlobalUnlock(hp); return 0; } break; case WM_KEYUP: if (wParam == ' ' && !p->captured) { p->depressed = FALSE; InvalidateRect(handle, NULL, TRUE); PostMessage(GetParent(handle), WM_COMMAND, GetMenu(handle), MAKELONG(handle, BN_CLICKED)); GlobalUnlock(hp); return 0; } break; case WM_DESTROY: if (p->picture != NULL) DeleteObject(p->picture); if (p->disabled_picture != NULL && p->disabled_picture != p->picture) DeleteObject(p->disabled_picture); GlobalUnlock(hp); GlobalFree(hp); return 0; } return DefWindowProc(handle, message, wParam, lParam); } =============================== PROGRESS.CPP =============================== // Windows Progress Bar Control #include #include "progress.h" extern "C" { long far pascal _export ProgressBarProc(HWND, UINT, WPARAM, LPARAM); } /*----------------------------------------------------------------------------- This function registers the PROGRESSBAR class. It is called automatically when the program starts up. The external variables _hPrev and _hInstance duplicate the arguments hPrevInstance and hInstance, which are passed to WinMain(). If the startup code does not supply these external variables, you must pass the arguments to this function and call it explicitly before invoking any PROGRESSBAR control. -----------------------------------------------------------------------------*/ extern HINSTANCE _hPrev, _hInstance; static void register_progress_bar(void) { if (!_hPrev) { WNDCLASS w; w.cbClsExtra = 0; w.cbWndExtra = 4*sizeof(WORD); w.hbrBackground = (HBRUSH) COLOR_WINDOW + 1; w.hCursor = LoadCursor(NULL, IDC_ARROW); w.hIcon = NULL; w.hInstance = _hInstance; w.lpfnWndProc = ProgressBarProc; w.lpszClassName = "PROGRESSBAR"; w.lpszMenuName = NULL; w.style = 0; RegisterClass(&w); } } #pragma startup register_progress_bar /*----------------------------------------------------------------------------- This function receives all messages directed to the control. -----------------------------------------------------------------------------*/ long far pascal _export ProgressBarProc(HWND handle, UINT message, WPARAM wParam, LPARAM lParam) { WORD height = GetWindowWord(handle, 0); WORD width = GetWindowWord(handle, 2); WORD completed = GetWindowWord(handle, 4); WORD total = GetWindowWord(handle, 6); switch (message) { case WM_CREATE: { SetWindowWord(handle, 0, ((CREATESTRUCT far *) lParam)->cy); // height SetWindowWord(handle, 2, ((CREATESTRUCT far *) lParam)->cx); // width SetWindowWord(handle, 6, 100); // default value of total return 0; } case WM_PAINT: { PAINTSTRUCT paint; HDC dc = BeginPaint(handle, &paint); HPEN pen = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_WINDOWTEXT)); HPEN old_pen = SelectObject(dc, pen); HBRUSH brush = CreateSolidBrush(GetSysColor(COLOR_WINDOW)); HBRUSH old_brush = SelectObject(dc, brush); RECT r; char text[5]; // draw border Rectangle(dc, 0, 0, width-1, height-1); // display completion percentage SetTextColor(dc, GetSysColor(COLOR_WINDOWTEXT)); SetBkColor(dc, GetSysColor(COLOR_WINDOW)); r.left = r.top = 1; r.bottom = height - 2; r.right = width - 2; wsprintf(text, "%d%%", (100L * (DWORD) completed) / (DWORD) total); DrawText(dc, text, lstrlen(text), &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE); // invert progress portion r.right = 1 + ((DWORD) (width - 3) * (DWORD) completed) / (DWORD) total; if (r.right > r.left) InvertRect(dc, &r); SelectObject(dc, old_pen); DeleteObject(pen); SelectObject(dc, old_brush); DeleteObject(brush); EndPaint(handle, &paint); return 0; } case PB_SETCOMPLETED: { RECT r; r.top = 1; r.bottom = height - 2; SetWindowWord(handle, 4, wParam); // completed if ((100L * (DWORD) wParam) / (DWORD) total != (100L * (DWORD) completed) / (DWORD) total) { HDC dc = GetDC(handle); unsigned text_width = (WORD) GetTextExtent(dc, "100%", 4); r.left = 1 + (width - 2) / 2 - (text_width + 1) / 2; r.right = 1 + (width - 2) / 2 + (text_width + 1) / 2; InvalidateRect(handle, &r, TRUE); ReleaseDC(handle, dc); } r.left = 1 + ((DWORD) (width - 3) * (DWORD) completed) / (DWORD) total; r.right = 1 + ((DWORD) (width - 3) * (DWORD) wParam) / (DWORD) total; if (r.left != r.right) { if (r.left > r.right) { int x = r.left; r.left = r.right; r.right = x; } InvalidateRect(handle, &r, TRUE); } return 0; } case PB_GETCOMPLETED: return completed; case PB_SETTOTAL: SetWindowWord(handle, 6, wParam); // total SetWindowWord(handle, 4, 0); // completed InvalidateRect(handle, NULL, TRUE); return 0; case PB_GETTOTAL: return total; } return DefWindowProc(handle, message, wParam, lParam); } =============================== PROGRESS.H =============================== #define PB_GETCOMPLETED (WM_USER+0) #define PB_SETCOMPLETED (WM_USER+1) #define PB_GETTOTAL (WM_USER+2) #define PB_SETTOTAL (WM_USER+3) =============================== PROGRESS.RC =============================== /**************************************************************************** progress.rc produced by Borland Resource Workshop *****************************************************************************/ PROGRESSTEST DIALOG 6, 15, 161, 127 STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX CLASS "PROGRESSTEST" CAPTION "Progress Bar Test" FONT 8, "MS Sans Serif" { PUSHBUTTON "lower", 101, 12, 84, 50, 14 CONTROL "", 102, "PROGRESSBAR", 0 | WS_CHILD | WS_VISIBLE, 9, 18, 144, 14 PUSHBUTTON "higher", 103, 96, 84, 50, 14 } NOCURSOR CURSOR { '00 00 02 00 01 00 20 20 00 00 10 00 10 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 02 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 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 0F F0 00 00 1F F8 00 00 38 1C 00 00 70' '1E 00 00 E0 3F 00 00 C0 73 00 00 C0 E3 00 00 C1' 'C3 00 00 C3 83 00 00 C7 03 00 00 CE 03 00 00 FC' '07 00 00 78 0E 00 00 38 1C 00 00 1F F8 00 00 0F' 'F0 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 FF FF FF FF FF FF FF FF FF FF' 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' 'FF FF FF FF FF FF' } =============================== PROGTEST.CPP =============================== // Windows Progress Bar Control Test Program #include #include "progress.h" #include "progtest.h" extern "C" { long far pascal _export WndProc(HWND, UINT, WORD, LONG); int pascal WinMain(HINSTANCE, HINSTANCE, LPSTR, int); } const TOTAL = 500; long far pascal _export WndProc(HWND hwnd, UINT message, WORD wParam, LONG lParam) { switch (message) { case WM_CREATE: return 0; case WM_COMMAND: switch (wParam) { case LOWER: { int n = SendDlgItemMessage(hwnd, PROGRESSBAR, PB_GETCOMPLETED, 0, 0L); n = n - 1; if (n < 0) n = TOTAL; SendDlgItemMessage(hwnd, PROGRESSBAR, PB_SETCOMPLETED, n, 0L); return 0; } case HIGHER: { int n = SendDlgItemMessage(hwnd, PROGRESSBAR, PB_GETCOMPLETED, 0, 0L); n = n + 1; if (n > TOTAL) n = 0; SendDlgItemMessage(hwnd, PROGRESSBAR, PB_SETCOMPLETED, n, 0L); return 0; } } break; case WM_CLOSE: DestroyWindow(hwnd); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefDlgProc (hwnd, message, wParam, lParam); } #pragma argsused int pascal WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { HWND hwnd; MSG msg; if (!hPrevInstance) { WNDCLASS wndclass; wndclass.style = 0; wndclass.lpfnWndProc = (WNDPROC) WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = DLGWINDOWEXTRA; wndclass.hInstance = hInstance; wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = NULL; wndclass.lpszMenuName = NULL; wndclass.lpszClassName = "PROGRESSTEST"; RegisterClass (&wndclass); } hwnd = CreateDialog(hInstance, "PROGRESSTEST", 0, NULL); SendDlgItemMessage(hwnd, PROGRESSBAR, PB_SETTOTAL, TOTAL, 0L); ShowWindow(hwnd, nCmdShow == SW_SHOWMAXIMIZED ? SW_SHOW : nCmdShow); UpdateWindow(hwnd); while (GetMessage(&msg, NULL, 0, 0)) if (!IsDialogMessage (hwnd, &msg)) DispatchMessage (&msg); return msg.wParam; } =============================== PROGTEST.H =============================== #define PROGRESSBAR 101 #define LOWER 102 #define HIGHER 103 =============================== PROGTEST.RC =============================== #include "progtest.h" PROGRESSTEST DIALOG 6, 15, 161, 127 STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CLASS "PROGRESSTEST" CAPTION "Progress Bar Test" FONT 8, "MS Sans Serif" { PUSHBUTTON "lower", LOWER, 12, 84, 50, 14 CONTROL "", PROGRESSBAR, "PROGRESSBAR", 0 | WS_CHILD | WS_VISIBLE, 9, 18, 144, 14 PUSHBUTTON "higher", HIGHER, 96, 84, 50, 14 }