# ------------------------------------------------------------------------------ # optional documents directory, implemented as a GUI plugin # # this feature provides a beginner-friendly location for patches & externals and # is created by a dialog when first running Pd # # a subfolder for externals is automatically added to the user search paths and # file dialogs will open in the docs path by default # # Dan Wilcox 2017 # # The minimum version of TCL that allows the plugin to run package require Tcl 8.4 9 package require pdwindow 0.1 package require pd_guiprefs 0.1 package require wheredoesthisgo namespace eval ::pd_docsdir:: { variable docspath namespace export init namespace export get_default_path namespace export get_disabled_path namespace export create_path namespace export set_path namespace export path_is_valid namespace export get_default_externals_path namespace export get_externals_path namespace export externals_path_is_valid } # if empty, the user is prompted about creating this # if set to "DISABLED", the docs dir functionality is disabled set ::pd_docsdir::docspath "" # check the Pd documents directory path & prompt user to create if it's empty, # otherwise ignore all of this if the user cancelled it # set reset to true to force the welcome prompt proc ::pd_docsdir::init {{reset false}} { if {$reset} { set ::pd_docsdir::docspath "" } else { set ::pd_docsdir::docspath [::pd_guiprefs::read docspath] } set docspath $::pd_docsdir::docspath if { "$docspath" eq "DISABLED" } { # respect prev decision return } elseif { "$docspath" eq "" } { # sanity check, Pd might be running on a non-writable install if {![file exists $::env(HOME)] || ![file writable $::env(HOME)]} { return } # default location: Documents dir if it exists, otherwise use home set docspath_default [file join $::env(HOME) "Documents"] if {![file exists $docspath_default]} { set docspath_default $::env(HOME) } if {![file writable $docspath_default]} { # sanity check set docspath "DISABLED" return } set docspath_default [file join $docspath_default "Pd"] # prompt if {[file isdir ${docspath_default}]} { set msg [_ "Do you want Pd to use the existing documents directory for patches and external libraries?\n\nLocation: %s\n\nYou can change or disable this later in the Path preferences." ] } { set msg [_ "Do you want Pd to create a documents directory for patches and external libraries?\n\nLocation: %s\n\nYou can change or disable this later in the Path preferences." ] } set _args "-message \"[format $msg $docspath_default]\" -type yesnocancel -default yes -icon question -parent .pdwindow" switch -- [eval tk_messageBox ${_args}] { yes { set docspath $docspath_default # set the new docs path if {![::pd_docsdir::create_path $docspath]} { # didn't work return } } no { # bug off set docspath "DISABLED" } cancel { # defer return } } ::pd_docsdir::set_path $docspath if {[::pd_docsdir::create_externals_path]} { ::pd_docsdir::add_externals_path if {[namespace exists ::deken]} { # clear first so set_installpath doesn't pick up prev value from guiprefs set ::deken::installpath "" :::deken::set_installpath [::pd_docsdir::get_externals_path] } } focus .pdwindow } else { # check saved setting set fullpath [file normalize "$docspath"] if {![file exists $fullpath]} { set msg [_ "Pd documents directory cannot be found:\n\n%s\n\nChoose a new location?" ] set _args "-message \"[format $msg $::pd_docsdir::docspath]\" -type yesno -default yes -icon warning -parent .pdwindow" switch -- [eval tk_messageBox ${_args}] { yes { # set the new docs path set newpath [tk_chooseDirectory -title [_ "Choose Pd documents directory:"] \ -initialdir $::env(HOME)] if {$newpath ne ""} { if {![::pd_docsdir::update_path $newpath]} { # didn't work return } } } no { # defer return } } focus .pdwindow } } # open dialogs in docs dir if GUI was started first if {[::pd_docsdir::path_is_valid]} { if {"$::filenewdir" eq "$::env(HOME)"} { set ::filenewdir $::pd_docsdir::docspath } if {"$::fileopendir" eq "$::env(HOME)"} { set ::fileopendir $::pd_docsdir::docspath } } ::pdwindow::verbose 0 "Pd documents directory: $::pd_docsdir::docspath\n" } # try to find a default docs path, returns an empty string if not possible proc ::pd_docsdir::get_default_path {} { # sanity check, Pd might be running on a non-writable install if {![file exists $::env(HOME)] || ![file writable $::env(HOME)]} { return "" } # default location: Documents dir if it exists, otherwise use home set path [file join $::env(HOME) "Documents"] if {![file exists $path]} { set path $::env(HOME) } if {[file writable $path]} { return [file join $path "Pd"] } return "" } # get the default disabled path value proc ::pd_docsdir::get_disabled_path {} { return "DISABLED" } # update the Pd documents directory path & externals path, creates if needed # higher level complement to create_path & path_set # checks ::deken::installpath value # returns 1 if the update was successful proc ::pd_docsdir::update_path {path} { if {"$path" eq "" || "$path" eq "DISABLED"} { ::pd_docsdir::set_path "$path" } else { if {![::pd_docsdir::create_path "$path"]} {return 0} ::pd_docsdir::set_path "$path" # only try creating the externals path if it is the deken installpath, # this keeps the installpath from changing arbitrarily if {![namespace exists ::deken] || [::pd_docsdir::is_externals_path_deken_installpath]} { if {[::pd_docsdir::create_externals_path]} { ::pd_docsdir::add_externals_path } } } return 1 } # create the Pd documents directory path and it's "externals" subdir, # set externalsdir to false to create the given path only # returns true if the path already exists proc ::pd_docsdir::create_path {path} { if {"$path" eq "" || "$path" eq "DISABLED"} {return 0} if {[file mkdir [file normalize "$path"]] eq ""} { return 1 } set msg [_ "couldn't create Pd documents directory: %s" $path] ::pdwindow::error "${msg}\n" return 0 } # set the Pd documents directory path proc ::pd_docsdir::set_path {path} { set ::pd_docsdir::docspath $path ::pd_guiprefs::write docspath "$path" } # returns 1 if the docspath is set, not disabled, & a directory proc ::pd_docsdir::path_is_valid {} { if {"$::pd_docsdir::docspath" eq "" || "$::pd_docsdir::docspath" eq "DISABLED" || ![file isdirectory "$::pd_docsdir::docspath"]} { return 0 } return 1 } # create the externals subdir within the current docs path or a given path, # returns 1 on success proc ::pd_docsdir::create_externals_path {{path ""}} { if {"$path" eq ""} { if {"$::pd_docsdir::docspath" eq "" || "$::pd_docsdir::docspath" eq "DISABLED"} { return 0 } set path $::pd_docsdir::docspath } set newpath [file join [file normalize "$path"] "externals"] if {[file mkdir "$newpath" ] eq ""} { return 1 } set msg [_ "couldn't create \"externals\" directory in: %s" $path] ::pdwindow::error "${msg}\n" return 0 } # get the externals subdir within the current docs path or a given path, # returns an empty string if docs path is not valid proc ::pd_docsdir::get_externals_path {{path ""}} { if {"$path" eq ""} { if {[::pd_docsdir::path_is_valid]} { return [file join $::pd_docsdir::docspath "externals"] } else { return "" } } return [file join "$path" "externals"] } # returns 1 if the docspath externals subdir exists proc ::pd_docsdir::externals_path_is_valid {} { set path [::pd_docsdir::get_externals_path] if {"$path" ne "" && [file writable [file normalize "$path"]]} { return 1 } return 0 } # add the externals subdir within the current docs path or a given path to the # the user search paths proc ::pd_docsdir::add_externals_path {{path ""}} { set externalspath [::pd_docsdir::get_externals_path $path] if {$externalspath ne ""} { add_to_searchpaths $externalspath } } # is the deken installpath the externals subdir within the current docs path? proc ::pd_docsdir::is_externals_path_deken_installpath {} { if {![namespace exists ::deken]} {return 0} if {[file normalize $::deken::installpath] eq [file normalize [::pd_docsdir::get_externals_path]]} { return 1 } return 0 } # self-init after loading ::pd_docsdir::init