How to tell the position of a menu item in VB6? - windows

I am using Visual Basic 6 (please don't laugh), and I would like to dynamically position a control, at runtime, in a position based upon the position of one of the "top level" menu items (such as "File", "Edit", "View").
Unfortunately, VB's Menu control (which is the type of control that these "File", "Edit", etc. things are) does not have any properties like "Top", "Left", "Height", or "Width".
I could just experiment, eyeballing it, and eventually arrive at numbers that I'll hardcode, but for various reasons I would prefer that the code actually figure out where the control should go.
I am thinking that perhaps there is some Windows API call that I can use to figure out the position of the Menu control?
Thanks in advance.
Edit: In case it matters, this is on an MDI form.
Edit #2:
OK, answering my own question:
You can get the position of each item on the main menu bar via the GetMenuBarInfo function, such as:
Dim mbi as MENUBARINFO
mbi.cbSize = LenB(mbi)
GetMenuBarInfo Me.hWnd, OBJID_MENU, lMenuNumber, mbi
Where "lMenuNumber" is 1 for the first (e.g. "File"), 2 for the second (e.g. "Edit"), etc.
That mbi struct has an rcBar member, which is the coordinates of the rectangle where that menu item is.
Unfortunately, it's in absolute coordinates relative to the screen, not relative to the MDI form. So, get the mbi the one you're interested in and of the first, and subtract.
Plus, the position as given in the mbi is in pixels, so convert as necessary to twips or whatever.

This is sort of doomed. Standard menus will vary in appearance and width with the version of Windows and the user's settings for UI fonts and such. For all we know Windows Next/8 may put them along the left side of the window!
If this information were meant to be used it would be available.
Perhaps if you could explain more about what you are trying to do with your UI we might have alternative suggestions.

OK, answering my own question:
You can get the position of each item on the main menu bar via the GetMenuBarInfo function, such as:
Dim mbi as MENUBARINFO
mbi.cbSize = LenB(mbi)
GetMenuBarInfo Me.hWnd, OBJID_MENU, lMenuNumber, mbi
Where "lMenuNumber" is 1 for the first (e.g. "File"), 2 for the second (e.g. "Edit"), etc.
That mbi struct has an rcBar member, which is the coordinates of the rectangle where that menu item is.
Unfortunately, it's in absolute coordinates relative to the screen, not relative to the MDI form. So, get the mbi the one you're interested in and of the first, and subtract.
Plus, the position as given in the mbi is in pixels, so convert as necessary to twips or whatever.

Related

How to grab the scrolling amount in one window?

This is the first step to scroll two windows simulteneously, the second step is to find a way to apply that amount to the other program. But I really don't know where to start, all I see in from Google is about modifying the behavior of the scroll of the mouse, not the scrolling amount in one window. Advantages of using this instead of listening to the keys:
Scrolling will be seamless since the other program are scrolled in the background
Clicking on the scrollbar works
Different scrolling speeds don't affect the comparison
Moving lines in text editors won't scroll pages in PDF viewers
I can tell you what I would do, but it's not going to be fun...
Assuming the text is roughly evenly distributed (which it might not be between two languages like that, and two windows with different text sizes and widths, really consider this carefully before you do the work), then the goal is to force both scroll bars to be at the same percent relative to their whole. So what you need to do is write a function to determine what percent each scroll bar is at. I would screen capture both windows and crop out the important parts of the scroll bar like this:
Specifically the up and down buttons, the top of the scroll handle, the bottom of the scroll handle. Save them in their own files. Do it for both windows in case they draw their scroll bars a little differently.
Now the tricky part. Write a function that does the following: imagesearch for the top button within rightmost 25 pixels or of the specified window. Same for the bottom button. Same for the top of the handle. Same for the bottom of the handle. Use this to determine where your window is.
SetTitleMatchMode, 2 ; so it matches the end of the title
WinGetPos , X, Y, Width, Height, LibreOffice Writer ;exact substring of window name required
Use something like this to find the scroll bar parts.
CoordMode, Pixel , Screen ;so image search searches entire screen
barwidth = 25 ; make sure it's more than the bar is wide.
ImageSearch, TopButtonX, TopButtonY, X+Width-barwidth , Y, X+Width, Y+Height, TopButton.bmp ; no jpg, fuzzy edges make searches fail
Then do some math, something like:
TopButtonY := TopButtonY+TopButtonImageHeight ; because we only care about the position of the bottom of the button.
BottomHandleY := BottomHandleY+BottomHandleImageHeight ; because we only care about where the bottom of the handle is.
HandleHeight := TopHandleY - BottomHandleY ; how tall the scroll handle is
TotalHeight := TopButtonY - BottomButtonY - HandleHeight ;how tall the scroll field is
HandleOffset := TopHandleY - TopButtonY ;how far it is from the top
HandlePercent := HandleOffset / TotalHeight ; the part we care about. return this value
With a function like that, you can know how scrolled each window is. All that's left is to send the scroll commands. There's a few choices.
; ControlSend , Control, Keys, LibreOffice Writer
ControlSend , Control, {Pgdown}, LibreOffice Writer ; or {Pgup}
ControlSend , Control, {WheelUp}, LibreOffice Writer ; or {wheeldown}
ControlSend , Control, {Up}, LibreOffice Writer ; or {down}
If it lets you move the caret with up/down arrows while the window is inactive, that is probably the most precise option, even if it takes a bit longer. The fastest most precise way is to simulate a click drag also using control send. To use ControlSend you need to figure out which control you're working on. WindowSpy can help you with that.
So first: Find the scroll positions of both windows. Second determine which window is active. Third, nudge the inactive window in the correct direction. Repeat until they're within a certain tolerance range (otherwise it will bounce up and down endlessly).
I can't emphasize this enough, but please make sure that getting the scroll bars in approximately the same positions is sufficiently close before even attempting this. If it isn't, you will have wasted a lot of time fiddling with it. Keep in mind it will be less and less accurate the longer the text is.
If it is an option, I would definitely consider copying the contents of both windows into a program that gives you more access to the controls (or better one that is specialized for this purpose). If you had more access, you could use the paragraph breaks to line up the texts with far more precision.
If you really just want to see both texts side by side (and the paragraphs do line up), you could find a text editor that tells you information like this:
If autohotkey will let you read the text of that information, you can copy the PDF into autohotkey's memory (separated by line) and use autohotkey to show only the corresponding paragraph of the PDF as you move around in the editable document.
Hope something I said helps, good luck.

How can I make a certain part of my form scrollable?

I'm making a program in vb6 that requires me to be able to scroll a certain area of the screen to the right. It consists solely of lines and picture boxes, is there a way to only scroll that area? Thanks!
You can take advantage from the fact that some visual controls can act as container of other visual controls.
Just an example:
In the VBIDE, place a Frame over a VB Form. Then - inside this Frame place a PictureBox. Pay attention that the PictureBox shall be fully contained inside this Frame.
Now, if you drag the Frame around the Form, you will see that the PictureBox inside is moving together, while keeping the position inside the container Frame, i.e. it will keep the original top & left coordinates relative to the container control.
To find out which visual controls have this capability, simply retry the test. You will see, for example, that a Label can't act as a container.
That said, you need following:
one visual control (preferably a PictureBox) which act as container (the viewport)
one visual control (preferably a PictureBox) which act as scrollable area
a HScrollBar (and, optionally, a VScrollBar) to scroll the
view-able area
Now, inside the second PictureBox (the scrollable area) you can place your controls, the Lines and PictureBoxes you mentioned in your question.
Why is a PictureBox preferable? Because you can profit from the ScaleMode property, set it to 3-Pixeland use pixel-exact scrolling. With Frames you can't do that, you are limited to just Twips.
By using a contained control you have two advantages:
you can visually place and reposition the controls ypu need inside the IDE
you need to scroll just only one control - all other hosted controls will
move together
The boring thing you must code is the synchronization of the container with the ScrollBars.
But luckily, as VB6 has been going a long way, you will find enough cut-and-paste code examples of such a task, one of which is on VBForums: Scroll bar in picturebox
Some final notes:
PictureBoxes in VB6 are constrained to a maximum size of 16,383 x 16,383 pixels. If your scrollable area should be bigger, you may implement a kind of custom "infinite scroller", and manage the position of your controls by grouping them, and you will need some additional coding.
ScrollBars in VB6 can range from a minimum value of -32,768 to a maximum value of 32,767. If you need more, you will end up with some other additional coding tasks.
If you stick to Twips, you can have a bigger logical area available - for example: until 245,745 with the typical 15 TwipsPerPixel - but you can't use such a big value with ScrollBars.
Here is a simple example illustrating what you requested. The key is that the scrollable area must be a container control hosting the controls you wish to scroll.
Option Explicit
Private oldPos As Integer
Private Sub Form_Load()
HScroll1.Min = 0
HScroll1.Max = 1000
HScroll1.SmallChange = Screen.TwipsPerPixelX * 10
HScroll1.LargeChange = HScroll1.SmallChange
End Sub
Private Sub HScroll1_Change()
ScrollPictureBox
End Sub
Private Sub HScroll1_Scroll()
ScrollPictureBox
End Sub
Private Sub ScrollPictureBox()
Dim c As Control
For Each c In Me.Controls
If c.Container.Name = "Picture1" And Not TypeOf c Is HScrollBar Then
c.Left = c.Left - (oldPos - HScroll1.Value)
End If
Next
oldPos = HScroll1.Value
End Sub
In this code, Picture1 is a PictureBox (the scrollable area) containing HScroll1 (a horizontal scrollbar) and the other controls you wish to scroll.

How can I measure the width of the caption bar available for text?

I'm trying to adjust the caption of my main window to show as much of a file name as is possible to fit in the caption area. So, I'm looking to calculate the width of the area marked up here in the red rectangle:
Now, I would ideally like to have code that can use whatever system metrics are available and thereby avoid being caught out by all the various platform/theme/dpi variations that can exist.
How can this be done, if indeed it is even possible?
As andlabs said in the comments, the WM_GETTITLEBARINFOEX message can be used to obtain the required information.
This message can be sent to the window and the TITLEBARINFOEX struct is populated with the state and location of the titlebar and each of its buttons. From there it is a simple task to determine how much space is available for the caption.
First, call GetTitleBarInfo, passing a pointer to the TITLEBARINFO structure:
TITLEBARINFO tbi;
GetTitleBarInfo(hwnd, &tbi);
The width of the titlebar is tbi.rcTitlebar.right - tbi.rcTitlebar.left. But that includes the three buttons (Close, Minimize, and Maximize). The width of one button is GetSystemMetrics(SM_CXSIZE), so the width of the title bar minus the three buttons is
(tbi.rcTitlebar.right - tbi.rcTitlebar.left) - (3 * GetSystemMetrics(SM_CXSIZE))
Of course, the code works on all versions of Windows where the width of each of the 3 buttons is equal

How to find absolute value of caret position in pixels using Cocoa in MacOS?

For mouse I'm using:
ourEvent = CGEventCreate(None);
currentpos = CGEventGetLocation(ourEvent);
What can I use for the caret?
First the bad news.
Not every app is Cocoa-based, and those that are neither Cocoa nor Carbon nor a straight mix of the two—i.e., those based on wxWidgets, Qt, or some other cross-platform framework—typically reimplement the entire GUI stack on top of raw event and drawing primitives.
That means that there is typically no way to get this information from those applications (unless they're scriptable and expose it that way).
The good news is, Cocoa apps and some Carbon apps may expose this via Accessibility.
The user will need to have assistive devices turned on in System Preferences. Once that condition is met, you can use the Accessibility framework to get the frontmost application, get its focused window, get its focused view, and get its selection ranges.
A text view with an insertion point has exactly one selection range, and that range is empty (length=0). The location is where the insertion point is.
Of course, those are character indexes, not on-screen bounds.
That's where parameterized attributes come in. There's one for converting ranges to bounds. That's the one you want.
Theoretically (I haven't tried this), you should be able to convert the empty range of the insertion point to an empty or nearly-empty rectangle whose location is somewhere within the vertical line of the insertion point.
Make sure you test this with text views that are in scroll views, particularly when the insertion point is scrolled partially or completely out of view.
You'll want to use the Accessibility Inspector to see for yourself where your application will need to look, and to test individual applications and investigate reported failures.
You can get it from the Developer Downloads page, in the “Accessibility Tools” disk image.
If you want to focus a window, forging a mouse event to click on it is a bad idea—anything can happen if you click on the wrong thing. Send the window an kAXRaiseAction action instead.
If you want to set a text view's insertion point (and are looking to find where you need to forge a mouse event to click to set it in the desired position), again, that's a bad way to do it. Set the view's kAXSelectedTextRangesAttribute attribute instead. Again, an insertion point is a single empty range.
Did you try like this below?
NSPoint p=[[NSApp currentEvent]locationInWindow];
CGFloat X=p.x;
CGFloat Y=p.y;
NSLog(#"%f %f",X,Y);

TabCtrl_SetItemSize and user drawn tab controls

I have this Win32 user-drawn tab control that is created as:
CONTROL "Tab1",IDC_TAB_CONT,"SysTabControl32",TCS_BOTTOM |
TCS_OWNERDRAWFIXED | NOT WS_VISIBLE,0,14,185,88
I'd like for this control to have its tabs resize as never to have to see the "sliding arrows":
Now, pretty much everything about this control works as expected, except for that fact that it won't respond to TabCtrl_SetItemSize. Try as I may, the size I get for the tabs when I get to draw them (in the DRAWITEMSTRUCT passed to WM_DRAWITEM) is always the size that fits the longest caption in them and never the size I've set with TabCtrl_SetItemSize.
However, in the TabCtrl_SetItemSize documentation, it says that:
[TabCtrl_SetItemSize] sets the width and height of tabs in a
fixed-width or owner-drawn tab
control.
The only way I've managed to have a decent resizing is by setting a dummy string of the desired length in it by sending the control a TCM_SETITEM message, and writing the desired text in it at draw time. This is rather inconvenient and not a particularly nice hack.
Is there anybody who would know
Why TabCtrl_SetItemSize isn't working as expected? and/or
How to set the tab size properly?
Many thanks,
joce.
Setting TCS_OWNERDRAWFIXED style is not enough, you have also to add TCS_FIXEDWIDTH style.
The minimum size of a tab is at least icon width + 3 if icon is present.
If you have icons (imageList attached to tabControl), you might get those "sliding arrows" even with fixed width (if there is less space available than: number of tabs*(icon width+3)

Resources