If your question isn't answered here, you might also like to read the Plugin Development Wiki for TimeSnapper. For more in-depth help developing plugins, don't hesitate to contact us.
A plugin is a way of extending and customizing the way TimeSnapper behaves.
At the code level, it is a .net class that implements an interface (ITimeSnapperPlugin) that TimeSnapper will seek out talk to.
At the file level, a TimeSnapper plugin is a dll that ends with the word "Plugin" -- for example "SamplePlugin.dll". It sits in a subfolder called "Plugins" located under the TimeSnapper folder. For example "C:\Program Files\TimeSnapper\Plugins".
A plugin can subscribe to TimeSnapper events, and it can add items to menus inside TimeSnapper.
[ Back to top ]
A plugin can choose to be notified when any of these things happen:
[ Back to top ]
In addition to subscribing to events, a plugin can tell TimeSnapper to put extra menu items into some of the context menus in the TimeSnapper application.
In particular a plugin can add menu items to these locations:
[ Back to top ]
Just about anything you like, of course.
The standard answer we've given to tricky questions for the last year has always been "If you want to do that, you'll need to write a plugin".
A sample that ships with the latest version of TimeSnapper is called 'the Animated GIF' plugin. It lets you export a series of images as an animated gif.
Achieving this inside a plugin was quite trivial (we did it by following Jon Galloway's lead when he built the same plugin feature for Cropper. We tinkered with the NGif project at codeproject, and then leveraged public domain portions of the animated gif plugin that Jon wrote.)
Here are more ideas for plugins:
Connect to TFS, get outstanding tasks and assign time against them. Or -- connect to TFS, read what work items a user completed, and drop them into the timeline as flags.
Run over older images, resize them and/or convert them to lower resolution. Record current application activity in outlook Journal.
Integrate with a user's Outlook Calendar and note in the timeline that the user was in a meeting from 2-3pm instead of showing a gap.
[ Back to top ]
Create a new project in visual studio, of type "Class Library". (In other words, we will be creating a DLL.)
Add a reference to "ITimeSnapperPlugin.dll"
Give a suitable name to your class, and then implement "ITimeSnapperPlugin"
using System; using TimeSnapperPluginAPI;namespace CSharpSamplePlugin { class AnotherPluginSample : ITimeSnapperPlugin {
Create a new guid. We'll use this to make sure our plugin doesn't clash with any other plugins.
Return the guid you've created, from the 'PluginID' property getter.
Return the name of your plugin from the "Friendly Name" property getter:
string ITimeSnapperPlugin.FriendlyName { get { return "My Sample Plugin"; } }
Next, you should return a paragraph describing your plugin from the "Description" property getter.
string ITimeSnapperPlugin.Description { get { return "This plugin demonstrates some of the ways that plugins work."; } }
The two properties above are used by TimeSnapper for providing information about the plugin, inside the 'Options' dialog:
[ Back to top ]
When TimeSnapper first loads up our plugin, it will ask it which events it wants to subscribe to.
It asks this question by calling the "SubscribesTo
" method.
From "Subscribes to" you return an array of enums, chosen from the enumeration "TimeSnapperEvent
"
So, if you want your plugin to subscribe to the "Flag Saved" event and the "Snapshot Saved" event, you would write this code:
TimeSnapperEvent[] ITimeSnapperPlugin.SubscribesTo() { return new TimeSnapperEvent[] { TimeSnapperEvent.FlagSaved, TimeSnapperEvent.SnapshotSaved }; }
[ Back to top ]
Similarly, when TimeSnapper loads up our plugin, it asks if there are any menu items it wants to add to any menus.
It asks this question by calling the "MenuItems
" method, from which the plugin can return an array of TimeSnapperMenuItem classes.
[ Back to top ]
When the event itself occurs, you are notified in a method called "HandleEvent".
In 'HandleEvent
' you write a switch
statement, (a Select case
, in Visual Basic speak), with one case for each event you want to handle.
Select Case TimeSnapperEvent Case TimeSnapperEvent.SnapshotSaved MessageBox.Show("A snap shot saved!")
Here's one for our example:
object ITimeSnapperPlugin.HandleEvent (TimeSnapperEvent TimeSnapperEvent, EventArgs args) { switch (TimeSnapperEvent) { case TimeSnapperEvent.FlagSaved : Debug.WriteLine("A flag was saved"); break; case TimeSnapperEvent.SnapshotSaved : Debug.WriteLine("A snapshot was saved"); break; default: Debug.Assert(false, "Hey! I didn't subscribe to " + TimeSnapperEvent.ToString() + "... so this default case won't occur"); break; } return null; }
[ Back to top ]
Each plugin has a readonly Boolean property named "Configurable
."
Most simple plugins do not need any configuration by the end user, so their authors would simply return 'false' from this property, like so:
bool ITimeSnapperPlugin.Configurable { get { return false; } }
But if your Plugin is a little more complex, and requires the end user to configure it, you will want to return "true" from this property.
What exactly does that do? It means that the "Configure" button, in the "Plugin" tab of the Options dialog will be enabled, when your plugin is selected.
If "Configurable" returns true, then the button will be enabled. When the user presses the button, TimeSnapper will call the "Configure" method on your plugin.
It's up to you, as the plugin author, to decide what your plugin will do when the Configure method is called. You could show a modal windows dialog, you could launch a url inside a browser, you could show a WPF form -- anything you like.
It's also up to you how you will persist the user's choices across sessions (if required). You might for example, write an xml file into the user's app settings folder, or record settings in the registry.
[ Back to top ]
The full list of events you can subscribe to is provided in the enumeration TimeSnapperPluginAPI.TimeSnapperEvent and it looks like this:
public enum TimeSnapperEvent { SnapshotTakingCancel = 0, SnapshotTakenSavingCancel = 1, SnapshotSaved = 2, AutoPoppingUpCancel = 3, FlagSavingCancel = 4, FlagSaved = 5, PluginsLoaded = 6, Closing = 7, ArchivingCancel = 8, Archived = 9, SnapshotDeletingCancel = 10, SnapshotDeleted = 11, }
Some events have the word 'Cancel' on the end of their name, because they are events that give you the opportunity to Cancel an activity from occuring.
For example, if you subscribe to the SnapshotTakenSavingCancel
event, you will be notified whenever a snapshot has been taken and it is about to be saved. You will have the opportunity to cancel the saving of the snapshot.
All events pass an 'eventArgs' parameter -- whose base class is the familiar .net class "System.EventArg".
The events whose name ends with cancel, pass through a subclass of this, using the familiar "System.ComponentModel.CancelEventArgs" derivation.
You cancel an event by setting the Cancel property of the event args to 'True'.
If you cancel an event, then no other plugin will see that event, and the action won't go ahead. (The saving won't occur, in this example.) You can think about that from another point of view as well: any other plugin may cancel an event before it gets to you.
Here is how you cancel an event...
Make sure the event is one of the ones that end in 'Cancel'. Cast it to a System.ComponentModel.CancelEventArgs
, and set it's cancel property to true.
object ITimeSnapperPlugin.HandleEvent (TimeSnapperEvent TimeSnapperEvent, EventArgs args) { switch (TimeSnapperEvent) { case TimeSnapperEvent.AutoPoppingUpCancel: //No! we won't let the auto popup occurr... ((System.ComponentModel.CancelEventArgs)EventArgs).Cancel = true; break;
Here's a similar example in VB.net, where we stop a snap shot from being taken...
Public Function HandleEvent(ByVal TimeSnapperEvent As TimeSnapperEvent, _ ByVal args As EventArgs) _ As Object Implements ITimeSnapperPlugin.HandleEvent Select Case TimeSnapperEvent Case TimeSnapperEvent.SnapshotTakingCancel 'You plan to take a snapshot hey. Let me cancel that! Dim realargs As System.ComponentModel.CancelEventArgs realargs = CType(args, System.ComponentModel.CancelEventArgs) realargs.Cancel = True 'Stop it from happening!
[ Back to top ]
Here is a very simple example of a plugin, written in C#
Sample Plugin -- C# (zip)
[ Back to top ]
Here is a very simple example of a plugin, written in Visual Basic .net
Sample Plugin -- Visual Basic.net (zip)
[ Back to top ]
Sorry, example are not currently provided in languages other than C# and VB.net. Why not write one and share it with the world?
TimeSnapper looks for plugins in a subfolder called "Plugins" located under the TimeSnapper application folder.
For example "C:\Program Files\TimeSnapper\Plugins".
Here's the trickiest part... TimeSnapper only looks inside files whose name ends with "Plugin.dll".
So for example, "SamplePlugin.dll" would be inspected for ITimeSnapeprPlugin's -- but PluginSample.dll would NOT be inspected and loaded.
Just repeating that one more time: your DLL's name must end with "Plugin.dll" or it will not be loaded.
(Why is this done? This is so that if a plugin references a lot of supporting dlls, they can live in that same folder, without slowing down the startup time for TimeSnapper.)
[ Back to top ]
If errors occur during loading or activating a plugin, details are written to the TimeSnapper log file.
Also, you can emit debug statements from your plugin, and then look at them using a debug listener, such as the one provided by sysinternals.
[ Back to top ]
You are most welcome to share your plugins!
We intend to provide a gallery of plugins from on TimeSnapper.com, to encourage their usage, and I would like to further promote them on my blog at secretGeek.net.
It is still very early days, but we are hoping for some activity here.
[ Back to top ]
You certainly may.
You are welcome to sell or to give away your plugins. You can include your own licensing registration mechanism within your plugin if you wish. We don't ask for a share of any royalties from your plugin, if it does make money. The only caveat we have is that we insist you don't use your plugins to circumvent the goals of TimeSnapper, or to damage our existing licensing and protection.
[ Back to top ]
Currently, the only plugins available are the Animated Gif plugin and the sample plugins. We hope to have more available soon. (Written March 2009)