Click here to Skip to main content
Click here to Skip to main content

Style Inspector

By , 12 Dec 2004
Rate this:
Please Sign up or sign in to vote.

Sample Image

Introduction

Style Inspector is a simple tool tailored to current needs of people working in teams to develop web pages or HTML based documentation, teams within which brilliant people use daily their native talent and knowledge to achieve the tasks. Frequently, documents make use of "in-line" style sheets while plenty of style sheets are referenced by a bunch of multiple pages. All of a sudden, a team member decides to use some style elements on individual items within the page he is working on. "At the end of the day", the command closest to the element will be the one to affect the look of the document. To keep the whole work professional, there should be one (or more) members of the team dealing with checking the results and making sure the outcome fits to the target(s).

Last but not least, there are the people learning HTML and willing to use CSS to control their pages. One of the ways to do it is by looking at how others are dealing with that. I bet those people would appreciate a tool allowing them to "go" over an HTML page and "see" what is happening beyond the scene.

Using the Style Inspector

Style Inspector consists mainly of two windows: the info window and the hit window, both created with the "top most" flag set, so that they stay always on top of the other windows on your screen.

The info window keeps track of the opened browser windows. The URL used of each browser instance to display an HTML content is listed within this window as soon as the loading process is ended.

If only one browser window is present (meaning, you get only one item in the info list), the document displayed by it is automatically selected. Otherwise, you have to choose an item from the info list in order to enable the actual use of the hit window.

You should pay attention to one important thing: the info list refers the different instances of your internet browser and not the displayed pages. So, should you navigate with your browser, you must click on the "Update" button of the hit window in order to inspect the latest document(s).

Once you have selected an item from the info list, the "Origin" button of the hit window turns active and you may start inspecting the loaded document.

Grab the upper half of the diamond within the hit window (that is actually part of the title bar of the window allowing you to drag the window over the screen) and move the hit window so that its top-left corner points to the top-left corner of the "view" area of your browser. Click now the "Origin" button.

From now on, the current "hit" position within the browser display area is shown at the bottom of the hit window. The yellow pointer (the icon of the hit window) will point to an HTML element while moving the hit window around. The type of the hovered element (the HTML tag) is displayed in the info window, along with its position, size and style.

Individual HTML elements may be affected by in-line style elements, rather than spanned styles. In that case, the information related to the style is referred as "local" within the info window. Only inspection for color and font is supported.

You may remark differences between the hit position and the position displayed for an HTML element while inspecting the page. That is entirely OK! The hit point is relative to the top-left corner of the "view" area of your browser, while an HTML element position is computed relative to the top-left corner of the entire document (document which can be scrolled within the browser). Just do not forget to set again the origin whenever the browser's position (and not the document scroll) changes on the screen.

Note: you should also repeat the process of setting the origin when you customize the appearance of your browser (this also affects the position of the "view" area on the screen).

How this works

Note: The code blocks within the article are simplified and some of the variable names are adjusted for a better understanding of the explanation, when compared to the source code.

At start, Style Inspector makes use of a SHDocVw::IShellWindowsPtr m_pInterface pointer to create an instance of a ShellWindows connectable object. In case of success, it "asks" the object about its outgoing interface by using QueryInterface() on IID_IConnectionPointContainer. If the object supports the outgoing interface, the application gets a valid pointer to the connection point container. Read more on how to use connectable objects on the help and support site of Microsoft. With that pointer, it finds the specific event sink interface and retrieves a pointer to the connection point object. In case of success, it establishes the connection between the sink and the connection point object, and retrieves a cookie value for terminating the connection later. At this point, Style Inspector creates a list of open browser windows and fills the list of corresponding document URLs within the info window, using its specially created function UpdateListOfWindows().

for (long i=0; i<m_pInterface->GetCount(); i++)
{
    _variant_t va(i, VT_I4);
    IDispatchPtr lpDisp = m_pInterface->Item(va);
    SHDocVw::IWebBrowser2Ptr lpBrowser(lpDisp);
    if ( lpBrowser != NULL )
    {
        // Get location name
        .....
        // Get location URL
        .....
        // Decide if dealing with a html/htm/shtml page
        .....
        // Add URL as new item to the list
        .....
        // Set the item data pointer to the value of lpBrowser;
        .....
        lpBrowser->AddRef();
    }
}

If the resulted list of URLs contains only one item, that item is automatically selected within the list, a boolean m_bPageIsLoadedAndSelected flag is set to FALSE, and Style Inspector starts polling the busy status of the selected browser using a 100 ms timer (otherwise, the user must make a selection). As long as the browser is busy finding and loading the document, the "Origin" button of the hit window stays disabled.

The UpdateListOfWindows() function is called each time a browser window instance is registered or revoked, through the following dispatch mechanism, within the IsRegistered() and IsRevoked() functions respectively:

BEGIN_DISPATCH_MAP(CStyleInspectorDlg, CCmdTarget)
    DISP_FUNCTION_ID(CStyleInspectorDlg, "WindowRegistered", 
                     0x000000C8, IsRegistered, VT_EMPTY,VTS_I4)
    DISP_FUNCTION_ID(CStyleInspectorDlg, "WindowRevoked", 
                     0x000000C9, IsRevoked, VT_EMPTY,VTS_I4)
END_DISPATCH_MAP()

Once the browser is no longer busy, the "Origin" button turns active and the user can perform the process of setting the origin as described earlier in this article. Behind the scene, Style Inspector collects all the information on style(s) for the loaded document before setting the m_bPageIsLoadedAndSelected flag to true. Assuming IWebBrowser2 *lpSelBrowser to be the pointer to the currently selected browser instance and lpDispatch a pointer to the IDispatch interface, Style Inspector obtains a pointer to the loaded document:

lpSelBrowser->get_Document(&lpDispatch);
lpDispatch->QueryInterface(IID_IHTMLDocument2, (void**)&lpHtmlDocument);

and calls the FindStyleClasses() function:

if ( lpHtmlDocument ) FindStyleClasses(lpHtmlDocument);

where lpHtmlDocument is of IHTMLDocument2* type. Within this function, it uses the lpHtmlDocument->get_styleSheets() function to retrieve a pointer to the collection of elements used by the document and then iterates through that collection:

IHTMLStyleSheetsCollection *pElemCollection;
IUnknown *lpUnknown;
IEnumVARIANT *lpNewEnum;
IHTMLStyleSheet *lpElement;
VARIANT varElement;
BSTR bstrStyleSheet;
CString strStyle;

if ( lpHtmlDocument->get_styleSheets(&pElemCollection) != S_OK ) return;
if ( pElemCollection == NULL ) return;
if ( !m_listElements.IsEmpty() ) m_listElements.RemoveAll();
// where m_listElements is the list of used syles (css)

if (SUCCEEDED(pElemCollection->get__newEnum(&lpUnknown)) && lpUnknown != NULL)
{
    lpUnknown->QueryInterface(IID_IEnumVARIANT,(void**)&lpNewEnum);
    if (lpNewEnum == NULL) return;
    while (lpNewEnum->Next(1, &varElement, NULL) == S_OK)
    {
        if (varElement.vt != VT_DISPATCH) { VariantClear(&varElement); break; }
        varElement.pdispVal->QueryInterface(IID_IHTMLStyleSheet,(void**)&lpElement);
        if (lpElement)
        {
           lpElement->get_cssText(&bstrStyleSheet);
           _bstr_t bstr1(bstrStyleSheet);
           strStyle.Format(_T("%s"), (LPCTSTR)bstr1);
           m_listElements.AddTail(strStyle);
        }
        VariantClear(&varElement);
    }
    if ( lpNewEnum ) lpNewEnum->Release();
}

The style inspection process is entirely achieved within the OnMove() function of the hit window. Having the screen coordinates of the hit window obtained through the GetWindowRect() function and the offset values on x and y axes obtained in the process of setting the origin, Style Inspector obtains a pointer to the HTML element under the top-left corner of the hit window through:

lpHtmlDocument->elementFromPoint(xPos, yPos, &lpElement)

where lpElement is of IHTMLElement* type. Most of the time, an HTML element which a user sees is contained within another, and the containment may occur multiple times. That is why the left, top, width and height values of element pointed to by lpElement may not represent its absolute position (considered from the top-left corner of the document).

Let lLeft, lTop, lWidth and lHeight be the geometrical elements of the element pointed to by lpElement. A block of code like the following one helps getting the real values:

IHTMLElement *lpElement, *lpContainer;
long lValue, lTop, lLeft, lWidth, lHeight;

lpElement->get_offsetTop(&lValue); lTop = lValue;
lpElement->get_offsetLeft(&lValue); lLeft = lValue;
lpElement->get_offsetWidth(&lValue); lWidth = lValue;
lpElement->get_offsetHeight(&lValue); lHeight = lValue;
lpElement->get_offsetParent(&lpContainer);
while ( lpContainer )
{
    lpContainer->get_offsetLeft(&lValue); lLeft += lValue;
    lpContainer->get_offsetTop(&lValue);  lTop += lValue;
    lpContainer->get_offsetParent(&lpContainer);
}

Calling the lpElement->get_tagName() and lpElement->get_className() functions, Style Inspector retrieves the tag name of the HTML element and the particular style rule (class) within the style sheet associated with it, respectively. Then, Style Inspector "looks" for that class within the list of style elements it built before setting the m_bPageIsLoadedAndSelected flag to true, and displays the style information within the info window. After that, Style Inspector checks for the presence of local (or in-line) style elements and displays them if any:

IHTMLStyle *pStyle;
CString strInfo;
VARIANT vtValue;

lpElement->get_style(&pStyle);
if ( pStyle )
{
    // Color
    if ( pStyle->get_color(&vtValue) == S_OK )
    {
        _bstr_t bstrA(vtValue.bstrVal);
        strInfo.Format(_T(", local color:%s"), (LPCTSTR)bstrA);
        // Add this information to the already displayed information
        // ..........
    }
    // Font
    if ( pStyle->get_font(&bstrInfo) == S_OK )
    {
        _bstr_t bstrB(bstrInfo);
        strInfo.Format(_T(", local font:%s"), (LPCTSTR)bstrB);
        // Add this information to the already displayed information
        // ..........
    }
}

Instead of conclusions

Before being "cool" or not, a tool must be a tool. That means, a tool should provide you the means to perform what you need to.

Joke: Why has C++ pluses and VB not? Because C++ has something more: the local color is black Smile | :) )

History

  • Posted: Dec. 10, 2004.
  • Section "How this works" added: Dec. 12, 2004.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Mircea Puiu
Software Developer (Senior)
Europe Europe
More than 22 years of software development experience.
SCRUM Master nowadays

Comments and Discussions

 
Generalconvertion of begin_dispatch_msg for wtl Pinsusssaravanan_vv24-May-05 8:55 
GeneralRe: convertion of begin_dispatch_msg for wtl PinmemberMircea Puiu24-May-05 20:59 
General... and also this PinmemberMircea Puiu24-May-05 21:04 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140415.2 | Last Updated 13 Dec 2004
Article Copyright 2004 by Mircea Puiu
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid