Panda3D
|
00001 // Filename: winStatsPianoRoll.cxx 00002 // Created by: drose (03Dec03) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) Carnegie Mellon University. All rights reserved. 00008 // 00009 // All use of this software is subject to the terms of the revised BSD 00010 // license. You should have received a copy of this license along 00011 // with this source code in a file named "LICENSE." 00012 // 00013 //////////////////////////////////////////////////////////////////// 00014 00015 #include "winStatsPianoRoll.h" 00016 #include "winStatsMonitor.h" 00017 #include "numeric_types.h" 00018 00019 static const int default_piano_roll_width = 400; 00020 static const int default_piano_roll_height = 200; 00021 00022 bool WinStatsPianoRoll::_window_class_registered = false; 00023 const char * const WinStatsPianoRoll::_window_class_name = "piano"; 00024 00025 //////////////////////////////////////////////////////////////////// 00026 // Function: WinStatsPianoRoll::Constructor 00027 // Access: Public 00028 // Description: 00029 //////////////////////////////////////////////////////////////////// 00030 WinStatsPianoRoll:: 00031 WinStatsPianoRoll(WinStatsMonitor *monitor, int thread_index) : 00032 PStatPianoRoll(monitor, thread_index, 00033 default_piano_roll_width, 00034 default_piano_roll_height), 00035 WinStatsGraph(monitor) 00036 { 00037 _left_margin = 128; 00038 _right_margin = 8; 00039 _top_margin = 16; 00040 _bottom_margin = 8; 00041 00042 // Let's show the units on the guide bar labels. There's room. 00043 set_guide_bar_units(get_guide_bar_units() | GBU_show_units); 00044 00045 create_window(); 00046 clear_region(); 00047 } 00048 00049 //////////////////////////////////////////////////////////////////// 00050 // Function: WinStatsPianoRoll::Destructor 00051 // Access: Public, Virtual 00052 // Description: 00053 //////////////////////////////////////////////////////////////////// 00054 WinStatsPianoRoll:: 00055 ~WinStatsPianoRoll() { 00056 } 00057 00058 //////////////////////////////////////////////////////////////////// 00059 // Function: WinStatsPianoRoll::idle 00060 // Access: Public, Virtual 00061 // Description: Called as each frame's data is made available. There 00062 // is no gurantee the frames will arrive in order, or 00063 // that all of them will arrive at all. The monitor 00064 // should be prepared to accept frames received 00065 // out-of-order or missing. 00066 //////////////////////////////////////////////////////////////////// 00067 void WinStatsPianoRoll:: 00068 new_data(int thread_index, int frame_number) { 00069 if (!_pause) { 00070 update(); 00071 } 00072 } 00073 00074 //////////////////////////////////////////////////////////////////// 00075 // Function: WinStatsPianoRoll::force_redraw 00076 // Access: Public, Virtual 00077 // Description: Called when it is necessary to redraw the entire graph. 00078 //////////////////////////////////////////////////////////////////// 00079 void WinStatsPianoRoll:: 00080 force_redraw() { 00081 PStatPianoRoll::force_redraw(); 00082 } 00083 00084 //////////////////////////////////////////////////////////////////// 00085 // Function: WinStatsPianoRoll::changed_graph_size 00086 // Access: Public, Virtual 00087 // Description: Called when the user has resized the window, forcing 00088 // a resize of the graph. 00089 //////////////////////////////////////////////////////////////////// 00090 void WinStatsPianoRoll:: 00091 changed_graph_size(int graph_xsize, int graph_ysize) { 00092 PStatPianoRoll::changed_size(graph_xsize, graph_ysize); 00093 } 00094 00095 //////////////////////////////////////////////////////////////////// 00096 // Function: WinStatsPianoRoll::set_time_units 00097 // Access: Public, Virtual 00098 // Description: Called when the user selects a new time units from 00099 // the monitor pulldown menu, this should adjust the 00100 // units for the graph to the indicated mask if it is a 00101 // time-based graph. 00102 //////////////////////////////////////////////////////////////////// 00103 void WinStatsPianoRoll:: 00104 set_time_units(int unit_mask) { 00105 int old_unit_mask = get_guide_bar_units(); 00106 if ((old_unit_mask & (GBU_hz | GBU_ms)) != 0) { 00107 unit_mask = unit_mask & (GBU_hz | GBU_ms); 00108 unit_mask |= (old_unit_mask & GBU_show_units); 00109 set_guide_bar_units(unit_mask); 00110 00111 RECT rect; 00112 GetClientRect(_window, &rect); 00113 rect.left = _right_margin; 00114 InvalidateRect(_window, &rect, TRUE); 00115 } 00116 } 00117 00118 //////////////////////////////////////////////////////////////////// 00119 // Function: WinStatsPianoRoll::clicked_label 00120 // Access: Public, Virtual 00121 // Description: Called when the user single-clicks on a label. 00122 //////////////////////////////////////////////////////////////////// 00123 void WinStatsPianoRoll:: 00124 clicked_label(int collector_index) { 00125 if (collector_index >= 0) { 00126 WinStatsGraph::_monitor->open_strip_chart(_thread_index, collector_index, false); 00127 } 00128 } 00129 00130 //////////////////////////////////////////////////////////////////// 00131 // Function: WinStatsPianoRoll::set_horizontal_scale 00132 // Access: Public 00133 // Description: Changes the amount of time the width of the 00134 // horizontal axis represents. This may force a redraw. 00135 //////////////////////////////////////////////////////////////////// 00136 void WinStatsPianoRoll:: 00137 set_horizontal_scale(double time_width) { 00138 PStatPianoRoll::set_horizontal_scale(time_width); 00139 00140 RECT rect; 00141 GetClientRect(_window, &rect); 00142 rect.bottom = _top_margin; 00143 InvalidateRect(_window, &rect, TRUE); 00144 } 00145 00146 //////////////////////////////////////////////////////////////////// 00147 // Function: WinStatsPianoRoll::clear_region 00148 // Access: Protected 00149 // Description: Erases the chart area. 00150 //////////////////////////////////////////////////////////////////// 00151 void WinStatsPianoRoll:: 00152 clear_region() { 00153 RECT rect = { 0, 0, get_xsize(), get_ysize() }; 00154 FillRect(_bitmap_dc, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH)); 00155 } 00156 00157 //////////////////////////////////////////////////////////////////// 00158 // Function: WinStatsPianoRoll::begin_draw 00159 // Access: Protected, Virtual 00160 // Description: Erases the chart area in preparation for drawing a 00161 // bunch of bars. 00162 //////////////////////////////////////////////////////////////////// 00163 void WinStatsPianoRoll:: 00164 begin_draw() { 00165 clear_region(); 00166 00167 // Draw in the guide bars. 00168 int num_guide_bars = get_num_guide_bars(); 00169 for (int i = 0; i < num_guide_bars; i++) { 00170 draw_guide_bar(_bitmap_dc, get_guide_bar(i)); 00171 } 00172 } 00173 00174 //////////////////////////////////////////////////////////////////// 00175 // Function: WinStatsPianoRoll::draw_bar 00176 // Access: Protected, Virtual 00177 // Description: Draws a single bar on the chart. 00178 //////////////////////////////////////////////////////////////////// 00179 void WinStatsPianoRoll:: 00180 draw_bar(int row, int from_x, int to_x) { 00181 if (row >= 0 && row < _label_stack.get_num_labels()) { 00182 int y = _label_stack.get_label_y(row) - _graph_top; 00183 int height = _label_stack.get_label_height(row); 00184 00185 RECT rect = { 00186 from_x, y - height + 2, 00187 to_x, y - 2, 00188 }; 00189 int collector_index = get_label_collector(row); 00190 HBRUSH brush = get_collector_brush(collector_index); 00191 FillRect(_bitmap_dc, &rect, brush); 00192 } 00193 } 00194 00195 //////////////////////////////////////////////////////////////////// 00196 // Function: WinStatsPianoRoll::end_draw 00197 // Access: Protected, Virtual 00198 // Description: Called after all the bars have been drawn, this 00199 // triggers a refresh event to draw it to the window. 00200 //////////////////////////////////////////////////////////////////// 00201 void WinStatsPianoRoll:: 00202 end_draw() { 00203 InvalidateRect(_graph_window, NULL, FALSE); 00204 } 00205 00206 //////////////////////////////////////////////////////////////////// 00207 // Function: WinStatsPianoRoll::idle 00208 // Access: Protected, Virtual 00209 // Description: Called at the end of the draw cycle. 00210 //////////////////////////////////////////////////////////////////// 00211 void WinStatsPianoRoll:: 00212 idle() { 00213 if (_labels_changed) { 00214 update_labels(); 00215 } 00216 } 00217 00218 //////////////////////////////////////////////////////////////////// 00219 // Function: WinStatsPianoRoll::window_proc 00220 // Access: Protected 00221 // Description: 00222 //////////////////////////////////////////////////////////////////// 00223 LONG WinStatsPianoRoll:: 00224 window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { 00225 switch (msg) { 00226 case WM_LBUTTONDOWN: 00227 if (_potential_drag_mode == DM_new_guide_bar) { 00228 set_drag_mode(DM_new_guide_bar); 00229 SetCapture(_graph_window); 00230 return 0; 00231 } 00232 break; 00233 00234 default: 00235 break; 00236 } 00237 00238 return WinStatsGraph::window_proc(hwnd, msg, wparam, lparam); 00239 } 00240 00241 //////////////////////////////////////////////////////////////////// 00242 // Function: WinStatsPianoRoll::graph_window_proc 00243 // Access: Protected 00244 // Description: 00245 //////////////////////////////////////////////////////////////////// 00246 LONG WinStatsPianoRoll:: 00247 graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { 00248 switch (msg) { 00249 case WM_LBUTTONDOWN: 00250 if (_potential_drag_mode == DM_none) { 00251 set_drag_mode(DM_scale); 00252 PN_int16 x = LOWORD(lparam); 00253 _drag_scale_start = pixel_to_height(x); 00254 SetCapture(_graph_window); 00255 return 0; 00256 00257 } else if (_potential_drag_mode == DM_guide_bar && _drag_guide_bar >= 0) { 00258 set_drag_mode(DM_guide_bar); 00259 PN_int16 x = LOWORD(lparam); 00260 _drag_start_x = x; 00261 SetCapture(_graph_window); 00262 return 0; 00263 } 00264 break; 00265 00266 case WM_MOUSEMOVE: 00267 if (_drag_mode == DM_none && _potential_drag_mode == DM_none) { 00268 // When the mouse is over a color bar, highlight it. 00269 PN_int16 x = LOWORD(lparam); 00270 PN_int16 y = HIWORD(lparam); 00271 _label_stack.highlight_label(get_collector_under_pixel(x, y)); 00272 00273 // Now we want to get a WM_MOUSELEAVE when the mouse leaves the 00274 // graph window. 00275 TRACKMOUSEEVENT tme = { 00276 sizeof(TRACKMOUSEEVENT), 00277 TME_LEAVE, 00278 _graph_window, 00279 0 00280 }; 00281 TrackMouseEvent(&tme); 00282 00283 } else { 00284 // If the mouse is in some drag mode, stop highlighting. 00285 _label_stack.highlight_label(-1); 00286 } 00287 00288 if (_drag_mode == DM_scale) { 00289 PN_int16 x = LOWORD(lparam); 00290 double ratio = (double)x / (double)get_xsize(); 00291 if (ratio > 0.0f) { 00292 set_horizontal_scale(_drag_scale_start / ratio); 00293 } 00294 return 0; 00295 00296 } else if (_drag_mode == DM_new_guide_bar) { 00297 // We haven't created the new guide bar yet; we won't until the 00298 // mouse comes within the graph's region. 00299 PN_int16 x = LOWORD(lparam); 00300 if (x >= 0 && x < get_xsize()) { 00301 set_drag_mode(DM_guide_bar); 00302 _drag_guide_bar = add_user_guide_bar(pixel_to_height(x)); 00303 return 0; 00304 } 00305 00306 } else if (_drag_mode == DM_guide_bar) { 00307 PN_int16 x = LOWORD(lparam); 00308 move_user_guide_bar(_drag_guide_bar, pixel_to_height(x)); 00309 return 0; 00310 } 00311 break; 00312 00313 case WM_MOUSELEAVE: 00314 // When the mouse leaves the graph, stop highlighting. 00315 _label_stack.highlight_label(-1); 00316 break; 00317 00318 case WM_LBUTTONUP: 00319 if (_drag_mode == DM_scale) { 00320 set_drag_mode(DM_none); 00321 ReleaseCapture(); 00322 return 0; 00323 00324 } else if (_drag_mode == DM_guide_bar) { 00325 PN_int16 x = LOWORD(lparam); 00326 if (x < 0 || x >= get_xsize()) { 00327 remove_user_guide_bar(_drag_guide_bar); 00328 } else { 00329 move_user_guide_bar(_drag_guide_bar, pixel_to_height(x)); 00330 } 00331 set_drag_mode(DM_none); 00332 ReleaseCapture(); 00333 return 0; 00334 } 00335 break; 00336 00337 case WM_LBUTTONDBLCLK: 00338 { 00339 // Double-clicking on a color bar in the graph is the same as 00340 // double-clicking on the corresponding label. 00341 PN_int16 x = LOWORD(lparam); 00342 PN_int16 y = HIWORD(lparam); 00343 clicked_label(get_collector_under_pixel(x, y)); 00344 return 0; 00345 } 00346 break; 00347 00348 default: 00349 break; 00350 } 00351 00352 return WinStatsGraph::graph_window_proc(hwnd, msg, wparam, lparam); 00353 } 00354 00355 //////////////////////////////////////////////////////////////////// 00356 // Function: WinStatsPianoRoll::additional_window_paint 00357 // Access: Protected, Virtual 00358 // Description: This is called during the servicing of WM_PAINT; it 00359 // gives a derived class opportunity to do some further 00360 // painting into the window (the outer window, not the 00361 // graph window). 00362 //////////////////////////////////////////////////////////////////// 00363 void WinStatsPianoRoll:: 00364 additional_window_paint(HDC hdc) { 00365 // Draw in the labels for the guide bars. 00366 HFONT hfnt = (HFONT)GetStockObject(ANSI_VAR_FONT); 00367 SelectObject(hdc, hfnt); 00368 SetTextAlign(hdc, TA_LEFT | TA_BOTTOM); 00369 SetBkMode(hdc, TRANSPARENT); 00370 00371 int y = _top_margin; 00372 00373 int i; 00374 int num_guide_bars = get_num_guide_bars(); 00375 for (i = 0; i < num_guide_bars; i++) { 00376 draw_guide_label(hdc, y, get_guide_bar(i)); 00377 } 00378 00379 int num_user_guide_bars = get_num_user_guide_bars(); 00380 for (i = 0; i < num_user_guide_bars; i++) { 00381 draw_guide_label(hdc, y, get_user_guide_bar(i)); 00382 } 00383 } 00384 00385 //////////////////////////////////////////////////////////////////// 00386 // Function: WinStatsPianoRoll::additional_graph_window_paint 00387 // Access: Protected, Virtual 00388 // Description: This is called during the servicing of WM_PAINT; it 00389 // gives a derived class opportunity to do some further 00390 // painting into the window (the outer window, not the 00391 // graph window). 00392 //////////////////////////////////////////////////////////////////// 00393 void WinStatsPianoRoll:: 00394 additional_graph_window_paint(HDC hdc) { 00395 int num_user_guide_bars = get_num_user_guide_bars(); 00396 for (int i = 0; i < num_user_guide_bars; i++) { 00397 draw_guide_bar(hdc, get_user_guide_bar(i)); 00398 } 00399 } 00400 00401 //////////////////////////////////////////////////////////////////// 00402 // Function: WinStatsPianoRoll::consider_drag_start 00403 // Access: Protected, Virtual 00404 // Description: Based on the mouse position within the window's 00405 // client area, look for draggable things the mouse 00406 // might be hovering over and return the apprioprate 00407 // DragMode enum or DM_none if nothing is indicated. 00408 //////////////////////////////////////////////////////////////////// 00409 WinStatsGraph::DragMode WinStatsPianoRoll:: 00410 consider_drag_start(int mouse_x, int mouse_y, int width, int height) { 00411 if (mouse_y >= _graph_top && mouse_y < _graph_top + get_ysize()) { 00412 if (mouse_x >= _graph_left && mouse_x < _graph_left + get_xsize()) { 00413 // See if the mouse is over a user-defined guide bar. 00414 int x = mouse_x - _graph_left; 00415 double from_height = pixel_to_height(x - 2); 00416 double to_height = pixel_to_height(x + 2); 00417 _drag_guide_bar = find_user_guide_bar(from_height, to_height); 00418 if (_drag_guide_bar >= 0) { 00419 return DM_guide_bar; 00420 } 00421 00422 } else if (mouse_x < _left_margin - 2 || 00423 mouse_x > width - _right_margin + 2) { 00424 // The mouse is left or right of the graph; maybe create a new 00425 // guide bar. 00426 return DM_new_guide_bar; 00427 } 00428 } 00429 00430 return WinStatsGraph::consider_drag_start(mouse_x, mouse_y, width, height); 00431 } 00432 00433 //////////////////////////////////////////////////////////////////// 00434 // Function: WinStatsPianoRoll::get_collector_under_pixel 00435 // Access: Private 00436 // Description: Returns the collector index associated with the 00437 // indicated vertical row, or -1. 00438 //////////////////////////////////////////////////////////////////// 00439 int WinStatsPianoRoll:: 00440 get_collector_under_pixel(int xpoint, int ypoint) { 00441 if (_label_stack.get_num_labels() == 0) { 00442 return -1; 00443 } 00444 00445 // Assume all of the labels are the same height. 00446 int height = _label_stack.get_label_height(0); 00447 int row = (get_ysize() - ypoint) / height; 00448 if (row >= 0 && row < _label_stack.get_num_labels()) { 00449 return _label_stack.get_label_collector_index(row); 00450 } else { 00451 return -1; 00452 } 00453 } 00454 00455 //////////////////////////////////////////////////////////////////// 00456 // Function: WinStatsPianoRoll::update_labels 00457 // Access: Private 00458 // Description: Resets the list of labels. 00459 //////////////////////////////////////////////////////////////////// 00460 void WinStatsPianoRoll:: 00461 update_labels() { 00462 _label_stack.clear_labels(); 00463 for (int i = 0; i < get_num_labels(); i++) { 00464 int label_index = 00465 _label_stack.add_label(WinStatsGraph::_monitor, this, 00466 _thread_index, 00467 get_label_collector(i), true); 00468 } 00469 _labels_changed = false; 00470 } 00471 00472 //////////////////////////////////////////////////////////////////// 00473 // Function: WinStatsPianoRoll::draw_guide_bar 00474 // Access: Private 00475 // Description: Draws the line for the indicated guide bar on the 00476 // graph. 00477 //////////////////////////////////////////////////////////////////// 00478 void WinStatsPianoRoll:: 00479 draw_guide_bar(HDC hdc, const PStatGraph::GuideBar &bar) { 00480 int x = height_to_pixel(bar._height); 00481 00482 if (x > 0 && x < get_xsize() - 1) { 00483 // Only draw it if it's not too close to either edge. 00484 switch (bar._style) { 00485 case GBS_target: 00486 SelectObject(hdc, _light_pen); 00487 break; 00488 00489 case GBS_user: 00490 SelectObject(hdc, _user_guide_bar_pen); 00491 break; 00492 00493 case GBS_normal: 00494 SelectObject(hdc, _dark_pen); 00495 break; 00496 } 00497 MoveToEx(hdc, x, 0, NULL); 00498 LineTo(hdc, x, get_ysize()); 00499 } 00500 } 00501 00502 //////////////////////////////////////////////////////////////////// 00503 // Function: WinStatsPianoRoll::draw_guide_label 00504 // Access: Private 00505 // Description: Draws the text for the indicated guide bar label at 00506 // the top of the graph. 00507 //////////////////////////////////////////////////////////////////// 00508 void WinStatsPianoRoll:: 00509 draw_guide_label(HDC hdc, int y, const PStatGraph::GuideBar &bar) { 00510 switch (bar._style) { 00511 case GBS_target: 00512 SetTextColor(hdc, _light_color); 00513 break; 00514 00515 case GBS_user: 00516 SetTextColor(hdc, _user_guide_bar_color); 00517 break; 00518 00519 case GBS_normal: 00520 SetTextColor(hdc, _dark_color); 00521 break; 00522 } 00523 00524 int x = height_to_pixel(bar._height); 00525 const string &label = bar._label; 00526 SIZE size; 00527 GetTextExtentPoint32(hdc, label.data(), label.length(), &size); 00528 00529 if (bar._style != GBS_user) { 00530 double from_height = pixel_to_height(x - size.cx); 00531 double to_height = pixel_to_height(x + size.cx); 00532 if (find_user_guide_bar(from_height, to_height) >= 0) { 00533 // Omit the label: there's a user-defined guide bar in the same space. 00534 return; 00535 } 00536 } 00537 00538 int this_x = _graph_left + x - size.cx / 2; 00539 if (x >= 0 && x < get_xsize()) { 00540 TextOut(hdc, this_x, y, 00541 label.data(), label.length()); 00542 } 00543 } 00544 00545 //////////////////////////////////////////////////////////////////// 00546 // Function: WinStatsPianoRoll::create_window 00547 // Access: Private 00548 // Description: Creates the window for this strip chart. 00549 //////////////////////////////////////////////////////////////////// 00550 void WinStatsPianoRoll:: 00551 create_window() { 00552 if (_window) { 00553 return; 00554 } 00555 00556 HINSTANCE application = GetModuleHandle(NULL); 00557 register_window_class(application); 00558 00559 const PStatClientData *client_data = 00560 WinStatsGraph::_monitor->get_client_data(); 00561 string thread_name = client_data->get_thread_name(_thread_index); 00562 string window_title = thread_name + " thread piano roll"; 00563 00564 00565 RECT win_rect = { 00566 0, 0, 00567 _left_margin + get_xsize() + _right_margin, 00568 _top_margin + get_ysize() + _bottom_margin 00569 }; 00570 00571 // compute window size based on desired client area size 00572 AdjustWindowRect(&win_rect, graph_window_style, FALSE); 00573 00574 _window = 00575 CreateWindow(_window_class_name, window_title.c_str(), graph_window_style, 00576 CW_USEDEFAULT, CW_USEDEFAULT, 00577 win_rect.right - win_rect.left, 00578 win_rect.bottom - win_rect.top, 00579 WinStatsGraph::_monitor->get_window(), NULL, application, 0); 00580 if (!_window) { 00581 nout << "Could not create PianoRoll window!\n"; 00582 exit(1); 00583 } 00584 00585 SetWindowLongPtr(_window, 0, (LONG_PTR)this); 00586 setup_label_stack(); 00587 00588 // Ensure that the window is on top of the stack. 00589 SetWindowPos(_window, HWND_TOP, 0, 0, 0, 0, 00590 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); 00591 } 00592 00593 //////////////////////////////////////////////////////////////////// 00594 // Function: WinStatsPianoRoll::register_window_class 00595 // Access: Private, Static 00596 // Description: Registers the window class for the pianoRoll window, if 00597 // it has not already been registered. 00598 //////////////////////////////////////////////////////////////////// 00599 void WinStatsPianoRoll:: 00600 register_window_class(HINSTANCE application) { 00601 if (_window_class_registered) { 00602 return; 00603 } 00604 00605 WNDCLASS wc; 00606 00607 ZeroMemory(&wc, sizeof(WNDCLASS)); 00608 wc.style = 0; 00609 wc.lpfnWndProc = (WNDPROC)static_window_proc; 00610 wc.hInstance = application; 00611 wc.hCursor = LoadCursor(NULL, IDC_ARROW); 00612 wc.hbrBackground = (HBRUSH)COLOR_BACKGROUND; 00613 wc.lpszMenuName = NULL; 00614 wc.lpszClassName = _window_class_name; 00615 00616 // Reserve space to associate the this pointer with the window. 00617 wc.cbWndExtra = sizeof(WinStatsPianoRoll *); 00618 00619 if (!RegisterClass(&wc)) { 00620 nout << "Could not register PianoRoll window class!\n"; 00621 exit(1); 00622 } 00623 00624 _window_class_registered = true; 00625 } 00626 00627 //////////////////////////////////////////////////////////////////// 00628 // Function: WinStatsPianoRoll::static_window_proc 00629 // Access: Private, Static 00630 // Description: 00631 //////////////////////////////////////////////////////////////////// 00632 LONG WINAPI WinStatsPianoRoll:: 00633 static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { 00634 WinStatsPianoRoll *self = (WinStatsPianoRoll *)GetWindowLongPtr(hwnd, 0); 00635 if (self != (WinStatsPianoRoll *)NULL && self->_window == hwnd) { 00636 return self->window_proc(hwnd, msg, wparam, lparam); 00637 } else { 00638 return DefWindowProc(hwnd, msg, wparam, lparam); 00639 } 00640 }