Suppressing the sidebar in NSOpenPanel

Is it possible to suppress the sidebar (the one with “DEVICES”, “SHARED”, “PLACES” etc.) in an NSOpenPanel?

I have carefully limited access to one folder with

		myOpenPanel's setDirectoryURL_(FILE_PATH)
		. . .
		myOpenPanel's setCanChooseDirectories_(false)

however users can (um) use this sidebar to escape. It’s particularly worrying that if they go into the music or photos folders they get stuck there and my ‘myOpenPanel’s setDirectoryURL_(FILE_PATH)’ seems to have no effect!

rhb

If you don’t want to allow users to change folders, you don’t want an open panel – you should list the folder yourself and put it in a table. Hobbling an open panel is just going to confuse people.

Thanks Shane,
the only trouble with your suggestion is that within that folder there is a structure of sub-folders (which the users manage).
It seems to me that Apple has gone too far with that side-panel— it clearly contradicts setCanChooseDirectories_(false), since with it there you CAN choose directories!

Assuming that I’m going to be stubborn (and too lazy to replicate the folder management by other means) Is there no way to turn it off? It has always seemed suspiciously “microsofty” to me.

rhb

???
setCanChooseDirectories_(false) does not allow you to choose directories. It only allows you to open into them.

Ah! but the side-bar allows you to go to special places, like the Desktop—whence you can get all over the place thus rendering the setCanChooseDirectories_(false) pretty useless. :frowning:

setCanChooseDirectories_(false) is not for stopping access to areas.
Its for stopping directories being chosen as the result of the choose command, and being passed back the the application as the argument, when it expects a file.
So in your case Yes. it is useless for what you are trying to do, but it does do the job it is meant to do.

Do you need to keep users from seeing things in your other directories, or just not be able to choose anything in them – if it’s the latter, you can use the panel_validateURL_error_ method to limit them to only files that contain your top directory’s name in their path.

rdelmar, nice suggestion, but I really want to control access to directories ‘before the event’ rather than after. I was hoping that there was some way, that I hadn’t noticed, of removing/hiding the side-bar without subclassing my own panel (which is probably beyond my current abilities).

I did find a way to suppress the sidebar, but in addition to that you also need to get rid of the popup button and the search field, since each of them allows you to escape your folder and get all the way to the root directory. The following code does does all three things by burrowing down through the views to find the sidebar view, the popup button and the search field. The sidebar is then set to 0 width, and the popup button and search field are removed from their respective superviews. I’m not sure how kosher this is since it probably violates all of Apple’s human interface guidelines, but here it is:

script OpenPanelAppDelegate
	property parent : class "NSObject"
	property FILE_PATH : POSIX path of (path to documents folder as string)
	property NSOpenPanel : class "NSOpenPanel"
	property sideView : 0
	
	on splitViewDidResizeSubviews_(aNotification) --This is a splitView delegate method
		set newSideFrame to {sideView's frame()'s origin(), {0, sideView's frame()'s |size|()'s height}} --makes the side view have 0 width as you try to resize
		sideView's setFrame_(newSideFrame)
	end splitViewDidResizeSubviews_
	
	on applicationWillFinishLaunching_(aNotification)
		set myPanel to NSOpenPanel's openPanel()
		set view4 to myPanel's contentView()'s subviews()'s objectAtIndex_(4)'s subviews()'s objectAtIndex_(0)'s subviews()
		set splitView to view4's objectAtIndex_(0)'s subviews()'s objectAtIndex_(2)
		set popup to (view4's objectAtIndex_(1))'s subviews()'s objectAtIndex_(5)
		set search to (view4's objectAtIndex_(1))'s subviews()'s objectAtIndex_(2)
		popup's removeFromSuperview()
		search's removeFromSuperview()
		set sideView to view4's objectAtIndex_(0)'s subviews()'s objectAtIndex_(2)'s subviews()'s objectAtIndex_(0)
		sideView's setFrame_({sideView's frame()'s origin(), {0, sideView's frame()'s |size|()'s height}}) --sets the width to 0
		myPanel's setDirectoryURL_(FILE_PATH)
		myPanel's setDelegate_(me)
		splitView's setDelegate_(me)
		myPanel's runModal()
	end applicationWillFinishLaunching_
end script

This allows one to navigate anything below the starting folder (Documents in this example) and choose files. I don’t know if there is a way to leave the popup button in place so you can see what folder you are in, but have it disabled so you can’t choose anything further up the path. Same with the search field – maybe there is a way to leave it there to make the panel look more standard but not allow you to search.

Great effort.

But then the user hits Command-up arrow…

G R E A T !

works perfectly

rdelmar thank you.

and Shane I think I can live with that.

Here’s one more modification to get around the command-up arrow problem. In order for the performKeyEquivalent method to work, the script’s parent class was changed to NSOpenPanel and myPanel was initialized with “my init()”. I’ve also included the panel:validateURL:error: method to prevent any file from being chosen that’s not in the path of your intended folder, in case the user (or Shane) finds some other sneaky way to escape your folder.

script OpenPanelAppDelegate
	property parent : class "NSOpenPanel"
	property FILE_PATH : POSIX path of (path to documents folder as string)
	--property NSOpenPanel : class "NSOpenPanel"
	property sideView : 0
	
	on splitViewDidResizeSubviews_(aNotification) --This is a splitView delegate method
		set newSideFrame to {sideView's frame()'s origin(), {0, sideView's frame()'s |size|()'s height}} --makes the side view have 0 width as you try to resize
		sideView's setFrame_(newSideFrame)
	end splitViewDidResizeSubviews_
	
	on panel_validateURL_error_(myPanel, theURL, myError) --This validates whether the user has chosen a file that meets your criteria
		set URLString to theURL's absoluteString() --Converts the URL to an NSString needed for the following method
		if URLString's rangeOfString_("/Users/rdelmar/Documents")'s |length| is not 0 then --Equals 0 if the quoted string is not contained in theURL
			set chosenURLs to myPanel's URLs()
			log chosenURLs
			return 1
		else
			return 0
		end if
	end panel_validateURL_error_
	
	on performKeyEquivalent_(theEvent)
		if theEvent's keyCode's intValue() = 126 and theEvent's modifierFlags() = 11534608 then --This is Command-UpArrow
			return 1 --Responds to theEvent so that it doesn't get passed to myPanel
		else
			return 0 --Other keystrokes still get passed on to myPanel
		end if
	end performKeyEquivalent_
	
	on applicationWillFinishLaunching_(aNotification)
		set myPanel to my init() --Makes myPanel an instance of the applescript's class
		set view4 to myPanel's contentView()'s subviews()'s objectAtIndex_(4)'s subviews()'s objectAtIndex_(0)'s subviews()
		set splitView to view4's objectAtIndex_(0)'s subviews()'s objectAtIndex_(2)
		set popup to (view4's objectAtIndex_(1))'s subviews()'s objectAtIndex_(5)
		set search to (view4's objectAtIndex_(1))'s subviews()'s objectAtIndex_(2)
		popup's removeFromSuperview()
		search's removeFromSuperview()
		set sideView to view4's objectAtIndex_(0)'s subviews()'s objectAtIndex_(2)'s subviews()'s objectAtIndex_(0)
		sideView's setFrame_({sideView's frame()'s origin(), {0, sideView's frame()'s |size|()'s height}}) --sets the width to 0
		myPanel's setDirectoryURL_(FILE_PATH)
		myPanel's setDelegate_(me)
		splitView's setDelegate_(me)
		myPanel's runModal()
	end applicationWillFinishLaunching_
end script

Ric

Help! I can’t get out!

:slight_smile:

I didn’t like the fact that the current directory wasn’t displayed in the code I posted above, so I revised the program to include an NSImageView with the folder icon in it, and a NSTextField (configured as a label) to display the current directory. I found that I could get the current directory from a notification sent out by the popup button --so instead of getting rid of the popup button (which eliminates the notifications), I just made it transparent and put the image view and label in the same location in its superview:

script OpenPanelAppDelegate
	property parent : class "NSOpenPanel"
	property FILE_PATH : POSIX path of (path to documents folder as string)
	property NSNotificationCenter : class "NSNotificationCenter"
	property NSString : class "NSString"
	property NSTextField : class "NSTextField"
	property NSImage : class "NSImage"
	property NSImageView : class "NSImageView"
	property sideView : 0
	property myLabel : ""
	
	on splitViewDidResizeSubviews_(aNotification) --This is a splitView delegate method
		set newSideFrame to {sideView's frame()'s origin(), {0, sideView's frame()'s |size|()'s height}} --makes the side view have 0 width as you try to resize
		sideView's setFrame_(newSideFrame)
	end splitViewDidResizeSubviews_
	
	on panel_validateURL_error_(myPanel, theURL, myError) --This validates whether the user has chosen a file that meets your criteria
		set URLString to theURL's absoluteString() --Converts the URL to an NSString needed for the following method
		if URLString's rangeOfString_("/Users/rdelmar/Documents")'s |length| is not 0 then --Equals 0 if the quoted string is not contained in theURL
			set chosenURLs to myPanel's URLs()
			log chosenURLs
			return 1
		else
			display alert "Unsupported Action" message "Only files in Documents or its subfolders can be chosen"
			return 0
		end if
	end panel_validateURL_error_
	
	on performKeyEquivalent_(theEvent)
		if theEvent's keyCode's intValue() = 126 and theEvent's modifierFlags() = 11534608 then --This is Command-UpArrow
			return 1 --Responds to theEvent so that it doesn't get passed to myPanel
			--Could invoke an info panel here to explain that command-up arrow has been disabled
		else
			return 0 --Other keystrokes still get passed on to myPanel
		end if
	end performKeyEquivalent_
	
	on updateLabel_(aNotification)
		myLabel's setStringValue_(aNotification's object()'s itemAtIndex_(0)'s |title|)
	end updateLabel_
	
	
	on applicationWillFinishLaunching_(aNotification)
		set observer to NSNotificationCenter's defaultCenter
		observer's addObserver_selector_name_object_(me, "updateLabel:", "NSMenuDidAddItemNotification", missing value)
		set myPanel to init() --Makes myPanel an instance of the applescript's class
		set view4 to myPanel's contentView()'s subviews()'s objectAtIndex_(4)'s subviews()'s objectAtIndex_(0)'s subviews()
		set popup to (view4's objectAtIndex_(1))'s subviews()'s objectAtIndex_(5)
		set search to (view4's objectAtIndex_(1))'s subviews()'s objectAtIndex_(2)
		popup's setTransparent_(1) --Keeps it from appearing but still sends notifications that I observe above
		search's removeFromSuperview() --Removes the search field entirely
		set splitView to view4's objectAtIndex_(0)'s subviews()'s objectAtIndex_(2)
		set sideView to view4's objectAtIndex_(0)'s subviews()'s objectAtIndex_(2)'s subviews()'s objectAtIndex_(0)
		sideView's setFrame_({sideView's frame()'s origin(), {0, sideView's frame()'s |size|()'s height}}) --sets the width to 0
		set myLabel to NSTextField's alloc()'s initWithFrame_({{275, 6}, {280, 22}})
		set myView to NSImageView's alloc()'s initWithFrame_({{242, 9}, {26, 22}})
		myView's setImageFrameStyle_(0)
		myView's setEditable_(0)
		myView's setImage_(NSImage's imageNamed_("NSFolder"))
		myLabel's setSelectable_(0)
		myLabel's setDrawsBackground_(0)
		myLabel's setBordered_(0)
		view4's objectAtIndex_(1)'s addSubview_(myLabel)
		view4's objectAtIndex_(1)'s addSubview_(myView)
		myPanel's setDirectoryURL_(FILE_PATH)
		myPanel's setDelegate_(me)
		splitView's setDelegate_(me)
		myPanel's runModal()
	end applicationWillFinishLaunching_
end script

Ric

Hi rdelmar,
I’m en vacances in England at the moment, so this will have to be in version 1.1 of the app. :smiley:
Thanks very much for the help (and to Shane too)
RHB