Issue 4-42, October 20, 1999

Be Engineering Insights: Tracker Icons

By Robert Polic

Probably the easiest was to show the path that the Tracker (or BNodeInfo's GetTrackerIcon() method) uses for selecting a document's icon is through a flowchart...

           /\
          /  \
         /    \
        /      \
       /        \
      / Does the \
     /  document  \   Yes       +----------------------+
    /   contain    \----------->|  Display document's  |
    \   an icon    /            |    icon attribute    |
     \ attribute? /             +----------------------+
      \          /
       \        /
        \      /
         \    /
          \  /
           \/
           |
           | No
           |
           V
           /\
          /  \
         /    \
        /      \
       /        \
      /          \
     /   Is it    \   No
    /   a known    \---------------------------------------+
    \  mime type?  /                                       |
     \            /                                        |
      \          /                                         |
       \        /                                          |
        \      /                                           |
         \    /                                            |
          \  /                                             |
           \/                                              |
           |                                               |
           | Yes                                           |
           |                                               |
           V                                               |
           /\                          /\                  |
          /  \                        /  \                 |
         /    \                      /    \                |
        /      \                    /      \               |
       /        \                  /        \              |
      /          \                / Does the \             |
     /  Is there  \   No         / mime type  \   No       |
    /  a handler   \-----+----->/ setting file \-----------+
    \   for the    /     |      \  contain a   /           |
     \ mime type? /      |       \ meta icon  /            |
      \          /       |        \attribute?/             |
       \        /        |         \        /              |
        \      /         |          \      /               |
         \    /          |           \    /                |
          \  /           |            \  /                 |
           \/            |             \/                  |
           |             |             |                   |
           | Yes         |             | Yes               |
           |             |             |                   |
           V             |             V                   |
           /\            |   +----------------------+      |
          /  \           |   | Display mime type's  |      |
         /    \          |   | meta icon attribute  |      |
        /      \         |   +----------------------+      |
       /  Does  \        |                                 |
      / handler  \       |                                 |
     /  contain   \   No |                                 |
    / an attribute \-----+                                 |
    \ icon for the /                                       |
     \ mime type? /                                        |
      \          /                                         |
       \        /                                          |
        \      /                                           |
         \    /                                            |
          \  /                                             |
           \/                                              |
           |                                               |
           | Yes                                           |
           |                                               |
           V                                               |
+----------------------+     +----------------------+      |
|  Display handler's   |     |   Display generic    |<-----+
|    icon attribute    |     |    document icon     |
+----------------------+     +----------------------+

'Icon' in the flowchart actually refers to two icons, a mini-icon (16 x 16) and a large icon (32 x 32). Both icons are 8-bits/pixel color (B_CMAP8). A mini-icon has an attribute type of 'MICN' and a large icon has a type of 'ICON'.

For documents that contain icon attributes, the attribute names are 'BEOS:M:STD ICON' for mini-icons and 'BEOS:L:STD ICON' for large icons.

Applications that have icon attributes for documents concatinate 'BEOS:M:' (or 'BEOS:L:') with the mime type of the document for the attribute's name. For example, an image-editing application that saves to a private file format with a mime type of 'image/x-vnd.acme-bits' would contain icon attributes named 'BEOS:M:image/x-vnd.acme-bits' and 'BEOS:L:image/x-vnd.acme-bits'.

To programatically retrieve the Tracker icon for a file (also known as a node), there's a BNodeInfo method called (surprisingly) GetTrackerIcon. GetTrackerIcon takes a preconstructed bitmap of the correct size and color space and an enum of the icon size to retrieve (B_MINI_ICON or B_LARGE_ICON). Given an entry_ref, here's an example...

BNode node(ref);
if (node.InitCheck() == B_NO_ERROR) {
    BBitmap*  large_icon;
    BBitmap*  mini_icon;
    BNodeInfo info(&node);


    large_icon = new BBitmap(BRect(0, 0,
             B_LARGE_ICON - 1,
             B_LARGE_ICON - 1),
             B_CMAP8);

    if (info.GetTrackerIcon(large_icon, B_LARGE_ICON)
        != B_NO_ERROR) {
        // handle error
    }

    mini_icon = new BBitmap(BRect(0, 0,
                B_MINI_ICON - 1,
                B_MINI_ICON - 1),
                B_CMAP8);

    if (info.GetTrackerIcon(mini_icon, B_MINI_ICON)
        != B_NO_ERROR) {
        // handle error
    }
}

Setting an icon for a document is just as easy, given a bitmap of the correct size and color space...

info.SetTrackerIcon(large_icon, B_LARGE_ICON);
info.SetTrackerIcon(mini_icon, B_MINI_ICON);

And to remove an icon from a document...

info.SetTrackerIcon(NULL, B_LARGE_ICON);
info.SetTrackerIcon(NULL, B_MINI_ICON);

Here's a handy function for converting any bitmap to the correct size and color space, optionally maintaining the aspect ratio...

BBitmap* ConvertBitmap(BBitmap*  src,
     BRect  dst,
     color_space  color,
     bool    preserve_aspect_ratio)
{
  // convert a bitmap from one size/color space into a different
  // size color space (possibly maintaining the aspect ratio) by
  // using the app servers DrawBitmap


  // create a temporary bitmap and attach a view to it so we
  // can draw into it
  BBitmap*  tmp = new BBitmap(dst, color, true);
  BBitmap*  final = new BBitmap(dst, color);
  BView*  view = new BView(dst, "", B_FOLLOW_ALL, B_WILL_DRAW);


  tmp->AddChild(view);
  tmp->Lock();
  view->SetHighColor(B_TRANSPARENT_32_BIT);
  view->FillRect(dst);


  if (preserve_aspect_ratio)
  {
      float  f;
      BRect  bounds(src->Bounds());


      if (bounds.Width() > bounds.Height())
      {
        // leave width, adjust height to maintain aspect ratio
        f = bounds.Height() / bounds.Width() * dst.Height();
        dst.top = dst.top + ((dst.Height() - f) / 2);
        dst.bottom = dst.top + f;
      } else {
        // leave height, adjust width to maintain aspect ratio
        f = bounds.Width() / bounds.Height() * dst.Width();
        dst.left = dst.left + ((dst.Width() - f) / 2);
        dst.right = dst.left + f;
      }
  }

  view->DrawBitmap(src, src->Bounds(), dst);

  // we can draw do additional drawing at this point such as
  // placing a frame around the image or a 'bug' in the corner
  view->Sync();
  tmp->Unlock();

  // copy bits from temporary bitmap into final bitmap
  memcpy(final->Bits(), tmp->Bits(), tmp->BitsLength());

  // delete the temporary bitmap (deletes the attached view as well)
  delete tmp;
  return final;
}

Follow the ftp link below to grab some sample source that uses the above to create thumbnails for images and videos.

<ftp://ftp.be.com/pub/samples/graphics/Thumbnailer.zip


Be Engineering Insights: Inside FtpIt

By Trevor Smith

One of the powerful tools Tracker provides is the API for specific add-ons that only a small number of users may need. In a nonextensible windowing system, only general purpose features are available. For example, in browser development I often need to quickly and repeatedly FTP a file to a remote server, which is too simple a task to merit a full scale FTP client and would needlessly take up space on the majority of user's hard drives.

Enter a new Tracker add-on, FtpIt. Extending Howard's FtpClient object to include more messaging for state and status, FtpIt allows context menu choosing of files and folders for quick and dirty uploading. (See "Be Engineering Insights: Duty Now for the Future" for Howard's FTP client code.) While there are many possibilities for improvement in FtpIt (including secure display and saving of passwords, mulitple servers, alternate port numbers, and storage of specific servers for files or folders), this is a simplified example which should allow for easy extension.

