views:

585

answers:

9

Our application has a file format similar to the OpenDocument file format (see http://en.wikipedia.org/wiki/OpenDocument) - i.e. zipped with a manifest file, a thumbnail image, etc.

I notice that OpenOffice files have a preview image of the Open Office file as their icons, both in Windows and in Linux. Is there some way to accomplish this for our files: i.e. I want a dynamic icon based on the internal thumbnail.png?

Edit 1 Wow, thanks for all the quick answers. Thumbnailer looks great for the GNOME world. Windows I'll be looking into those links, thanks. As for the comment question: programmatically OR via our installer.

Edit 2 Oh, forgot Mac. How about on the Mac? (Sorry Mac lovers!) Also are there any links or info for how OpenOffice does their IconHandler stuff - since ours would be very similar?

A: 

This is up to the operating system as far as I know, it will be based on the file extension.

Paul Whelan
+1  A: 

For Gnome you use a thumbnailer.

David Dorward
A: 

for WINDOWS try this:

http://www.easydesksoftware.com/news/news12.htm

Tom
A: 

Executables have the icon inside the file (potentially multiple) as a "resource".

Data files pick up an icon based on file association.

If you want a custom icon per file that is much harder. you either need too fool the OS into thinking it is an executable and embed the icon as a resource in the file, or deep link into the OS to override the default icon selection routine.

pappes
A: 

I think, "custom own" icon can have only PE files in windows. Every other icons for file extensions are stored in windows registry.

For specification of PE file, you can look at An In-Depth Look into the Win32 Portable Executable File Format and Peering Inside the PE: A Tour of the Win32 Portable Executable File Format.

How it works in other OS, I don't know :/.

TcKs
A: 

I don't know about Linux, but for Windows you can start here: http://msdn.microsoft.com/en-us/library/bb774614.aspx

Edit: I think this interface is for the thumbnails shown in thumbnail view, not icons. Sorry for wasting your time.

Mark Ransom
+2  A: 

In Windows, what you need is to implement an Icon Handler. I did this many moons ago and it is not difficult as long as you know the basics of COM.

See: http://msdn.microsoft.com/en-us/library/bb776857(VS.85).aspx

Fake Jim
+4  A: 
Adam Davis
..and that's why you've got 4k rep and I've got 81! Great depth there. Good answer!
Fake Jim
Nice stuff, Adam. Thanks so much. Add a link to the Gnome thumbnailer and I'll accept this answer.
Epaga
+1  A: 

Mac OSX since version 10.5 …

… has two approaches:

  1. Your document is in the standard OSX bundle format and has a static image This can be done by creating a subfolder QuickLook and placing the Thumbnail/Preview.png/tiff/jpg inside.

  2. Everything else needs a QuickLook generator plugin which can be stored in either /Library/QuickLook ~/Library/QuickLook or inside the YourApp.app/Contents/Library/QuickLook Folders.

This generator is being used to create Thumbnails and QuickLook previews on the fly. XCode offers a template for this. The template generates the needed ANSI C files which have to be implemented. If you want to write Object-C code you have to rename the GenerateThumbnailForURL.c and GeneratePreviewForURL.c to GenerateThumbnailForURL.m and GeneratePreviewForURL.m (and read the Apple Devel Docs carefully ;) )


Simple zip container based demo:

You will have to add the Cocoa.framework and Foundation.framework to your project In your GenerateThumbnailForURL.c (this is partly out of my head - so no guarantee that it works out of the box ;) ):

#include <Cocoa/Cocoa.h>
#include <Foundation/Foundation.h>

