Mailbox hierarchy

I’m reting to display a list of Mail’s mailboxes with the choose list command and have them pick a mailbox. code is:

			tell application "Mail" to set mboxList to the name of every mailbox
			set usrchoice to choose from list mboxList with prompt "Choose mailbox" default items "Inbox"

The code works OK but doesn;t indicate the hierarchy of the mailboxes. For example if I have mailbox named “Green” inside a mailbox named “Colours”, the list only shows “Green” with no indication that it is contained by “Colours”

If I get a list of ever mailbox from Mail, it looks like:

{mailbox “Colours/Green” of application “Mail”, …}

How can I extract “Colours/Green” from this list instead of just “Green”?

As a secondary issue, once I’ve done that, I want to sort the resulting list. I know there’s a way to sort Finder itmes but is there a general sort routine for lists?



I think I’ve figured out how to do this with the use of a recursive subroutine. I’m sure the code is not as elegant as it could be but here it is if anyone would like to use it. I still need to sort the resulting list.

global master_mbox_list
global root_mbox_name
tell application "Mail" to set mboxList to every mailbox
using terms from application "Mail"
	set master_mbox_list to {}
	repeat with thisMbox in mboxList
		set myContainer to container of thisMbox
			set myClass to class of myContainer
		on error
			-- Trying to get the class of the container of a mailbox that doesn't have any containers causes an error so his means we have a top level mailbox
			set root_mbox_name to name of thisMbox
			my get_mailboxes(thisMbox)
		end try
	end repeat
end using terms from
set theMbox to choose from list master_mbox_list with prompt "Pick a mailbox"
on get_mailboxes(mbox)
	using terms from application "Mail"
		set end of master_mbox_list to root_mbox_name
		set temp_mbox_name to root_mbox_name
		set subordinate_mbox_list to every mailbox of mbox
		repeat with this_subordinate_mbox in subordinate_mbox_list
			set root_mbox_name to root_mbox_name & "/" & name of this_subordinate_mbox
			my get_mailboxes(this_subordinate_mbox)
			set root_mbox_name to temp_mbox_name
		end repeat
	end using terms from
end get_mailboxes

I ended up taking nearly the same approach. I choose to catch the error of getting the account object’s container instead of the error caused by getting the account object’s class. Here is my code, for what it is worth.

on run
	tell application "Mail" to set mailboxListRef to a reference to every mailbox
	set mailboxPaths to {}
	set mismatches to {}
	repeat with mailbox in mailboxListRef
		set mailbox to contents of mailbox
		set mailboxPath to computeMailboxPath(mailbox)
		set mailboxByPath to missing value
			tell application "Mail" to set mailboxByPath to mailbox mailboxPath
		end try
		if mailboxByPath is not equal to mailbox then
			set end of mismatches to {mailbox, mailboxPath}
			set end of mailboxPaths to mailboxPath
		end if
	end repeat
	if length of mismatches is 0 then
		{mailboxPaths, mismatches}
	end if
end run

to computeMailboxPath(mailbox)
	-- this is a bit slow
	-- memoization might help for many calls for mailboxes in the same (sub)hierarchy(ies)
	set pathComponents to {}
	set theName to name of mailbox
	-- backtrace the containers of the starting mailbox to collect the path components
	-- eventually the container will be an account object, which has no container and will cause an error if we try to get it
	-- using "class of container of |mailbox| equals account" also results in an error (trying to get the account object's class also throws an error)
	repeat until theName is missing value
		set beginning of pathComponents to theName
			using terms from application "Mail"
				set |mailbox| to container of |mailbox|
			end using terms from
			set theName to name of mailbox
		on error
			set theName to missing value
		end try
	end repeat
	join("/", pathComponents)
end computeMailboxPath

to join(separator, components)
	set {otid, text item delimiters} to {text item delimiters, {separator}}
		set joined to components as Unicode text
		set text item delimiters to otid
	on error m
		set text item delimiters to otid
		error m
	end try
end join

If the code looks a bit odd, I was trying to minimize the extent of the tell blocks/statements and the using terms from blocks. I also refused to rename my mailbox variable (thus the bars around the name in the Mail-targeted block). I tested it with Mail Version 2.1.3 (753.1) on my 10.4.11 machine.

For your sorting needs, a simple bubble sort is a common recommendation around here. The search function should turn up several examples (and some more advanced sorts, like quick sort and maybe even a heap sort). If I remember correctly, someone posted a bubble sort example just in the last couple of days.

Thanks Chris, nice to know I’m kindof on the right track. I’ll search for the sort routines.

The code I used does eeem to be on the slow side but it’s part of a function to set up preferences for another process so it probably only gets executed at installation or perhaps once every few months so performance isn’t really a worry.