• The Old New Thing

    Other tricks with WM_GETDLGCODE

    • 8 Comments

    The WM_GETDLCODE message lets you influence the behavior of the dialog manager. A previous entry on using WM_GETDLGCODE described the DLGC_HASSETSEL flag which controls whether edit control content is auto-selected when focus changes.

    I was going to write a bit about the other flags, but it turns out that Knowledge Base Article 83302 already covers this, so I'll just call out some highlights.

    The DLGC_WANTMESSAGE flag is the most powerful one. It lets your control prevent the dialog manager from handling a message. So for example if you don't want ESC to dismiss the dialog box when focus is on a particular control but rather be delivered to the control itself, handle the WM_GETDLGCODE message and peek at the lParam. If it is a press of the ESC key, then return DLGC_WANTMESSAGE so the message will not be handled by the dialog manager.

    The DLGC_WANTCHARS, DLGC_WANTTAB and DLGC_WANTARROWS flags are just conveniences that save you the trouble of checking certain categories of messages.

    As always, consider the consequences of overriding default behavior. Doing so generally makes your program harder to use, since it goes against what people are accustomed to seeing in other programs.
  • The Old New Thing

    Christmas gift idea for your favorite glasses-wearing geek

    • 8 Comments

    Yes, I'm talking about Christmas gifts (or "winter solstice gifts" if you prefer) in July. I'm one of those people for whom buying Christmas gifts is a brain-wracking ordeal, and I'm always on the lookout all year round for the "perfect gift".

    Last Christmas, a friend of mine gave me a micro-fiber lens-cleaning cloth that comes in a pouch you can attach to a keychain. Example 1. Example 2. (I have no affiliation with those two sites; I just hunted around looking for a picture.) Best gift ever. I use it several times a day.

    My keychain has only four things on it: The aforementioned cleaning cloth, a USB thumb drive, my car key, and my house key. And I could do without the thumb drive and car key most days. But don't take away my cleaning cloth.

  • The Old New Thing

    The traffic gods are punishing me for bicycling

    • 8 Comments

    It happened again.

    The last time I participated in a bicycle ride that started in Seattle, I got stuck in highway traffic both going out and coming back. The 520 bridge was closed for inspection so everybody was taking the I-90 bridge instead. But traffic at the western terminal of I-90 was backed up because the Alaska Way Viaduct was closed for some sort of fundraiser walk-a-thon. And then, after the ride was over, I got stuck in traffic on the return trip as well because the Mariners baseball game had just let out, and that on top of all the traffic created by the 520 bridge being closed.

    This weekend, heading to the starting point for a light group ride through Seattle (which our group leader nicknamed "Crying over STP" since it coincided with the annual Seattle to Portland bike ride), there was a huge back-up on westbound highway 520 due to a multiple-car accident that closed both main lanes, forcing everybody to squeeze into the car pool lane. And then after the ride was over, I got stuck on the 520 bridge at around 4:10pm. The flashing lights were on, indicating that the drawbridge was open. I turned in to the Department of Transportation highway radio station and learned that the bridge was scheduled to be open from 4pm to 4:30pm for "boat fair traffic". But at least we knew how long we were going to be waiting, so I turned off the car engine, got out, and walked around the bridge. (It's not often that you get to walk on the 520 bridge. It didn't occur to me to ride my bicycle on the bridge.)

    When I got home, I tried to find any information on this scheduled closure, but turned up nothing. I guess you just had to know.

    But at least I won't get stuck in traffic next month when I head to Mercer Island to watch the Blue Angels perform, because I'm going to ride my bicycle there. We'll see whether the hills have returned.

    Postscript 1: And I hadn't known about the Elliot Bay Trail, which runs from the marina, through the trainyards, along the waterfront, to downtown Seattle. Very nice.

    Postscript 2: You can still see the after-effects of that accident on highway 520: The light barriers on the median near Hunts Point have all been scraped off!

    Postscript 3: We stopped for lunch at the Elliott Bay Marina, looking for the the sushi place that we dimly remembered, but it turns out that they no longer exist. (We had lunch at Maggie Bluff's Grill instead.) Sushi is a somewhat unorthodox bicycle food; I amused myself with the image of a support car driving up to a rider and handing over some unagi and maguro...

  • The Old New Thing

    Buy me some peanuts and a set of double-pointed 2's

    • 8 Comments

    It's the second annual Stitch 'n Pitch at Safeco Field. Stitch n' Pitch events for other cities can be found on the Stitch n' Pitch web site. (Channeling Lynne Truss: Ahem, people, the spelling of the middle word is 'n' with an apostrophe fore and aft.) [10am: Fixed sepleling.]

    Seattle Times readers Dave Butner and Mike Wilson took issue with the event (though Mr. Wilson's outrage bordered on satirical). To me, baseball is like soccer: It's a game whose primary draw is not the actual scoring but rather the anticipation that a run might be scored. It's in the tension that builds as scoring opportunities develop (most of which prove fruitless), not in the actual scoring itself. And unlike soccer, where something exciting could happen at almost any time, in baseball, there are long stretches where you can reliably predict that nothing exciting will occur. Like, say, when the pitcher is taking a walk around the mound scratching himself. In other words, baseball is a social event, not a sporting event. And if you're going to attend a social event, why not do it with people whom you share interests with?

    It's not like I don't appreciate baseball. I know when you should perform a double-switch. I know why the catcher sometimes tags the batter with the baseball after a strikeout. I can even explain the infield fly rule. But when I attend a baseball game with friends, we don't talk about baseball the whole time. We enjoy the sunshine, catch up on each other's lives, gossip about friends who aren't present, admire some of the goofball fans in the bleachers. And enjoy a baseball game.

  • The Old New Thing

    Multiplexing multiple tools into one in a tooltip

    • 8 Comments

    The tooltip control lets you set multiple "tools" (regions of the owner window) for it to monitor. This is very convenient when the number of tools is manageably small and they don't move around much. For example, the toolbar control creates a tool for each button. But if you have hundreds or thousands of screen elements with tooltips, creating a tool for each one can be quite a lot of work, especially if the items move around a lot. For example, the listview control does not create a separate tool for each listview item, since a listview can have thousands of items, and scrolling the view results in the items moving around. Updating the tool information whenever the listview control scrolls would be extremely slow, and the work would be out of proportion to the benefit. (Updating thousands of tools on the off chance the user hovers over one of them doesn't really sit well on the cost/benefit scale.)

    Instead of creating a tool for each item, you can instead multiplex all the tools into one, updating that one tool dynamically to be the one corresponding to the element the user is currently interacting with. We'll start with a fresh scratch program and create a few items which we want to give tooltips for.

    int g_cItems = 10;
    int g_cyItem = 20;
    int g_cxItem = 200;
    
    BOOL
    GetItemRect(int iItem, RECT *prc)
    {
     SetRect(prc, 0, g_cyItem * iItem,
             g_cxItem, g_cyItem * (iItem + 1));
     return iItem >= 0 && iItem < g_cItems;
    }
    
    int
    ItemHitTest(int x, int y)
    {
     if (x < 0 || x > g_cxItem) return -1;
     if (y < 0 || y > g_cItems * g_cyItem) return -1;
     return y / g_cyItem;
    }
    
    void
    PaintContent(HWND hwnd, PAINTSTRUCT *pps)
    {
     COLORREF clrSave = GetBkColor(pps->hdc);
     for (int iItem = 0; iItem < g_cItems; iItem++) {
      RECT rc;
      GetItemRect(iItem, &rc);
      COLORREF clr = RGB((iItem & 1) ? 0x7F : 0,
                         (iItem & 2) ? 0x7F : 0,
                         (iItem & 4) ? 0x7F : 0);
      if (iItem & 8) clr *= 2;
      SetBkColor(pps->hdc, clr);
      ExtTextOut(pps->hdc, rc.left, rc.top,
                 ETO_OPAQUE, &rc, TEXT(""), 0, NULL);
     }
     SetBkColor(pps->hdc, clrSave);
    }
    

    We merely paint a few colored bands. To make things more interesting, you can add scroll bars. I leave you to deal with that yourself, since it would be distracting from the point here, although it would also make the sample a bit more realistic.

    Next, we create a tooltip control and instead of creating a tool for each element, we create only one. For starters, it's an empty tool with no rectangle. The g_iItemTip variable tells us which item this tooltip is standing in for at any particular moment; we use -1 as a sentinel indicating that the tooltip is not active.

    HWND g_hwndTT;
    int g_iItemTip;
    
    BOOL
    OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
    {
     g_hwndTT = CreateWindowEx(WS_EX_TRANSPARENT, TOOLTIPS_CLASS, NULL,
                               TTS_NOPREFIX,
                               0, 0, 0, 0,
                               hwnd, NULL, g_hinst, NULL);
     if (!g_hwndTT) return FALSE;
    
     g_iItemTip = -1;
     TOOLINFO ti = { sizeof(ti) };
     ti.uFlags = TTF_TRANSPARENT;
     ti.hwnd = hwnd;
     ti.uId = 0;
     ti.lpszText = TEXT("Placeholder tooltip");
     SetRectEmpty(&ti.rect);
     SendMessage(g_hwndTT, TTM_ADDTOOL, 0, (LPARAM)&ti);
     return TRUE;
    }
    

    You may have noticed that we do not use the TTF_SUBCLASS flag in our tool. We'll see why later.

    The single tool for the tooltip covers our entire client rectangle. We maintain this property as the window resizes.

    void
    OnSize(HWND hwnd, UINT state, int cx, int cy)
    {
     TOOLINFO ti = { sizeof(ti) };
     ti.hwnd = hwnd;
     ti.uId = 0;
     GetClientRect(hwnd, &ti.rect);
     SendMessage(g_hwndTT, TTM_NEWTOOLRECT, 0, (LPARAM)&ti);
    }
    

    We need to keep the g_iItemTip up to date so we know which item our tooltip is standing for at any particular moment. That is done by the UpdateTooltip function:

    void
    UpdateTooltip(int x, int y)
    {
     int iItemOld = g_iItemTip;
     g_iItemTip = ItemHitTest(x, y);
     if (iItemOld != g_iItemTip) {
       SendMessage(g_hwndTT, TTM_POP, 0, 0);
     }
    }
    

    To update the tooltip, we check whether the mouse is over the same item as it was last time. If not, then we update our "Which item is under the mouse now?" variable and pop the old bubble (if any). And we always relay the message to the tooltip so it can do its tooltip thing. This function also explains why we did not use the TTF_SUBCLASS flag when we created our tool: We need to do some processing before the tooltip. If we had allowed the tooltip to subclass, then it would process the mouse message first, which means that our TTM_POP would have popped the new updated tooltip instead of the stale old tooltip.

    This UpdateTooltip function is very important. It must be called any time the mouse may be hovering over a different item. This could be because the mouse moved or because the items under the mouse changed positions. I don't have any scrolling in this example, but if I did, then you would see a call to UpdateTooltip whenever we updated the scroll origin point because the act of scrolling may have moved the item that was under the mouse. (Failing to maintain mouse state after a scrolling operation is a common programming oversight.) Furthermore, if items were added or deleted dynamically, then a call to UpdateTooltip would have to be made once an item was added or deleted because the added or deleted item might be the one under the mouse.

    The easy one to take care of is the mouse motion:

    void
    RelayEvent(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
    {
     UpdateTooltip(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
     MSG msg;
     msg.hwnd = hwnd;
     msg.message = uiMsg;
     msg.wParam = wParam;
     msg.lParam = lParam;
     SendMessage(g_hwndTT, TTM_RELAYEVENT, 0, (LPARAM)&msg);
    }
    
    LRESULT CALLBACK
    WndProc(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
    {
     if ((uiMsg >= WM_MOUSEFIRST && uiMsg <= WM_MOUSELAST) ||
         uiMsg == WM_NCMOUSEMOVE) {
      RelayEvent(hwnd, uiMsg, wParam, lParam);
     }
    
     switch (uiMsg) {
      ... as before ...
    }
    

    If we get a mouse message, then the RelayEvent message updates our tooltip state and then relays the message to the tooltip. See the discussion above for the importance of doing this in the right order.

    You can run the program now. Observe that the program acts as if each colored band has its own tooltip, even though there is really only one tooltip that we keep recycling.

    We're still not done. The tooltip text is the same for each item, which is unrealistic for a real program. We'll address this next time.

  • The Old New Thing

    2006 mid-year link clearance

    • 8 Comments

    A few random links that I've collected.

  • The Old New Thing

    More about the house in front of Microsoft's RedWest campus

    • 8 Comments

    After I mentioned that house in front of Microsoft's RedWest campus, I received an e-mail message from Mike Daly which corrects some of my mistakes and provides additional details:

    Actually, there were two houses in the strip in front of RedWest. The one you show has not been moved. The other ended up on blocks and parked opposite the RedWest driveway while the parking garage was built. That house eventually was moved north down the street and put on a foundation (about two years ago). You can see it in the middle of this picture. The yard is in straw, and there is a new blacktop drive going down the north side into the backyard.

    After the house was put on the foundation, a sign went up to subdivide the land. As far as I can tell, the house has never been lived in (in the new location). In fact the electricity has not been connected. I've been running past those houses for years, and it has been strange to watch the one move up and down the street. Also, when the land was purchased by Microsoft, there were two old chicken coops in the middle of the property under the largest blackberry brambles you have ever seen.

    I also recommend that you go back to that earlier article, because there's some good info in the comments.

  • The Old New Thing

    New dessert lounge: Coco la ti da

    • 8 Comments

    After a Seattle Symphony concert, it has long been a tradition among my friends to walk the three blocks from Benaroya Hall, up University Street, past Luly Yang Couture to gawk at the jaw-dropping gowns in the window (the pictures on the web site fail to capture their fabulousness), arriving at the W Hotel, home of Earth & Ocean, where we would settle ourselves in for a post-concert dessert. The desserts themselves were exquisite, thanks to the inspiration of Seattle's "diva of desserts", Sue McCown.

    Or at least, that was our routine until this season.

    Sue McCown left Earth & Ocean during the summer to open her own restaurant, and when we went to Earth & Ocean after a September concert, we could tell that the magic touch was gone. We waited anxiously for Sue's new restaurant Coco la ti da to open, which it did last week. (We so adore her desserts that we feel like we're on a first-name basis with her.)

    It was the new restaurant's opening weekend and the place was so packed that a few of us had to spend some time squeezed two in one chair. Even though it's hardly walking distance from Benaroya Hall, the drive was well worth it. Here's the power Sue McCown has over us: When the waiter came to take our orders, one of my friends who has very high standards for food quality (and those who know this circle of friends will immediately recognize who it is I'm talking about) said, "I'll have one of everything on this page."

    (Now, mind you, there were seven tiny desserts on the page, and a typical order would have been for two to four of them. Therefore, ordering all seven was not too absurd, especially since they were shared with the rest of the table. But the story just sounds better when you say, "X ordered everything on page seven!")

  • The Old New Thing

    Why the Fantastic 4 Human Torch ATV is the Worst Movie Tie-In Toy Ever

    • 8 Comments

    Columnist Wm. Steven Humphrey expounds on Why the Fantastic 4 Human Torch ATV (with Light-Up Headlights!) is the Worst Movie Tie-In Toy Ever. (Readers cautioned for strong language, but it's funny because it's true.)

  • The Old New Thing

    The audacity of selling things that don't belong to you

    • 8 Comments

    NPR some time ago reported on a most audacious scam: Selling someone else's property out from under them. But this guy was small potatoes, selling properties owned by churches and other non-profit organizations. The king in this category of fraud is Victor Lustig, the man who sold the Eiffel Tower... twice!

Page 380 of 449 (4,482 items) «378379380381382»