You can get to the FtpIt source code here: <ftp://ftp.be.com/pub/samples/add-ons/FtpIt.zip>. Let's dive in!

When Tracker receives the message to instantiate the add-on, it spawns a thread which calls the process_refs() function. In FtpIt, this function creates the FIWindow and then waits for the window to quit. It's important that process_refs() not return before everything has been completed and deleted, because without that context any threads you have running will be stranded and Tracker will probably crash.

During the construction of the window, there are four main actions: setting the working directory, creating the list of entries to FTP, reading the settings file, and adding the ServerView that will contain the UI. If passed an entry ref to a folder, FtpIt will traverse the folder and its children, adding all the paths to mPathList and calculating the total size of the transfer. This can take some time for large numbers of files, which delays the window's Show() call. Future versions of FtpIt could split this process out into a seperate thread with the ServerView UI indicating what is happening, but for simplicity's sake we do it in the constructor.

The settings file (currently ~/config/settings/FtpIt/settings) is used to store the FTP server information. Presently, the information stored is the hostname, the path, the username, the password, and whether the connection should be passive or not. As with many BeOS application, FtpIt stores its settings in attributes on a settings file. The FIWindow's ReadSettings and WriteSettings take care of the details of finding the file and manipulating the attributes.

Now that the file list and settings are initialized, the ServerView is created and displayed. From that point on, FtpIt is driven by user events such as button or window messages. I don't have enough room here to go through all the UI code, but the widgets used are basic. One point of interest is the BNodeInfo::GetTrackerIcon call which is used on the first entry_ref in mPathList to grab an icon for the ServerView.

