Lacurne - A PDF wallpaper displaying the last, current & next month

Last Monday a good friend who currently works abroad for an ad agency in New York city phoned me to ask if I could kindly write her a little AppleScript that creates and sets a wallpaper displaying the calendar of the last, current and next month on her desktop.

And as I still owed her a favour I quickly wrote a small script that did the trick. She loved it. But then her colleagues saw it and immediately flooded my inbox with new feature requets. Custom font and background color/image, positioning of the calendar and highlighting of the last and next month.

Well, I actually planned to spent my evenings reading a new and thrilling book (The Redeemer from Jo Nesbø), but then I found myself on the balcony writing a more elegant version of the initial script. And of course I am sharing my work with you and invite you to download and inspect the AppleScript below:

Lacurne - A customizable PDF wallpaper displaying the last, current & next month on your desktop (v0.6, ca. 153 KB)

The script was successfully tested on Intel and PowerPC based Macs running Mac OS X 10.5 Leopard.
Please note, that Lacurne requires Mac OS X 10.5 and does not support dual monitor setups.

To create and set a PDF wallpaper just open the script with a double click. If you want to open the configuration menu, then just drag and drop a Finder item onto the script’s icon:

In my opinion, Lacurne is yet another example for the great possibilities offered by the rich scripting environment in Mac OS X:

The calendar data is produced by the «cal» command, the PDF wallpaper is instantly(!) generated using Python and the desktop picture is finally updated with the Finder’s AppleScript dictionary. And all commands are glued together with our favourite language of automation, AppleScript, which also offers the basic GUI to create a convenient interface for the user. I just love this digital marble run!

All parts of the script can be viewed in Script Editor or a free text editor (Python script)!

Happy scripting!

Important: Opening and saving the below script code in Script Editor won’t result in a usable AppleScript! That is because Lacurne internally relies on several scripts, which are located inside its Script bundle. Therefor please download the complete script here.


-- created: 25.06.2008
-- modified: 05.07.2008
-- version: 0.6
-- tested on/with:
-- ¢ Intel- & PowerPC based Macs on Mac OS X 10.5.3 Leopard
-- requires:
-- ¢ Mac OS X 10.5 Leopard
-- note:
-- ¢ this does not work for dual monitor setups
-- history:
-- ¢ version 0.6
-- - added support for background images
-- ¢ version 0.5
-- - first public release

-- This AppleScript creates and sets a PDF wallpaper which displays a calendar for the last,
-- current and next month on your desktop.
-- It allows to adjust many of its settings, just drag a Finder item onto it to open the 
-- configuration menu.

-- Lacurne? «La»st, «cur»rent and «ne»xt month!
property myname : "Lacurne"

-- I am called when the user drops Finder items onto the script's icon
-- I am used to display the configuration menu where the user can
-- easily customize the small desktop calendar
on open dropitems
	-- the script responsible for displaying the configuration menu is saved
	-- in a separate script file which must be loaded first
	try
		set cfscriptpath to (((path to me) as Unicode text) & "Contents:Resources:Scripts:config.scpt")
		set config to load script (cfscriptpath as alias)
		-- we need to pass our application path to the script object, so that
		-- it can easily locate the preferences script and the PDF help document
		-- inside the application bundle
		config's init((path to me) as Unicode text)
		config's start()
	on error errmsg number errnum
		my dsperrmsg(errmsg, errnum)
	end try
end open

