04-04-2023 05:39 AM
I have a list of data (in a drop-down menu) each with two parts that need to be separated with two aligned borders like so:
part1 ||| 2nd string |||
variable len part1||| part2 |||
more data ||| 2nd part |||
However, since '\t' doesn't work (and even if it did I wouldn't know how to change the tab stops), I'm trying to make a method that calculates the number of space characters needed to align the borders. I've tried taking a screenshot of all the valid characters and measuring the pixel widths of each one, but not only did this take longer than anticipated the result wasn't accurate enough to align the borders to the nearest width of a space character.
So instead I want to get access to the particular font's metrics. According to this page you do that by going through the Font class (presumably Font.QueryTextMetrics() will have what I need). Unfortunately, the aforementioned page has no information for working with Font objects in C, * and repeatedly references things that I have no knowledge of such as .tlb files, and CComPtr-s. I included ocidl.h and that got me access to the IFontDisp type, but not the Font class itself. However, even if an IFontDisp has everything I need to align text correctly I still have no idea how to actually get the font object from the control.
Assuming Tab-spaces are not an option, in order to get the metrics I want I need to at least know:
For instance, while I was able to compile:
IFontDisp* fonty = NULL;//I have to initialize fonty before it can be passed into GetCtrlVal
int temp = GetCtrlVal(hConfig, ATTR_TEXT_FONT, (void*)fonty);
auto typeinfo = fonty->lpVtbl->GetTypeInfo;
Not only does GetCtrlVal not "return" the correct type into fonty it can't set anything into fonty at all, as ATTR_TEXT_FONT is not a valid control.
(The reason I'm spelling this all out is because I don't want to get another answer like "include ocidl.h". But I also realize that this is a lot to ask, and well that's because I don't really know where else to go to get answers, previous form posts just ask me to click on a bunch of broken links which redirect me back to the front page of the documentation, and neither that documentation nor any of the other resources I've found on this site so far are made for programmers working with the CVI libraries in Visual C.)
Lastly, being that fixing the alignment issue is taking up as much time as it is, I'm looking for a more permanent/precise solution to the problem than just aligning the text to the nearest width of a space, which means that I'm either going to need to input variably lengthed padding, thin spacing characters (U+2004 to U+200A), or at this point, I would prefer to just use tab stops if there's a simple function that enables tabs and they line up close enough to where I need them. And if that is the case then, perhaps I shouldn't even need to access the Font class to align text.
*Although I was told the project I am working on was going to be C++, the entire project appears to be in Visual C. Making this effectively the first project I've had to debug in C, and the first project that uses LabWindows/CVI.
P.S. I'm supposed to attach a zip file with my source code, but I agreed not to share it, and I don't think creating a new project that "recreates the issue" would be necessary. Also sorry for rambling.
04-04-2023 08:30 AM - edited 04-04-2023 08:33 AM
I think you can do that more simply:
- determine the font/metafont name used in your menu. I think it's GetMenuBarAttribute (, , ATTR_MENU_BAR_FONT, );
- call GetTextDisplaySize() with "part1 |||" and a variable number of spaces in the middle to determine the width you want.
- done
Alternatively I think (but I've never tried) you can use images in a menu. So write your string in a canvas any way you want, then somehow (*) place it in your menu. (*) using bitmap or file intermediaries.
04-05-2023 06:19 PM
Oh, thank you, oh thank you! I'm sorry my question was so verbose, but I still have a problem. How do I use GetMenuBarAttribute, and GetTextDisplaySize? I've changed my code to set the spacing to below, but GetMenuBarAttribute keeps returning an error code of -41 ("The ID is not a menu bar ID.") without doing anything. Likewise, GetTextDisplaySize returns -89 with no action. I assume I'm passing in the wrong values (duh), and for GetMenuBarAttribute(hConfig, pConfig_ddlCommPort, ATTR_MENU_BAR_FONT, font_name) these "expand" to: GetMenuBarAttribute(2, 3, 1015, font_name). Given the data is being placed into a drop-down menu (at the moment), does this not make sense? I would assume that if a value of 3 works for InserListItem it would work for GetMenuBarAttribute, but I guess not.
const unsigned int long_p1_len = 150, long_p2_len = 200;//The total amount of space in each part.
char* font_name = malloc(22 * sizeof(char)); //^-150 and 200 are stubs, later they should be set to the width of about 16 chacters, with the latter being a bit longer.
int height = 12, space_width, p1width = 100, p2width = 200, sts, spaces0, spaces1, spaces2;//12, 100, and 200 are stubs. They need to have values to enter GTDS.
char buffer[256], spacing1[SerialCharLimit * 4], spacing2[SerialCharLimit * 4];
Assert(strlen(second_part) <= SerialCharLimit);
int error = GetMenuBarAttribute(hConfig, pConfig_ddlCommPort, ATTR_MENU_BAR_FONT, font_name);//Get the name of the font.
spaces0 = GetTextDisplaySize(' ' , font_name, &height, &space_width);//Get the width of a space character using GetTextDisplaySize (GTDS).
spaces1 = GetTextDisplaySize(first_part , font_name, &height, &p1width);//Get the width of the first part.
spaces2 = GetTextDisplaySize(second_part, font_name, &height, &p2width);//Get the width of the second part.
spaces1 = (long_p1_len - p1width) / space_width;//Calculate the number of space characters in the first part.
spaces2 = (long_p2_len - p2width) / space_width;//Calculate the number of space characters in the second part.
memset(spacing1, ' ', spaces1 * sizeof(char));//Add a string of spaces to align the separators when displayed.
memset(spacing2, ' ', spaces2 * sizeof(char));
spacing1[spaces1] = '\0'; //This should terminate the string.
spacing2[spaces2] = '\0'; //This should terminate the string.
sprintf(buffer, "%s%s||| %s %s|||", first_part, spacing1, second_part, spacing2);
inp = InsertListItem(hConfig, pConfig_ddlCommPort, -1, buffer, second_part);
04-07-2023 02:45 AM
How did you get the menu bar handle ? Using GetPanelMenuBar() ? And your font_name malloc looks too small. Either use ATTR_MENU_BAR_FONT_NAME_LENGTH or a large enough static allocation. I think there might be other issues, your code looks overly complicated.