/*********************************************************************** Copyright 2002 Ben Rudiak-Gould. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA, or visit . ***********************************************************************/ #define WIN32_LEAN_AND_MEAN #define _WIN32_IE 0x400 #include #include #include #include "ui.h" #include void NestableWindow::GetClientSize(int* width, int* height) { RECT r; if (hwnd && ::GetClientRect(hwnd, &r)) { *width = r.right-r.left; *height = r.bottom-r.top; } else { *width = *height = 0; } } LONG APIENTRY NestableWindow::StaticWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { if (message == WM_CREATE) { LPCREATESTRUCT cs = (LPCREATESTRUCT)lParam; NestableWindow* self = (NestableWindow*)cs->lpCreateParams; self->hwnd = hwnd; SetWindowLong(hwnd, GWL_USERDATA, (LONG)self); return self->OnCreate(cs); } else { NestableWindow* self = (NestableWindow*)GetWindowLong(hwnd, GWL_USERDATA); if (!self) return DefWindowProc(hwnd, message, wParam, lParam); switch (message) { case WM_COMMAND: if (lParam) { int code = HIWORD(wParam); HWND hwnd_control = (HWND)lParam; NestableWindow* owner = (NestableWindow*)GetDlgCtrlID(hwnd_control); if (!owner) owner = (NestableWindow*)GetDlgCtrlID(GetParent(hwnd_control)); if (owner && owner->OnControlCommand(code)) return 0; } else { if (self->OnMenuCommand(LOWORD(wParam), HIWORD(wParam) & 1)) return 0; } break; case WM_CONTEXTMENU: { NestableWindow* owner = (NestableWindow*)GetDlgCtrlID((HWND)wParam); if ((void*)owner >= (void*)GetModuleHandle(0)) { owner->OnContextMenu(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); return 0; } } break; case WM_CLOSE: if (self->OnClose()) return 0; else break; case WM_SIZE: if (self->OnSize(LOWORD(lParam),HIWORD(lParam))) return 0; else break; case WM_ACTIVATE: if (self->OnActivate(LOWORD(wParam), !!HIWORD(wParam), HWND(lParam))) return 0; else break; case WM_SETFOCUS: if (self->OnSetFocus()) return 0; else break; case WM_DESTROY: self->hwnd = 0; if (self->OnDestroy()) return 0; else break; case WM_NOTIFY: { LPNMHDR nmhdr = (LPNMHDR)lParam; NestableWindow* owner = (NestableWindow*)wParam; if (!owner) owner = (NestableWindow*)GetDlgCtrlID(GetParent(nmhdr->hwndFrom)); if (owner && owner->OnNotify(nmhdr)) return 0; break; } case WM_MEASUREITEM: if (((NestableWindow*)wParam)->OnMeasureItem((LPMEASUREITEMSTRUCT) lParam)) return 0; else break; case WM_DRAWITEM: if (((NestableWindow*)wParam)->OnDrawItem((LPDRAWITEMSTRUCT) lParam)) return 0; else break; } return self->WndProc(hwnd, message, wParam, lParam); } } bool NestableWindow::OnNotify(LPNMHDR pnmhdr) { if (pnmhdr->code == NM_DBLCLK || pnmhdr->code == NM_RETURN) return OnDoubleClickOrEnterKey(pnmhdr); else return false; } void FrameWindow::SetChild(NestableWindow* new_child) { NestableWindow* old_child = child; child = new_child; Resize(); if (new_child) new_child->Show(); if (old_child && old_child != new_child) old_child->Hide(); } void FrameWindow::Resize() { int width, height; GetClientSize(&width, &height); OnSize(width, height); } bool FrameWindow::OnSize(int width, int height) { if (child) ::MoveWindow(child->GetHwnd(), 0, 0, width, height, TRUE); return true; } bool FrameWindow::OnActivate(int activation_code, bool minimized, HWND other_window) { if (activation_code == WA_ACTIVE) { if (hwndSavedFocus) { ::SetFocus(hwndSavedFocus); return true; } } else if (activation_code == WA_INACTIVE) hwndSavedFocus = ::GetFocus(); return false; } FrameWindow::FrameWindow(const char name[], HMENU hmenu) { child = 0; hwndSavedFocus = NULL; WNDCLASSEX wcx; wcx.cbSize = sizeof(wcx); wcx.style = CS_DBLCLKS /*| CS_NOCLOSE*/; wcx.lpfnWndProc = StaticWndProc; wcx.cbClsExtra = 0; wcx.cbWndExtra = 0; wcx.hInstance = GetModuleHandle(0); wcx.hIcon = NULL; wcx.hCursor = LoadCursor(NULL, IDC_SIZEWE); wcx.hbrBackground = GetStockBrush(NULL_BRUSH); wcx.lpszMenuName = NULL; wcx.lpszClassName = "FrameWnd"; wcx.hIconSm = NULL; RegisterClassEx(&wcx); // will fail if registered already hwnd = 0; ::CreateWindow( "FrameWnd", // class name name, // window name WS_OVERLAPPEDWINDOW, // style CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, // coords NULL, // parent hmenu, // menu GetModuleHandle(0), // hinstance this); // lParam // If window could not be created, return "failure" if (!hwnd) return; // Make the window visible; update its client area; and return "success" ShowWindow(hwnd, SW_SHOWDEFAULT); UpdateWindow(hwnd); } SplitterWndH::SplitterWndH(NestableWindow* parent) : splitter_width(GetSystemMetrics(SM_CXSIZEFRAME)) { Create(parent); left = right = 0; splitter_pos = 100; resizing = -1; } void SplitterWndH::Create(NestableWindow* parent) { int width, height; parent->GetClientSize(&width, &height); WNDCLASSEX wcx; wcx.cbSize = sizeof(wcx); wcx.style = CS_DBLCLKS /*| CS_NOCLOSE*/; wcx.lpfnWndProc = StaticWndProc; wcx.cbClsExtra = 0; wcx.cbWndExtra = 0; wcx.hInstance = GetModuleHandle(0); wcx.hIcon = NULL; wcx.hCursor = LoadCursor(NULL, IDC_SIZEWE); wcx.hbrBackground = HBRUSH(COLOR_3DFACE+1); wcx.lpszMenuName = NULL; wcx.lpszClassName = "SplitterWnd"; wcx.hIconSm = NULL; RegisterClassEx(&wcx); // will fail if registered already hwnd = 0; ::CreateWindow( "SplitterWnd", // class name "", // window name WS_CHILD | WS_CLIPCHILDREN, // style 0, 0, 0, 0, // coords parent->GetHwnd(), // parent NULL, // menu GetModuleHandle(0), // hinstance this); // lParam // If window could not be created, return "failure" if (!hwnd) return; // Make the window visible; update its client area; and return "success" ShowWindow(hwnd, SW_SHOW); } void SplitterWndH::Resize() { int width, height; GetClientSize(&width, &height); OnSize(width, height); } void SplitterWndH::SetLeft(NestableWindow* new_left) { NestableWindow* old_left = left; left = new_left; Resize(); if (new_left) new_left->Show(); if (old_left && old_left != new_left) old_left->Hide(); } void SplitterWndH::SetRight(NestableWindow* new_right) { NestableWindow* old_right = right; right = new_right; Resize(); if (new_right) new_right->Show(); if (old_right && old_right != new_right) old_right->Hide(); } LONG SplitterWndH::WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { if (message == WM_LBUTTONDOWN) { resizing = GET_X_LPARAM(lParam) - splitter_pos; SetCapture(hwnd); } else if (message == WM_MOUSEMOVE) { if (!(wParam & MK_LBUTTON)) { resizing = -1; ReleaseCapture(); } else if (resizing >= 0) { splitter_pos = GET_X_LPARAM(lParam) - resizing; int width, height; GetClientSize(&width, &height); if (splitter_pos < 0) splitter_pos = 0; if (splitter_pos > width-splitter_width) splitter_pos = width-splitter_width; Resize(); } } else if (message == WM_LBUTTONUP && resizing >= 0) { resizing = -1; ReleaseCapture(); } return NestableWindow::WndProc(hwnd, message, wParam, lParam); } bool SplitterWndH::OnSize(int width, int height) { if (left) ::MoveWindow(left->GetHwnd(), 0, 0, splitter_pos, height, TRUE); if (right) ::MoveWindow(right->GetHwnd(), splitter_pos+splitter_width, 0, width-splitter_pos-splitter_width, height, TRUE); return true; } ListView::ListView(NestableWindow* parent) { hwnd = CreateWindowEx( WS_EX_CLIENTEDGE, // extended styles WC_LISTVIEW, // class "", // caption WS_CHILD | WS_BORDER | WS_TABSTOP | LVS_REPORT | LVS_OWNERDATA, // styles 0, 0, 0, 0, // coords parent->GetHwnd(), (HMENU) this, // control ID GetModuleHandle(0), NULL); if (hwnd == NULL) return; ListView_SetExtendedListViewStyle(hwnd, LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP | LVS_EX_GRIDLINES /*| LVS_EX_LABELTIP*/); // ListView_SetImageList(hwnd, // ImageList_LoadImage(hinst, MAKEINTRESOURCE(IDB_ARROWS), 9, 3, CLR_DEFAULT, IMAGE_BITMAP, 0), // LVSIL_SMALL); } bool ListView::InsertColumn(int col, int width, const char caption[], int align) { LVCOLUMN lvc; lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_SUBITEM | LVCF_TEXT; lvc.iSubItem = col; lvc.cx = width; lvc.pszText = const_cast(caption); lvc.fmt = align; if (-1 == ListView_InsertColumn(hwnd, col, &lvc)) return false; else { // if (col >= num_columns) // num_columns = col+1; return true; } } void ListView::SetNumItems(int n) { ListView_SetItemCount(hwnd, n); } void ListView::AddItem(bool autoscroll) { // don't autoscroll if list box isn't currently at the bottom if (autoscroll) { SCROLLINFO si; si.cbSize = sizeof(si); si.fMask = SIF_POS | SIF_RANGE | SIF_PAGE; if (GetScrollInfo(hwnd, SB_VERT, &si) && si.nPos + (int)si.nPage <= si.nMax) autoscroll = false; } LVITEM new_item; memset(&new_item, 0, sizeof(new_item)); new_item.iItem = ListView_GetItemCount(hwnd); int item = ListView_InsertItem(hwnd, &new_item); if (autoscroll) { // LONG style = GetWindowLong(hwnd, GWL_STYLE); // SetWindowLong(hwnd, GWL_STYLE, style & ~WS_VISIBLE); ListView_Scroll(hwnd, 0, 32767); // SetWindowLong(hwnd, GWL_STYLE, style); } } void ListView::RedrawItems(int first, int last) { ListView_RedrawItems(hwnd, first, last); } bool ListView::OnNotify(LPNMHDR pnmhdr) { if (pnmhdr->code == LVN_GETDISPINFO) { NMLVDISPINFO* plvdi = (NMLVDISPINFO*)pnmhdr; if (plvdi->item.mask & LVIF_TEXT) { const char* text = GetItemText(plvdi->item.iItem, plvdi->item.iSubItem); strcpy(plvdi->item.pszText, text ? text : ""); return true; } } else if (pnmhdr->code == LVN_COLUMNCLICK) { NM_LISTVIEW* pnmv = (NM_LISTVIEW*)pnmhdr; return OnColumnClick(pnmv->iSubItem); } return NestableWindow::OnNotify(pnmhdr); } ColorListBox::ColorListBox(NestableWindow* parent, bool multi_select) { old_horizontal_extent = 0; DWORD styles = WS_CHILD | WS_BORDER | WS_HSCROLL | WS_VSCROLL | WS_TABSTOP | LBS_NOINTEGRALHEIGHT | LBS_HASSTRINGS | LBS_OWNERDRAWFIXED | LBS_USETABSTOPS | LBS_NOTIFY; if (multi_select) styles |= LBS_EXTENDEDSEL; hwnd = CreateWindowEx( WS_EX_CLIENTEDGE, // extended styles "LISTBOX", // class "", // caption styles, 0, 0, 0, 0, // coords parent->GetHwnd(), (HMENU) this, // control ID GetModuleHandle(0), NULL); if (hwnd) SetWindowFont(hwnd, GetStockFont(DEFAULT_GUI_FONT), true); } bool ColorListBox::OnMeasureItem(LPMEASUREITEMSTRUCT lpmis) { // Set the item height. Get the DC, select the font for the // list box, and compute the average height. HDC hdc = GetDC(hwnd); HANDLE oldfont = SelectObject(hdc, GetStockFont(DEFAULT_GUI_FONT)); // was GetWindowFont(m_hwndList) TEXTMETRIC tm; GetTextMetrics(hdc, &tm); SelectObject(hdc, oldfont); ReleaseDC(hwnd, hdc); lpmis->itemWidth = 100; lpmis->itemHeight = tm.tmHeight + 4; return true; } bool ColorListBox::OnDrawItem(LPDRAWITEMSTRUCT lpdis) { FillRect(lpdis->hDC, &lpdis->rcItem, (lpdis->itemState & ODS_SELECTED) ? (HBRUSH)(COLOR_HIGHLIGHT+1) : (HBRUSH)(COLOR_WINDOW+1)); if (lpdis->itemState & ODS_FOCUS) DrawFocusRect(lpdis->hDC, &lpdis->rcItem); if (lpdis->itemID != (UINT)-1) { // The text color is stored as the item data. COLORREF rgbText = (lpdis->itemState & ODS_SELECTED) ? GetSysColor(COLOR_HIGHLIGHTTEXT) : ListBox_GetItemData(hwnd, lpdis->itemID); COLORREF rgbBkgnd = GetSysColor((lpdis->itemState & ODS_SELECTED) ? COLOR_HIGHLIGHT : COLOR_WINDOW); SetBkColor(lpdis->hDC, rgbBkgnd); SetTextColor(lpdis->hDC, rgbText); char text[4096]; ListBox_GetText(hwnd, lpdis->itemID, text); int textlen = ListBox_GetTextLen(hwnd, lpdis->itemID); TextOut(lpdis->hDC, lpdis->rcItem.left + 2, lpdis->rcItem.top + 2, text, textlen); } return true; } int ColorListBox::InsertItem(const char text[], int index, COLORREF color) { index = ListBox_InsertString(hwnd, index, text); if (index == LB_ERR || index == LB_ERRSPACE) { MessageBox(NULL, "List box item add failed!", NULL, MB_OK); return -1; } ListBox_SetItemData(hwnd, index, color); // handle horizontal scrollbar HDC hdc = GetDC(hwnd); HANDLE oldfont = SelectObject(hdc, GetStockFont(DEFAULT_GUI_FONT)); SIZE sz; if (GetTextExtentPoint32(hdc, text, strlen(text)+1, &sz)) { if (old_horizontal_extent < sz.cx) ListBox_SetHorizontalExtent(hwnd, old_horizontal_extent = sz.cx); } SelectObject(hdc, oldfont); ReleaseDC(hwnd, hdc); return index; } int ColorListBox::AddItem(const char text[], COLORREF color, bool autoscroll) { // don't autoscroll if list box isn't currently at the bottom if (autoscroll) { SCROLLINFO si; si.cbSize = sizeof(si); si.fMask = SIF_POS | SIF_RANGE | SIF_PAGE; if (GetScrollInfo(hwnd, SB_VERT, &si) && si.nPos + (int)si.nPage <= si.nMax) autoscroll = false; } int index = InsertItem(text, -1, color); if (index >= 0) { RECT r; ListBox_GetItemRect(hwnd, index, &r); InvalidateRect(hwnd, &r, FALSE); // handle auto-scrolling if (autoscroll) { LONG style = GetWindowLong(hwnd, GWL_STYLE); SetWindowLong(hwnd, GWL_STYLE, style & ~WS_VISIBLE); ListBox_SetTopIndex(hwnd, index); SetWindowLong(hwnd, GWL_STYLE, style); } } return index; } ColorLogWindow::ColorLogWindow(NestableWindow* parent, int _max_entries) : ColorListBox(parent, true), max_entries(_max_entries) { SendMessage(hwnd, LB_INITSTORAGE, _max_entries, _max_entries*64); } void ColorLogWindow::LogItem(const char text[], COLORREF color) { // delete old entries int count = ListBox_GetCount(hwnd); while (count >= max_entries) { ListBox_DeleteString(hwnd, 0); --count; } // break string at newlines and if it gets too long char buf[1000]; while (*text) { const char* p = strchr(text, '\n'); if (!p) p = strchr(text, 0); int n = p - text; if (n >= sizeof(buf)) n = sizeof(buf)-1; memcpy(buf, text, n); buf[n] = 0; if (n && buf[n-1] == '\r') buf[n-1] = 0; AddItem(buf, color, true); text += n; if (*text == '\n') ++text; } }