The FtpClient object is essentially the same as Howard's previous newsletter article, with the addition of messaging of state. For example, when the client completes a Send call, it relays that information to the ServerView in the form of a BMessage. ServerView catches this in its MessageReceived and updates the status bar. Success or failure messages are sent in a similar manner for the connect, get, and put calls.


Developers' Workshop: TranslatorPanel: The Revenge

By Daniel Switkin

Welcome back to our two-part look at the Translation Kit. I'm going to hold off on Part II as planned due to...

<humble apology>

a small bug in the sample code from my last newsletter. Interestingly, it only shows up with certain plugins. Curiously, even the ones that work actually have the wrong behavior. So let's get to the bottom of this.

<bug description>

The issue here is how you request a format from a given translator. The correct way is to supply the 4-byte constant to the Translate() function. The bug involves using zero and assuming a certain behavior. For the record, passing zero to a translator means "Translate to the native BeOS format for that type."

Some of our translators took zero to mean "Translate to the opposite format of the input data," which happened to work with my code. Others tried to save as TranslatorBitmaps, and then unfortunately had a bug in their copy routines which resulted in only 32 bytes being written to disk.

</bug description>

<resolution>

Now the translators in the next release behave correctly when zero is passed in, and have working copy routines when the input and ouput data are in the same format. As a general rule, don't use zero—there are sure to be third-party add-ons that have undefined bahavior, too.

As for TranslatorPanel, it now queries on a translator id to determine which constant to use. The changes to the code are minimal.

</resolution>

</humble apology>

<begging for forgiveness>

To make up for this little slip, I've added some new features to TranslatorPanel, which you can find in the same spot as last time:

<ftp://ftp.be.com/pub/samples/translation_kit/TranslatorPanel.zip>

The primary additions are a C++ plugin architecture and live drag and drop to and from the Tracker.

</begging for forgiveness>

<plugins>

Let's start with the plugins. I've whipped together a simple interface to run filters on the current image, and three sample filters in the add-ons directory. There is currently no way to configure the effect—it's just a "do it" kind of add-on. For a tutorial on writing add-ons, check out the excellent description in the Be Book under Images in the Kernel Kit.

The interface is very simple—every add-on subclasses ImageFilter and provides a C hook to instantiate the class. It then sets up its name and id in the constructor, and implements the Run() function to do its work. The add-on makefile sets the BUILDING_ADDON flag to export its symbols; the BeIDE project and makefile for the app do not, and therefore import the symbols in ImageFilter.h.

The rest is pretty simple. TranslatorPanel builds a Filters Menu when the program is launched by looking in its add-ons directory and trying to find the instantiate() hook in each file there. Those that load successfully are inserted into a linked list, and the menu items are all given the same what field. To distinguish between the messages, a "filter_id" field is added using ImageFilter::GetId(). When a filter message is received, TestView iterates through the linked list and tries to find a match.

