@@ -68,6 +68,8 @@ std::string AdvanceOptions::max_retries_ = std::to_string(kDefaultMaxRetries);
6868std::string AdvanceOptions::private_service_connect_uris_;
6969std::string AdvanceOptions::enable_gcd_;
7070std::string AdvanceOptions::universe_domain_;
71+ int AdvanceOptions::scroll_pos_ = 0 ;
72+ int AdvanceOptions::wheel_remainder_ = 0 ;
7173
7274std::string const kLanguageDialect = " SQLDialect" ;
7375std::string const kLargeResultsDatasetId = " LargeResultsDatasetId" ;
@@ -112,6 +114,14 @@ int const kEditBoxHeight = 17;
112114int const kinputComboBoxXAxis = 237 ;
113115int const KComboBoxHeight = 100 ;
114116
117+ // Full height of the laid-out controls. The window is clamped to the desktop
118+ // work area, so on a short or DPI-scaled display this is larger than the client
119+ // area and the difference is what scrolls.
120+ int const kContentHeight = kButtonY + 44 + kButtonHeight + 10 ;
121+
122+ // Pixels scrolled per scrollbar arrow click.
123+ int const kScrollLine = 20 ;
124+
115125HWND AdvanceOptions::GetHwnd () const { return adv_hwnd; }
116126AdvanceOptions::AdvanceOptions () : adv_hwnd(NULL ) {}
117127AdvanceOptions::~AdvanceOptions () {
@@ -470,6 +480,57 @@ void AdvanceOptions::CreateAdditionalControls(HFONT h_font) {
470480 // SendMessage(h_hyperlink, WM_SETFONT, (WPARAM)h_font, TRUE);
471481}
472482
483+ void AdvanceOptions::UpdateScrollInfo (HWND hwnd) {
484+ RECT client = {};
485+ GetClientRect (hwnd, &client);
486+ int const page = client.bottom - client.top ;
487+
488+ SCROLLINFO si = {};
489+ si.cbSize = sizeof (si);
490+ si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS ;
491+ si.nMin = 0 ;
492+ si.nMax = kContentHeight - 1 ;
493+ si.nPage = page;
494+ si.nPos = scroll_pos_;
495+ SetScrollInfo (hwnd, SB_VERT , &si, TRUE );
496+
497+ // Growing the window can leave us scrolled past the end; pull the content
498+ // back so there is never blank space below the last control.
499+ int const max_pos = (kContentHeight > page) ? kContentHeight - page : 0 ;
500+ if (scroll_pos_ > max_pos) {
501+ ScrollWindow (hwnd, 0 , scroll_pos_ - max_pos, NULL , NULL );
502+ scroll_pos_ = max_pos;
503+ si.fMask = SIF_POS ;
504+ si.nPos = scroll_pos_;
505+ SetScrollInfo (hwnd, SB_VERT , &si, TRUE );
506+ }
507+ }
508+
509+ void AdvanceOptions::ScrollTo (HWND hwnd, int new_pos) {
510+ RECT client = {};
511+ GetClientRect (hwnd, &client);
512+ int const page = client.bottom - client.top ;
513+ int const max_pos = (kContentHeight > page) ? kContentHeight - page : 0 ;
514+ new_pos = std::max (0 , std::min (new_pos, max_pos));
515+ if (new_pos == scroll_pos_) {
516+ return ;
517+ }
518+
519+ int const delta = scroll_pos_ - new_pos;
520+ scroll_pos_ = new_pos;
521+ // ScrollWindow shifts the child controls along with the client area, which is
522+ // what makes absolutely-positioned controls scroll without repositioning each
523+ // one by hand.
524+ ScrollWindow (hwnd, 0 , delta, NULL , NULL );
525+
526+ SCROLLINFO si = {};
527+ si.cbSize = sizeof (si);
528+ si.fMask = SIF_POS ;
529+ si.nPos = scroll_pos_;
530+ SetScrollInfo (hwnd, SB_VERT , &si, TRUE );
531+ UpdateWindow (hwnd);
532+ }
533+
473534void AdvanceOptions::PopulateAllowedProjectsListView (
474535 HWND h_list_view, std::vector<std::string> const & project_ids) {
475536 if (!h_list_view) {
@@ -875,17 +936,26 @@ LRESULT CALLBACK AdvanceOptions::AdvanceOptProc(HWND hwnd, UINT u_msg,
875936 if (HIWORD (w_param) != BN_CLICKED ) {
876937 break ;
877938 }
878- // The credentials live on the parent DSN dialog; this dialog holds no
879- // copy of them.
880- HWND h_parent = GetParent (hwnd);
939+ // The credentials live on the main DSN dialog; this dialog holds no
940+ // copy of them. This is a top-level owned window rather than a child,
941+ // and GetParent only reports the owner for WS_POPUP windows, so ask
942+ // for the owner explicitly.
943+ HWND h_parent = GetWindow (hwnd, GW_OWNER );
944+ if (h_parent == NULL ) {
945+ h_parent = GetParent (hwnd);
946+ }
947+ if (h_parent == NULL ) {
948+ ShowErrorWindow (hwnd,
949+ " Internal error: cannot locate the main dialog to "
950+ " read the connection settings from." );
951+ break ;
952+ }
881953 char key_file_buffer[1024 ] = {0 };
882954 char auth_buffer[256 ] = {0 };
883- if (h_parent) {
884- GetWindowText (GetDlgItem (h_parent, kIdcKeyfileEdit ),
885- key_file_buffer, sizeof (key_file_buffer));
886- GetWindowText (GetDlgItem (h_parent, kIdcAuthBox ), auth_buffer,
887- sizeof (auth_buffer));
888- }
955+ GetWindowText (GetDlgItem (h_parent, kIdcKeyfileEdit ), key_file_buffer,
956+ sizeof (key_file_buffer));
957+ GetWindowText (GetDlgItem (h_parent, kIdcAuthBox ), auth_buffer,
958+ sizeof (auth_buffer));
889959 if (auth_buffer[0 ] == ' \0 ' ) {
890960 ShowErrorWindow (hwnd,
891961 " Select an OAuth mechanism on the main dialog "
@@ -930,6 +1000,76 @@ LRESULT CALLBACK AdvanceOptions::AdvanceOptProc(HWND hwnd, UINT u_msg,
9301000 }
9311001 break ;
9321002 }
1003+ case WM_VSCROLL : {
1004+ RECT client = {};
1005+ GetClientRect (hwnd, &client);
1006+ int const page = client.bottom - client.top ;
1007+ int pos = scroll_pos_;
1008+ switch (LOWORD (w_param)) {
1009+ case SB_TOP :
1010+ pos = 0 ;
1011+ break ;
1012+ case SB_BOTTOM :
1013+ pos = kContentHeight ;
1014+ break ;
1015+ case SB_LINEUP :
1016+ pos -= kScrollLine ;
1017+ break ;
1018+ case SB_LINEDOWN :
1019+ pos += kScrollLine ;
1020+ break ;
1021+ case SB_PAGEUP :
1022+ pos -= page;
1023+ break ;
1024+ case SB_PAGEDOWN :
1025+ pos += page;
1026+ break ;
1027+ case SB_THUMBTRACK :
1028+ case SB_THUMBPOSITION : {
1029+ SCROLLINFO si = {};
1030+ si.cbSize = sizeof (si);
1031+ si.fMask = SIF_TRACKPOS ;
1032+ if (GetScrollInfo (hwnd, SB_VERT , &si)) {
1033+ pos = si.nTrackPos ;
1034+ }
1035+ break ;
1036+ }
1037+ default :
1038+ break ;
1039+ }
1040+ ScrollTo (hwnd, pos);
1041+ return 0 ;
1042+ }
1043+ case WM_MOUSEWHEEL : {
1044+ // Accumulate: precision trackpads and high-resolution wheels send deltas
1045+ // smaller than WHEEL_DELTA, which would round to zero on their own.
1046+ wheel_remainder_ += GET_WHEEL_DELTA_WPARAM (w_param);
1047+ int const notches = wheel_remainder_ / WHEEL_DELTA ;
1048+ if (notches == 0 ) {
1049+ return 0 ;
1050+ }
1051+ wheel_remainder_ -= notches * WHEEL_DELTA ;
1052+
1053+ // Honour the system "roll the mouse wheel to scroll" setting.
1054+ UINT lines_per_notch = 3 ;
1055+ if (!SystemParametersInfo (SPI_GETWHEELSCROLLLINES , 0 , &lines_per_notch,
1056+ 0 )) {
1057+ lines_per_notch = 3 ;
1058+ }
1059+ RECT client = {};
1060+ GetClientRect (hwnd, &client);
1061+ int step = 0 ;
1062+ if (lines_per_notch == WHEEL_PAGESCROLL ) {
1063+ step = client.bottom - client.top ;
1064+ } else {
1065+ step = static_cast <int >(lines_per_notch) * kScrollLine ;
1066+ }
1067+ ScrollTo (hwnd, scroll_pos_ - notches * step);
1068+ return 0 ;
1069+ }
1070+ case WM_SIZE :
1071+ UpdateScrollInfo (hwnd);
1072+ break ;
9331073 case WM_KEYDOWN : // Capture global key presses
9341074 if (w_param == VK_ESCAPE ) {
9351075 if (p_current_window) {
@@ -1047,17 +1187,40 @@ void AdvanceOptions::Show(HWND hwnd) {
10471187
10481188 RegisterClass (&wc_adv);
10491189
1050- int window_width = 525 ;
1190+ scroll_pos_ = 0 ;
1191+ wheel_remainder_ = 0 ;
1192+
1193+ // The vertical scrollbar is carved out of the client area, so widen the frame
1194+ // by its width; otherwise every right-aligned control loses that many pixels
1195+ // and the edit boxes are clipped.
1196+ int window_width = 525 + GetSystemMetrics (SM_CXVSCROLL );
10511197 int window_height = 860 ;
1052- int screen_width = GetSystemMetrics (SM_CXSCREEN );
1053- int screen_height = GetSystemMetrics (SM_CYSCREEN );
1054- int x_pos = (screen_width - window_width) / 2 ;
1055- int y_pos = (screen_height - window_height) / 2 ;
1198+
1199+ // Never open taller than the desktop work area: on a short or DPI-scaled
1200+ // display the full control layout does not fit, and a window whose OK button
1201+ // sits below the screen edge cannot be dismissed. Whatever does not fit is
1202+ // reachable through the vertical scrollbar instead.
1203+ RECT work_area = {};
1204+ int work_left = 0 ;
1205+ int work_top = 0 ;
1206+ int work_width = GetSystemMetrics (SM_CXSCREEN );
1207+ int work_height = GetSystemMetrics (SM_CYSCREEN );
1208+ if (SystemParametersInfo (SPI_GETWORKAREA , 0 , &work_area, 0 )) {
1209+ work_left = work_area.left ;
1210+ work_top = work_area.top ;
1211+ work_width = work_area.right - work_area.left ;
1212+ work_height = work_area.bottom - work_area.top ;
1213+ }
1214+ if (window_height > work_height) {
1215+ window_height = work_height;
1216+ }
1217+ int x_pos = work_left + (work_width - window_width) / 2 ;
1218+ int y_pos = work_top + (work_height - window_height) / 2 ;
10561219
10571220 adv_hwnd = CreateWindowEx (
10581221 WS_EX_TOPMOST , CLASS_NAME , " Advanced Options" ,
1059- WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_DLGFRAME , x_pos, y_pos ,
1060- window_width, window_height, hwnd, NULL , g_hDllInstance, this );
1222+ WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_DLGFRAME | WS_VSCROLL , x_pos ,
1223+ y_pos, window_width, window_height, hwnd, NULL , g_hDllInstance, this );
10611224 if (adv_hwnd) {
10621225 HFONT h_font =
10631226 CreateFont (-10 , 0 , 0 , 0 , FW_NORMAL , FALSE , FALSE , FALSE ,
@@ -1073,6 +1236,8 @@ void AdvanceOptions::Show(HWND hwnd) {
10731236 CreateAdditionalControls (h_font);
10741237 CreateButtons (h_font);
10751238
1239+ UpdateScrollInfo (adv_hwnd);
1240+
10761241 ShowWindow (adv_hwnd, SW_SHOW );
10771242 UpdateWindow (adv_hwnd);
10781243
0 commit comments