OSStatus GenerateThumbnailForURL(void *thisInterface, QLThumbnailRequestRef thumbnail, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options, CGSize maxSize)
{
  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

  /* unzip the thumbnail and put it into an NSData object */
  // Create temporary path and writing handle for extraction
  NSString *tmpPath = [NSTemporaryDirectory() stringByAppendingFormat: [NSString stringWithFormat: @"%.0f.%@" , [NSDate timeIntervalSinceReferenceDate] * 1000.0, @"png"]];
  [[NSFileManager defaultManager] createFileAtPath: tmpPath contents: [NSData alloc] attributes:nil];
  NSFileHandle *writingHandle = [NSFileHandle fileHandleForWritingAtPath: tmpPath];


  // Use task to unzip - create command: /usr/bin/unzip -p <pathToFile> <fileToExtract>
  NSTask *unzipTask = [[NSTask alloc] init];
  [unzipTask setLaunchPath: @"/usr/bin/unzip"];

  // -p -> output to StandardOut, added File to extract, nil to terminate Array
  [unzipTask setArguments: [NSArray arrayWithObjects: @"-p", [(NSURL *) url path], @"Thumbnails/thumbnail.png", nil]];

  // redirect standardOut to writingHandle
  [unzipTask setStandardOutput: writingHandle];

  // Unzip - run task
  [unzipTask launch];
  [unzipTask waitUntilExit];

  // Read Image Data and remove File
  NSData *thumbnailData = [NSData dataWithContentsOfFile: tmpPath];
  [[NSFileManager defaultManager] removeFileAtPath: tmpPath handler:nil];


  if ( thumbnailData == nil || [thumbnailData length] == 0 ) {
     // Nothing Found. Don't care.
     [pool release];
     return noErr;
  }

  // That is the Size our image should have - create a dictionary too
  CGSize size = CGSizeMake(256, 256);
  NSDictionary *properties = [NSDictionary dictionaryWithObjectsAndKeys:
      [NSNumber numberWithInt:size.width],kQLPreviewPropertyWidthKey,
      [NSNumber numberWithInt:size.height],kQLPreviewPropertyHeightKey,
      nil];

  // Get CGContext for Thumbnail
  CGContextRef CGContext = QLThumbnailRequestCreateContext(thumbnail, size, TRUE, (CFDictionaryRef)properties);
  if(CGContext) {
     NSGraphicsContext* context = [NSGraphicsContext graphicsContextWithGraphicsPort:(void *)CGContext flipped:size.width > size.height];
     if(context) {
        //These two lines of code are just good safe programming…
       [NSGraphicsContext saveGraphicsState];
       [NSGraphicsContext setCurrentContext:context];

       NSBitmapImageRep *thumbnailBitmap = [NSBitmapImageRep imageRepWithData:thumbnailData];
       [thumbnailBitmap draw];

       //This line sets the context back to what it was when we're done
       [NSGraphicsContext restoreGraphicsState];
    }

    // When we are done with our drawing code QLThumbnailRequestFlushContext() is called to flush the context
    QLThumbnailRequestFlushContext(thumbnail, CGContext);

    // Release the CGContext
    CFRelease(CGContext);
  }

  [pool release];
  return noErr;
}


Info.plist

You will have to modify your info.plist file too - when you open it up it has a lot of fields pre-set. Most of them are self-explaning (or will not have to be changed) but I had to add the following structure (copy paste should do - copy the text, go into the plist editor and just paste.):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&gt;
<plist version="1.0">
<array>
   <dict>
     <key>UTTypeConformsTo</key>
     <array>
       <string>com.pkware.zip-archive</string>
     </array>
     <key>UTTypeDescription</key>
     <string>i-net Crystal-Clear Report File</string>
     <key>UTTypeIconName</key>
     <string>generic</string>
     <key>UTTypeIdentifier</key>
     <string>com.company.product</string>
     <key>UTTypeReferenceURL</key>
     <string>http://your-url.com&lt;/string&gt;
     <key>UTTypeTagSpecification</key>
     <dict>
       <key>public.filename-extension</key>
         <array>
           <string>$fileEXT$</string>
         </array>
     </dict>
  </dict>
</array>
</plist>

This will register your filetype $fileExt$ and tell the system that your filetype is a zipy format type. A nice refference, that I used here is the QuickLook IPA Plugin from googlecode

gamma