void CurveWnd::onSize(HWND hwnd, UINT state, int cx, int cy){ int nMax, nPos; nPos = getScrollPos(SB_HORZ); nMax = canvas.cx - 1; if(cx > canvas.cx - nPos){ if(cx >= canvas.cx) nPos = 0; else nPos = canvas.cx - cx; } setScrollInfo(SB_HORZ, 0, nMax, cx, nPos); nPos = getScrollPos(SB_VERT); nMax = canvas.cy - 1; if(cy > canvas.cy - nPos){ if(cy >= canvas.cy) nPos = 0; else nPos = canvas.cy - cy; } setScrollInfo(SB_VERT, 0, nMax, cy, nPos); }
void areaMouseEvent(HWND hwnd, void *data, DWORD button, BOOL up, uintptr_t heldButtons, LPARAM lParam) { int xpos, ypos; // mouse coordinates are relative to control; make them relative to Area getScrollPos(hwnd, &xpos, &ypos); xpos += GET_X_LPARAM(lParam); ypos += GET_Y_LPARAM(lParam); finishAreaMouseEvent(data, button, up, heldButtons, xpos, ypos); }
void LLTabContainer::updateMaxScrollPos() { BOOL no_scroll = TRUE; if (mIsVertical) { S32 tab_total_height = (BTN_HEIGHT + TABCNTRV_PAD) * getTabCount(); S32 available_height = getRect().getHeight() - getTopBorderHeight(); if( tab_total_height > available_height ) { S32 available_height_with_arrows = getRect().getHeight() - 2*(TABCNTRV_ARROW_BTN_SIZE + 3*TABCNTRV_PAD); S32 additional_needed = tab_total_height - available_height_with_arrows; setMaxScrollPos((S32) ceil(additional_needed / float(BTN_HEIGHT) ) ); no_scroll = FALSE; } } else { S32 tab_space = 0; S32 available_space = 0; tab_space = mTotalTabWidth; available_space = getRect().getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + TABCNTR_TAB_H_PAD); if( tab_space > available_space ) { S32 available_width_with_arrows = getRect().getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + TABCNTR_ARROW_BTN_SIZE + TABCNTR_ARROW_BTN_SIZE + 1); // subtract off reserved portion on left available_width_with_arrows -= TABCNTR_TAB_PARTIAL_WIDTH; S32 running_tab_width = 0; setMaxScrollPos(getTabCount()); for(tuple_list_t::reverse_iterator tab_it = mTabList.rbegin(); tab_it != mTabList.rend(); ++tab_it) { running_tab_width += (*tab_it)->mButton->getRect().getWidth(); if (running_tab_width > available_width_with_arrows) { break; } setMaxScrollPos(getMaxScrollPos()-1); } // in case last tab doesn't actually fit on screen, make it the last scrolling position setMaxScrollPos(llmin(getMaxScrollPos(), getTabCount() - 1)); no_scroll = FALSE; } } if (no_scroll) { setMaxScrollPos(0); setScrollPos(0); } if (getScrollPos() > getMaxScrollPos()) { setScrollPos(getMaxScrollPos()); // maybe just enforce this via limits in setScrollPos instead? } }
void areaOpenTextField(HWND area, HWND textfield, int x, int y, int width, int height) { int sx, sy; int baseX, baseY; LONG unused; getScrollPos(area, &sx, &sy); x += sx; y += sy; calculateBaseUnits(textfield, &baseX, &baseY, &unused); width = MulDiv(width, baseX, 4); height = MulDiv(height, baseY, 8); if (MoveWindow(textfield, x, y, width, height, TRUE) == 0) xpanic("error moving Area TextField in Area.OpenTextFieldAt()", GetLastError()); ShowWindow(textfield, SW_SHOW); if (SetFocus(textfield) == NULL) xpanic("error giving Area TextField focus", GetLastError()); }
// virtual void LLTabContainer::draw() { S32 target_pixel_scroll = 0; S32 cur_scroll_pos = getScrollPos(); if (cur_scroll_pos > 0) { if (!mIsVertical) { S32 available_width_with_arrows = getRect().getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + TABCNTR_ARROW_BTN_SIZE + TABCNTR_ARROW_BTN_SIZE + 1); for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) { if (cur_scroll_pos == 0) { break; } target_pixel_scroll += (*iter)->mButton->getRect().getWidth(); cur_scroll_pos--; } // Show part of the tab to the left of what is fully visible target_pixel_scroll -= TABCNTR_TAB_PARTIAL_WIDTH; // clamp so that rightmost tab never leaves right side of screen target_pixel_scroll = llmin(mTotalTabWidth - available_width_with_arrows, target_pixel_scroll); } else { S32 available_height_with_arrows = getRect().getHeight() - getTopBorderHeight() - (LLPANEL_BORDER_WIDTH + TABCNTR_ARROW_BTN_SIZE + TABCNTR_ARROW_BTN_SIZE + 1); for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) { if (cur_scroll_pos==0) { break; } target_pixel_scroll += (*iter)->mButton->getRect().getHeight(); cur_scroll_pos--; } S32 total_tab_height = (BTN_HEIGHT + TABCNTRV_PAD) * getTabCount() + TABCNTRV_PAD; // clamp so that the bottom tab never leaves bottom of panel target_pixel_scroll = llmin(total_tab_height - available_height_with_arrows, target_pixel_scroll); } } setScrollPosPixels((S32)lerp((F32)getScrollPosPixels(), (F32)target_pixel_scroll, LLCriticalDamp::getInterpolant(0.08f))); BOOL has_scroll_arrows = (mMaxScrollPos > 0) || (mScrollPosPixels > 0); if (!mIsVertical) { mJumpPrevArrowBtn->setVisible( has_scroll_arrows ); mJumpNextArrowBtn->setVisible( has_scroll_arrows ); } mPrevArrowBtn->setVisible( has_scroll_arrows ); mNextArrowBtn->setVisible( has_scroll_arrows ); S32 left = 0, top = 0; if (mIsVertical) { top = getRect().getHeight() - getTopBorderHeight() - LLPANEL_BORDER_WIDTH - 1 - (has_scroll_arrows ? TABCNTRV_ARROW_BTN_SIZE : 0); top += getScrollPosPixels(); } else { // Set the leftmost position of the tab buttons. left = LLPANEL_BORDER_WIDTH + (has_scroll_arrows ? (TABCNTR_ARROW_BTN_SIZE * 2) : TABCNTR_TAB_H_PAD); left -= getScrollPosPixels(); } // Hide all the buttons for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) { LLTabTuple* tuple = *iter; tuple->mButton->setVisible( FALSE ); } LLPanel::draw(); // if tabs are hidden, don't draw them and leave them in the invisible state if (!getTabsHidden()) { // Show all the buttons for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) { LLTabTuple* tuple = *iter; tuple->mButton->setVisible( TRUE ); } // Draw some of the buttons... LLRect clip_rect = getLocalRect(); if (has_scroll_arrows) { // ...but clip them. if (mIsVertical) { clip_rect.mBottom = mNextArrowBtn->getRect().mTop + 3*TABCNTRV_PAD; clip_rect.mTop = mPrevArrowBtn->getRect().mBottom - 3*TABCNTRV_PAD; } else { clip_rect.mLeft = mPrevArrowBtn->getRect().mRight; clip_rect.mRight = mNextArrowBtn->getRect().mLeft; } } LLLocalClipRect clip(clip_rect); S32 max_scroll_visible = getTabCount() - getMaxScrollPos() + getScrollPos(); S32 idx = 0; for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) { LLTabTuple* tuple = *iter; tuple->mButton->translate( left ? left - tuple->mButton->getRect().mLeft : 0, top ? top - tuple->mButton->getRect().mTop : 0 ); if (top) top -= BTN_HEIGHT + TABCNTRV_PAD; if (left) left += tuple->mButton->getRect().getWidth(); if (!mIsVertical) { if( idx < getScrollPos() ) { if( tuple->mButton->getFlashing() ) { mPrevArrowBtn->setFlashing( TRUE ); } } else if( max_scroll_visible < idx ) { if( tuple->mButton->getFlashing() ) { mNextArrowBtn->setFlashing( TRUE ); } } } LLUI::pushMatrix(); { LLUI::translate((F32)tuple->mButton->getRect().mLeft, (F32)tuple->mButton->getRect().mBottom, 0.f); tuple->mButton->draw(); } LLUI::popMatrix(); idx++; } if( mIsVertical && has_scroll_arrows ) { // Redraw the arrows so that they appears on top. gGL.pushMatrix(); gGL.translatef((F32)mPrevArrowBtn->getRect().mLeft, (F32)mPrevArrowBtn->getRect().mBottom, 0.f); mPrevArrowBtn->draw(); gGL.popMatrix(); gGL.pushMatrix(); gGL.translatef((F32)mNextArrowBtn->getRect().mLeft, (F32)mNextArrowBtn->getRect().mBottom, 0.f); mNextArrowBtn->draw(); gGL.popMatrix(); } } mPrevArrowBtn->setFlashing(FALSE); mNextArrowBtn->setFlashing(FALSE); }
BOOL LLTabContainer::setTab(S32 which) { if (which == -1) { if (mNextTabIdx == -1) { return FALSE; } which = mNextTabIdx; mNextTabIdx = -1; } LLTabTuple* selected_tuple = getTab(which); if (!selected_tuple) { return FALSE; } BOOL is_visible = FALSE; if (selected_tuple->mButton->getEnabled()) { setCurrentPanelIndex(which); S32 i = 0; for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) { LLTabTuple* tuple = *iter; BOOL is_selected = ( tuple == selected_tuple ); tuple->mTabPanel->setVisible( is_selected ); // tuple->mTabPanel->setFocus(is_selected); // not clear that we want to do this here. tuple->mButton->setToggleState( is_selected ); // RN: this limits tab-stops to active button only, which would require arrow keys to switch tabs tuple->mButton->setTabStop( is_selected ); if( is_selected && (mIsVertical || (getMaxScrollPos() > 0))) { // Make sure selected tab is within scroll region if (mIsVertical) { S32 num_visible = getTabCount() - getMaxScrollPos(); if( i >= getScrollPos() && i <= getScrollPos() + num_visible) { setCurrentPanelIndex(which); is_visible = TRUE; } else { is_visible = FALSE; } } else { if( i < getScrollPos() ) { setScrollPos(i); } else { S32 available_width_with_arrows = getRect().getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + TABCNTR_ARROW_BTN_SIZE + TABCNTR_ARROW_BTN_SIZE + 1); S32 running_tab_width = tuple->mButton->getRect().getWidth(); S32 j = i - 1; S32 min_scroll_pos = i; if (running_tab_width < available_width_with_arrows) { while (j >= 0) { LLTabTuple* other_tuple = getTab(j); running_tab_width += other_tuple->mButton->getRect().getWidth(); if (running_tab_width > available_width_with_arrows) { break; } j--; } min_scroll_pos = j + 1; } setScrollPos(llclamp(getScrollPos(), min_scroll_pos, i)); setScrollPos(llmin(getScrollPos(), getMaxScrollPos())); } is_visible = TRUE; } } i++; } if( selected_tuple->mOnChangeCallback ) { selected_tuple->mOnChangeCallback( selected_tuple->mUserData, false ); } } if (mIsVertical && getCurrentPanelIndex() >= 0) { LLTabTuple* tuple = getTab(getCurrentPanelIndex()); tuple->mTabPanel->setVisible( TRUE ); tuple->mButton->setToggleState( TRUE ); } return is_visible; }
void CurveWnd::onPaint(HWND hwnd){ PAINTSTRUCT ps; int xpos, ypos; int save; HDC hdc; xpos = getScrollPos(SB_HORZ); ypos = getScrollPos(SB_VERT); hdc = BeginPaint(hwnd, &ps); save = SaveDC(hdc); if(ypx == NULL){ EndPaint(hwnd, &ps); return; } SelectObject(hdc, gridPen); double gridmax, gridmin; double rem; if(cs->gridy > 0.0){ rem = fabs(fmod(maxy, cs->gridy)); if(fabs(rem - cs->gridy) < 1.0e-10) gridmax = maxy; else gridmax = maxy - rem; rem = fabs(fmod(miny, cs->gridy)); if(rem < 1.0e-10 || fabs(rem - cs->gridy) < 1.0e-10) gridmin = miny; else gridmin = miny + (cs->gridy - rem); int maxypx = (int)(maxy * ysf); int gridypx; for(double y = gridmin; y <= gridmax; y += cs->gridy){ gridypx = maxypx - (int)(y * ysf); MoveToEx(hdc, MARGIN - xpos, MARGIN + gridypx - ypos, NULL); LineTo(hdc, gwidth + MARGIN - xpos, MARGIN + gridypx - ypos); } } if(cs->gridx > 0.0){ rem = fabs(fmod(cs->x2, cs->gridx)); if(fabs(rem - cs->gridx) < 1.0e-10) gridmax = cs->x2; else gridmax = cs->x2 - rem; rem = fabs(fmod(cs->x2, cs->gridx)); if(rem < 1.0e-10 || fabs(rem - cs->gridx) < 1.0e-10) gridmin = cs->x1; else gridmin = cs->x1 + (cs->gridx - rem); int gridxpx; for(double x = gridmin; x <= gridmax; x += cs->gridx){ gridxpx = (int)((x - cs->x1) * xsf); MoveToEx(hdc, MARGIN + gridxpx - xpos, MARGIN - ypos, NULL); LineTo(hdc, MARGIN + gridxpx - xpos, MARGIN + gheight - ypos); } } SelectObject(hdc, GetStockObject(BLACK_PEN)); MoveToEx(hdc, MARGIN - xpos, ypx[0] + MARGIN - ypos, NULL); for(int xpx = 1; xpx <= gwidth; xpx++) LineTo(hdc, xpx + MARGIN - xpos, ypx[xpx] + MARGIN - ypos); RestoreDC(hdc, save); EndPaint(hwnd, &ps); }
static LRESULT CALLBACK areaWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { void *data; DWORD which; uintptr_t heldButtons = (uintptr_t) wParam; LRESULT lResult; data = getWindowData(hwnd, uMsg, wParam, lParam, &lResult); if (data == NULL) return lResult; switch (uMsg) { case WM_PAINT: paintArea(hwnd, data); return 0; case WM_ERASEBKGND: // don't draw a background; we'll do so when painting // this is to make things flicker-free; see http://msdn.microsoft.com/en-us/library/ms969905.aspx return 1; case WM_HSCROLL: scrollArea(hwnd, data, wParam, SB_HORZ); return 0; case WM_VSCROLL: scrollArea(hwnd, data, wParam, SB_VERT); return 0; case WM_SIZE: adjustAreaScrollbars(hwnd, data); return 0; case WM_ACTIVATE: // don't keep the double-click timer running if the user switched programs in between clicks areaResetClickCounter(data); return 0; case WM_MOUSEMOVE: areaMouseEvent(hwnd, data, 0, FALSE, heldButtons, lParam); return 0; case WM_LBUTTONDOWN: SetFocus(hwnd); areaMouseEvent(hwnd, data, 1, FALSE, heldButtons, lParam); return 0; case WM_LBUTTONUP: areaMouseEvent(hwnd, data, 1, TRUE, heldButtons, lParam); return 0; case WM_MBUTTONDOWN: SetFocus(hwnd); areaMouseEvent(hwnd, data, 2, FALSE, heldButtons, lParam); return 0; case WM_MBUTTONUP: areaMouseEvent(hwnd, data, 2, TRUE, heldButtons, lParam); return 0; case WM_RBUTTONDOWN: SetFocus(hwnd); areaMouseEvent(hwnd, data, 3, FALSE, heldButtons, lParam); return 0; case WM_RBUTTONUP: areaMouseEvent(hwnd, data, 3, TRUE, heldButtons, lParam); return 0; case WM_XBUTTONDOWN: SetFocus(hwnd); // values start at 1; we want them to start at 4 which = (DWORD) GET_XBUTTON_WPARAM(wParam) + 3; heldButtons = (uintptr_t) GET_KEYSTATE_WPARAM(wParam); areaMouseEvent(hwnd, data, which, FALSE, heldButtons, lParam); return TRUE; // XBUTTON messages are different! case WM_XBUTTONUP: which = (DWORD) GET_XBUTTON_WPARAM(wParam) + 3; heldButtons = (uintptr_t) GET_KEYSTATE_WPARAM(wParam); areaMouseEvent(hwnd, data, which, TRUE, heldButtons, lParam); return TRUE; case msgAreaKeyDown: return (LRESULT) areaKeyEvent(data, FALSE, wParam, lParam); case msgAreaKeyUp: return (LRESULT) areaKeyEvent(data, TRUE, wParam, lParam); case msgAreaSizeChanged: adjustAreaScrollbars(hwnd, data); repaintArea(hwnd, NULL); // this calls for an update return 0; case msgAreaGetScroll: getScrollPos(hwnd, (int *) wParam, (int *) lParam); return 0; case msgAreaRepaint: repaintArea(hwnd, (RECT *) lParam); return 0; case msgAreaRepaintAll: repaintArea(hwnd, NULL); return 0; default: return DefWindowProcW(hwnd, uMsg, wParam, lParam); } xmissedmsg("Area", "areaWndProc()", uMsg); return 0; // unreached }
static void paintArea(HWND hwnd, void *data) { RECT xrect; PAINTSTRUCT ps; HDC hdc; HDC rdc; HBITMAP rbitmap, prevrbitmap; RECT rrect; BITMAPINFO bi; VOID *ppvBits; HBITMAP ibitmap; HDC idc; HBITMAP previbitmap; BLENDFUNCTION blendfunc; void *i; intptr_t dx, dy; int hscroll, vscroll; // FALSE here indicates don't send WM_ERASEBKGND if (GetUpdateRect(hwnd, &xrect, FALSE) == 0) return; // no update rect; do nothing getScrollPos(hwnd, &hscroll, &vscroll); hdc = BeginPaint(hwnd, &ps); if (hdc == NULL) xpanic("error beginning Area repaint", GetLastError()); // very big thanks to Ninjifox for suggesting this technique and helping me go through it // first let's create the destination image, which we fill with the windows background color // this is how we fake drawing the background; see also http://msdn.microsoft.com/en-us/library/ms969905.aspx rdc = CreateCompatibleDC(hdc); if (rdc == NULL) xpanic("error creating off-screen rendering DC", GetLastError()); // the bitmap has to be compatible with the window // if we create a bitmap compatible with the DC we just created, it'll be monochrome // thanks to David Heffernan in http://stackoverflow.com/questions/23033636/winapi-gdi-fillrectcolor-btnface-fills-with-strange-grid-like-brush-on-window rbitmap = CreateCompatibleBitmap(hdc, xrect.right - xrect.left, xrect.bottom - xrect.top); if (rbitmap == NULL) xpanic("error creating off-screen rendering bitmap", GetLastError()); prevrbitmap = (HBITMAP) SelectObject(rdc, rbitmap); if (prevrbitmap == NULL) xpanic("error connecting off-screen rendering bitmap to off-screen rendering DC", GetLastError()); rrect.left = 0; rrect.right = xrect.right - xrect.left; rrect.top = 0; rrect.bottom = xrect.bottom - xrect.top; if (FillRect(rdc, &rrect, areaBackgroundBrush) == 0) xpanic("error filling off-screen rendering bitmap with the system background color", GetLastError()); i = doPaint(&xrect, hscroll, vscroll, data, &dx, &dy); if (i == NULL) // cliprect empty goto nobitmap; // we need to blit the background no matter what // now we need to shove realbits into a bitmap // technically bitmaps don't know about alpha; they just ignore the alpha byte // AlphaBlend(), however, sees it - see http://msdn.microsoft.com/en-us/library/windows/desktop/dd183352%28v=vs.85%29.aspx ZeroMemory(&bi, sizeof (BITMAPINFO)); bi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER); bi.bmiHeader.biWidth = (LONG) dx; bi.bmiHeader.biHeight = -((LONG) dy); // negative height to force top-down drawing bi.bmiHeader.biPlanes = 1; bi.bmiHeader.biBitCount = 32; bi.bmiHeader.biCompression = BI_RGB; bi.bmiHeader.biSizeImage = (DWORD) (dx * dy * 4); // this is all we need, but because this confused me at first, I will say the two pixels-per-meter fields are unused (see http://blogs.msdn.com/b/oldnewthing/archive/2013/05/15/10418646.aspx and page 581 of Charles Petzold's Programming Windows, Fifth Edition) // now for the trouble: CreateDIBSection() allocates the memory for us... ibitmap = CreateDIBSection(NULL, // Ninjifox does this, so do some wine tests (http://source.winehq.org/source/dlls/gdi32/tests/bitmap.c#L725, thanks vpovirk in irc.freenode.net/#winehackers) and even Raymond Chen (http://blogs.msdn.com/b/oldnewthing/archive/2006/11/16/1086835.aspx), so. &bi, DIB_RGB_COLORS, &ppvBits, 0, 0); if (ibitmap == NULL) xpanic("error creating HBITMAP for image returned by AreaHandler.Paint()", GetLastError()); // now we have to do TWO MORE things before we can finally do alpha blending // first, we need to load the bitmap memory, because Windows makes it for us // the pixels are arranged in RGBA order, but GDI requires BGRA // this turns out to be just ARGB in little endian; let's convert into this memory dotoARGB(i, (void *) ppvBits, FALSE); // FALSE = not NRGBA // the second thing is... make a device context for the bitmap :| // Ninjifox just makes another compatible DC; we'll do the same idc = CreateCompatibleDC(hdc); if (idc == NULL) xpanic("error creating HDC for image returned by AreaHandler.Paint()", GetLastError()); previbitmap = (HBITMAP) SelectObject(idc, ibitmap); if (previbitmap == NULL) xpanic("error connecting HBITMAP for image returned by AreaHandler.Paint() to its HDC", GetLastError()); // AND FINALLY WE CAN DO THE ALPHA BLENDING!!!!!!111 blendfunc.BlendOp = AC_SRC_OVER; blendfunc.BlendFlags = 0; blendfunc.SourceConstantAlpha = 255; // only use per-pixel alphas blendfunc.AlphaFormat = AC_SRC_ALPHA; // premultiplied if (AlphaBlend(rdc, 0, 0, (int) dx, (int) dy, // destination idc, 0, 0, (int) dx, (int)dy, // source blendfunc) == FALSE) xpanic("error alpha-blending image returned by AreaHandler.Paint() onto background", GetLastError()); // clean up after idc/ibitmap here because of the goto nobitmap if (SelectObject(idc, previbitmap) != ibitmap) xpanic("error reverting HDC for image returned by AreaHandler.Paint() to original HBITMAP", GetLastError()); if (DeleteObject(ibitmap) == 0) xpanic("error deleting HBITMAP for image returned by AreaHandler.Paint()", GetLastError()); if (DeleteDC(idc) == 0) xpanic("error deleting HDC for image returned by AreaHandler.Paint()", GetLastError()); nobitmap: // and finally we can just blit that into the window if (BitBlt(hdc, xrect.left, xrect.top, xrect.right - xrect.left, xrect.bottom - xrect.top, rdc, 0, 0, // from the rdc's origin SRCCOPY) == 0) xpanic("error blitting Area image to Area", GetLastError()); // now to clean up if (SelectObject(rdc, prevrbitmap) != rbitmap) xpanic("error reverting HDC for off-screen rendering to original HBITMAP", GetLastError()); if (DeleteObject(rbitmap) == 0) xpanic("error deleting HBITMAP for off-screen rendering", GetLastError()); if (DeleteDC(rdc) == 0) xpanic("error deleting HDC for off-screen rendering", GetLastError()); EndPaint(hwnd, &ps); }
void MainWindow::setZoomLevel(int zoom) { off_t sample = getCenterSample(); spectrogram.setZoomLevel(zoom); scrollArea.verticalScrollBar()->setValue(getScrollPos(sample)); }
void MainWindow::setFFTSize(int size) { off_t sample = getCenterSample(); spectrogram.setFFTSize(size); scrollArea.verticalScrollBar()->setValue(getScrollPos(sample)); }