</plugins>

<drag and drop>

The previous version of TranslatorPanel let you drop images into the app, but not drag them out. It also didn't give you any feedback. In this update, when you drag a file over the app, it scans the mime type of the file. If the supertype is "image," a blue border is drawn in the window to indicate that the file type is understood.

Dragging the current image out to the Tracker is now also possible. A semitransparent version of the image is built and a three-step messaging protocol is negotiated. The app writes the current image wherever the Tracker reports the drop landed. For now, the image is hardcoded in Targa format.

The real trick here is how the drag starts. I wanted to discard casual mouse clicks in the window, and only begin the drag session if the user holds the button down for a certain duration. Rather than sleep in TestView::MouseDown() and tie up the window's thread, I set a boolean to indicate that a drag may be starting, and then used a one time BMessageRunner to resume the drag initialization after the period of time. If the user releases the mouse button before the message arrives, TestView::MouseUp() will have already set the boolean to false. If not, the bitmap is built and passed to DragMessage().

</drag and drop>


The Agenda Schmoozefest

By Jean-Louis Gassée

Every year for the past 12, I've attended an industry conference called Agenda. The venue since 1991 has been the Phoenician, in Phoenix, Arizona. This is a very nice resort, built partly with taxpayer money -- thanks to the federally subsidized bailout of failed S&L (Savings and Loans, not Synergy and Leverage) real estate investments. The S&L can take pride in its free-market independence of government handouts.

By now, the real benefits of such conferences are well established. These gatherings are a benign, nerdy version of the Bohemian Grove. We go there to meet the brotherhood, people like us, more than for the contents of the sessions. Schmoozing in the hallways counts for more than the boiler plate of prepared speeches.

Of course, there are exceptions—occasionally, we see people who are not like us. And there's even the odd bit of real content in some of the artfully spun company manifestos—some of it voluntary. Among those present who were not like us was Rupert Murdoch, the media giant. He read a long speech about the future of his and our businesses combined. At least I think that what it was about. I asked around, and my brothers and sisters were not sure either. There was a brief, precious moment during the perfunctory question and answer period that followed the speech. Eric Raymond, the well-known, well-quoted Open Source advocate, stood up and asked our guest what he thought of the Open Source movement. I hope a tape exists of the look on Murdoch's face and of his brave recovery to make a heartfelt statement of his commitment to openness.

George W. Bush was another guest who was not like us, but the point of his before-dinner speech was easier to grasp. You are the American Dream incarnate. As your President, I'll make sure that more people believe in and have access to the American Dream. He even proposed some means to that end, ranging from tax cuts, to better legislation for technology, to more choices and accountability for education. The flattery worked, assisted by George W's warm, affable demeanour.

Al Gore was scheduled to give a talk and take questions by telephone later in the conference. We'll see how his brand of flattery works and how he fared with a crowd that was less than pleased with his gauche attempt to take some credit for the Internet, a point George W. gently rubbed in, without mentioning the claimant by name or title.

But there was more than the politics of politics—there was also computer industry politicking. Unfortunately, I have no revelations to bring you from that aspect of the gathering either. Exploiting the publicity around the "End of Moore's Law" question, Intel claimed that, yes, we were approaching an inflexion point, not right this minute, but maybe in a decade or so. Meanwhile, they would inflect their strategy, moving from making the building blocks of personal computing to building the key ingredients of e-business. How they will exert the kind of control on e-business that they have on the PC business was not explained.

Microsoft, no less predictably, claimed that the PC was approaching an inflexion point, a popular term this year, concurrent with the rise of Internet appliances. Microsoft added a little twist, saying that the inflexion point for PCs meant they would grow more rapidly in the future. And that there wouldn't be a shift from PCs to appliances, but rather more of both—more PCs and more appliances, from GSM telephones to Web TVs. In prior years such prophecies—about NetPCs, Zero Administration, pick a subject—drew retorts, attacks, controversy. This year, no one bothered. I find that reassuring.

Creative Commons License
Legal Notice
This work is licensed under a Creative Commons Attribution-Non commercial-No Derivative Works 3.0 License.