-- I am called when the user opens the script with a double click
on run
	try
		-- loading the preferences
		set prefs to my loadprefs()
		if prefs is missing value then
			set errmsg to "Could not load the preferences from inside my application bundle (prefs.scpt)."
			my dsperrmsg(errmsg, "--")
			return
		end if
		-- path to the script's application support folder
		set asfolderpath to (((path to application support folder from user domain) as Unicode text) & myname & ":")
		-- creating the script's application support folder if it does not already exists
		try
			set asfolderalias to asfolderpath as alias
		on error
			set command to "mkdir -p " & quoted form of POSIX path of asfolderpath
			do shell script command
		end try
		-- file path to the PDF wallpaper to be produced
		set wpfilepath to asfolderpath & myname & ".pdf"
		-- trying to delete an old PDF wallpaper file
		try
			do shell script "rm " & quoted form of (POSIX path of wpfilepath)
		end try
		-- getting the display resolution of the main monitor
		set {dspwidth, dspheight} to my getdisplayresolution()
		-- getting the current month and year as string values
		set curdate to current date
		set strmonth to ((month of curdate) as integer) as Unicode text
		set stryear to ((year of curdate) as integer) as Unicode text
		-- getting user chosen values from the preferences
		-- we need to replace the comma with a point in some values,
		-- as Python only accepts a point as the decimal separator, not a comma
		set fontname to prefs's fontname
		set fontsize to my searchnreplace(",", ".", (prefs's fontsize as Unicode text))
		-- x, y position of the calendar on the desktop
		set xpos to my searchnreplace(",", ".", (prefs's xpos as Unicode text))
		set ypos to my searchnreplace(",", ".", (prefs's ypos as Unicode text))
		-- creating the background color string, e.g. "0.9, 1.0, 0.8"
		set {bgredval, bggreenval, bgblueval} to prefs's bgcolor
		set bgredval to my searchnreplace(",", ".", (bgredval as Unicode text))
		set bggreenval to my searchnreplace(",", ".", (bggreenval as Unicode text))
		set bgblueval to my searchnreplace(",", ".", (bgblueval as Unicode text))
		set bgcolor to bgredval & space & bggreenval & space & bgblueval
		-- creating the font color string, e.g. "0.9, 1.0, 0.8"
		set {ftredval, ftgreenval, ftblueval} to prefs's fontcolor
		set ftredval to my searchnreplace(",", ".", (ftredval as Unicode text))
		set ftgreenval to my searchnreplace(",", ".", (ftgreenval as Unicode text))
		set ftblueval to my searchnreplace(",", ".", (ftblueval as Unicode text))
		set fontcolor to ftredval & space & ftgreenval & space & ftblueval
		-- tint value to highlight the last and next month
		set fonttint to my searchnreplace(",", ".", (prefs's fonttint as Unicode text))
		set bgimgpath to prefs's bgimgpath
		-- in case the background image file is missing we display an error message 
		if bgimgpath is not missing value then
			if not itempathexists(bgimgpath) then
				set errmsg to "We could not locate the background image file supposed to be at:" & return & return & bgimgpath
				my dsperrmsg(errmsg, "--")
				return
			end if
		end if
		-- finally creating the PDF wallpaper displaying last, current and next month
		my crtwallpaper(dspwidth, dspheight, stryear, strmonth, fontname, fontsize, xpos, ypos, bgcolor, fontcolor, fonttint, bgimgpath, wpfilepath)
		-- if the creation of the desktop wallpaper failed we display an error message
		if not itempathexists(wpfilepath) then
			set errmsg to "The creation of the desktop wallpaper simply failed. And I don't know exactly why."
			my dsperrmsg(errmsg, "--")
			return
		end if
		-- file path to the PNG image used as an update helper: if you try to update the desktop picture and
		-- the file path of your new image is the same as the current one, you won't see
		-- the new image. therefor we first «switch» to the small PNG image before updating
		-- the wallpaper. 
		set switchpicpath to (((path to me) as Unicode text) & "Contents:Resources:switch.png")
		tell application "Finder"
			set desktop picture to (switchpicpath as alias)
		end tell
		tell application "Finder"
			set desktop picture to (wpfilepath as alias)
		end tell
	on error errmsg number errnum
		my dsperrmsg(errmsg, errnum)
	end try
end run

-- I am loading the preferences script object and return it. 
-- If it cannot be loaded I return «missing value» instead.
on loadprefs()
	try
		set pfscriptpath to (((path to me) as Unicode text) & "Contents:Resources:Scripts:prefs.scpt")
		set prefs to load script (pfscriptpath as alias)
		return prefs
	on error
		return missing value
	end try
end loadprefs

-- I am returning the current display resolution of the main monitor
-- e.g. {800, 600}
on getdisplayresolution()
	set shelloutput to do shell script "/usr/sbin/system_profiler SPDisplaysDataType | grep Resolution"
	set {dspwidth, dspheight} to {(word 2 of shelloutput) div 1, (word 4 of shelloutput) div 1}
	return {dspwidth, dspheight}
end getdisplayresolution

-- I am creating the PDF wallpaper by passing a big bunch of arguments over to a clever Python script
-- utilizing the CoreGraphics module available in Mac OS X :D
on crtwallpaper(dspwidth, dspheight, stryear, strmonth, fontname, fontsize, xpos, ypos, bgcolor, fontcolor, fonttint, bgimgpath, wpfilepath)
	set scriptpath to (((path to me) as Unicode text) & "Contents:Resources:Scripts:lacurne.py")
	set qtdscriptpath to quoted form of (POSIX path of scriptpath)
	set qtdwpfilepath to quoted form of (POSIX path of wpfilepath)
	set qtdfontname to quoted form of fontname
	if bgimgpath is missing value then
		set qtdbgimgpath to quoted form of "missing value"
	else
		set qtdbgimgpath to quoted form of (POSIX path of bgimgpath)
	end if
	set cmd to "/usr/bin/python " & qtdscriptpath & space & dspwidth & space & dspheight & space & stryear & space & strmonth & space & qtdfontname & space & fontsize & space & xpos & space & ypos & space & bgcolor & space & fontcolor & space & fonttint & space & qtdbgimgpath & space & qtdwpfilepath
	do shell script cmd
end crtwallpaper

-- I am indicating if an item path exists or not
on itempathexists(itempath)
	try
		set itemalias to itempath as alias
		return true
	on error
		return false
	end try
end itempathexists

-- I am a very old search & replace function...
on searchnreplace(searchstr, replacestr, txt)
	considering case, diacriticals and punctuation
		if txt contains searchstr then
			set olddelims to AppleScript's text item delimiters
			set AppleScript's text item delimiters to {searchstr}
			set txtitems to text items of txt
			set AppleScript's text item delimiters to {replacestr}
			set txt to txtitems as Unicode text
			set AppleScript's text item delimiters to olddelims
		end if
	end considering
	return txt
end searchnreplace

-- I am displaying error messages to the user
on dsperrmsg(errmsg, errnum)
	tell me
		activate
		display dialog "Sorry, an error occured:" & return & return & errmsg & return & " (" & errnum & ")" buttons {"OK"} default button 1 with icon stop with title myname
	end tell
end dsperrmsg

On my dual-core G5 (OS X 10.5.3), changing background color and text color had no effect. I do have dual screens, but was satisfied that it chose the menu bar screen to alter.

Hi Adam,

I am very sorry for the poor user experience you encountered. But please note, that you won’t instantly see the changes made in the configuration menu. The new settings will only show up after you run Lacurne once again, as it will then create a new PDF document according to the chosen options.

Best regards from Germany!

No need for an apology, Martin; It worked perfectly as you describe, now running on my main screen. Thanks.

I just discovered this script, and I can report that it DOES work under 10.4.11. However, in the PDF it creates, the carriage returns are visable on the caldendar. Is there a way to edit either the Python script or the Applescript to supress showing the carriage returns?

I know that using GeekTool, I can use the command to embed a calendar on the desktop, and the carriage returns will not be visiable.

Having installed Mac OS X 10.4.11 on one of my current work Macs (Classic rulez!) I also encountered this phenomenon. But so far I had no time to find a workaround :frowning: So much to do, so little time :wink:

Hey Martin,
I installed your script and its great, Works fine under 10.5.7
But i dont understand how to get the custom background/menu thing? Could you explain it for someone a bit slow (me) ?
Cheers
Jonno

Go to the folder where you keep Lacurne and drag virtually anything from that window and drop it on Lacurne. You’ll get a “choose from list” menu to choose from to set things up. Nothing will happen to whatever you drop – that’s just the trigger.

I was able to disable my second display, run Lacurne, then enable the second again. Both displays then had the calendar. 10.5.6, Dual 2.3 G5.

Lacurne won’t run on Mac OS X 10.6 (Snow Leopard), as this OS does not include Core Graphics bindings (CGBindings) for 64-bit Python :frowning:

Disable how? By disconnecting it from the G5?

Where is the Lacurne PDF file that appears on an OS X 10.5.x desktop stored? Since I have both a Leopard and a Snow Leopard machine, couldn’t I create the desktop on the Leopard machine and copy it to the Snow Leopard machine?

Hi Adam,

It’s stored in a folder named Lacurne located in the Application Support folder of your user’s library folder:

~/Library/Application Support/Lacurne/

Best regards from rainy Berlin,

Martin

Thanks, Martin;

The approach I suggested works perfectly for me. I ran Lacurne on my dual core G5 (OS X 10.5.8), went to ~/Library/Application Support/Lacurne/ and copied the new file to my Intel core 2 duo MBP (OS X 10.6.1), chose it from the Desktop pref pane, and bingo; the October PDF is now the desktop there too. :lol:

I’ll test at some point to see if I can put it in the Dropbox by changing some of your code. That’ll keep it up to date on the laptop whenever I change it on the G5.