00001 # TKE - Advanced Programmer's Editor 00002 # Copyright (C) 2014-2019 Trevor Williams (phase1geo@gmail.com) 00003 # 00004 # This program is free software; you can redistribute it and/or modify 00005 # it under the terms of the GNU General Public License as published by 00006 # the Free Software Foundation; either version 2 of the License, or 00007 # (at your option) any later version. 00008 # 00009 # This program is distributed in the hope that it will be useful, 00010 # but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 # GNU General Public License for more details. 00013 # 00014 # You should have received a copy of the GNU General Public License along 00015 # with this program; if not, write to the Free Software Foundation, Inc., 00016 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 00017 00018 ###################################################################### 00019 # Name: share.tcl 00020 # Author: Trevor Williams (phase1geo@gmail.com) 00021 # Date: 9/14/2016 00022 # Brief: Namespace that handles settings sharing. 00023 ###################################################################### 00024 00025 namespace eval share { 00026 00027 variable last_directory "" 00028 variable last_items [list] 00029 00030 array set data {} 00031 array set widgets {} 00032 array set items {} 00033 00034 ###################################################################### 00035 # Returns the list of sharing items. The following is a description 00036 # of each item. 00037 # - Referred nickname 00038 # - Namespace 00039 # - Displayed name in GUI 00040 proc get_share_items {} { 00041 00042 return [list \ 00043 emmet emmet "Emmet" \ 00044 favorites favorites [msgcat::mc "Favorites"] \ 00045 launcher launcher [msgcat::mc "Launcher"] \ 00046 plugins plugins [msgcat::mc "Plugins"] \ 00047 prefs preferences [msgcat::mc "Preferences"] \ 00048 remote remote [msgcat::mc "Remote Connections"] \ 00049 sessions sessions [msgcat::mc "Sessions"] \ 00050 snippets snippets [msgcat::mc "Snippets"] \ 00051 templates templates [msgcat::mc "Templates"] \ 00052 themes themes [msgcat::mc "Themes"] \ 00053 ] 00054 00055 } 00056 00057 ###################################################################### 00058 # Called by the whenever sharing items are changed. 00059 proc save_changes {share_dir share_items} { 00060 00061 variable data 00062 variable last_directory 00063 variable last_items 00064 00065 # Save the last directory 00066 set last_directory $data(ShareDirectory) 00067 set last_items $data(ShareItems) 00068 00069 # Save the changes 00070 set data(ShareDirectory) $share_dir 00071 set data(ShareItems) $share_items 00072 00073 # Indicate that the share information have changed 00074 share_changed 00075 00076 # If the directory changed but was previously pointing at a remote directory, 00077 # transfer all of the information that was being stored in the remote directory 00078 # to the home directory 00079 if {$last_directory ne ""} { 00080 file_transfer $last_directory $::tke_home $last_items 00081 } 00082 00083 # If we are using a remote directory, create the directory and share the files 00084 # to that directory. 00085 if {$share_dir ne ""} { 00086 create_share_dir $data(ShareDirectory) $data(ShareItems) 00087 } 00088 00089 # Write the file contents 00090 write_file 00091 00092 } 00093 00094 ###################################################################### 00095 # Update all namespaces with the updated sharing information. 00096 proc share_changed {} { 00097 00098 variable data 00099 00100 # Indicate the new directory to all sharing items 00101 if {$data(ShareDirectory) ne ""} { 00102 foreach {type nspace name} [get_share_items] { 00103 ${nspace}::share_changed [expr {([lsearch $data(ShareItems) $type] != -1) ? $data(ShareDirectory) : $::tke_home}] 00104 } 00105 } else { 00106 foreach {type nspace name} [get_share_items] { 00107 ${nspace}::share_changed $::tke_home 00108 } 00109 } 00110 00111 } 00112 00113 ###################################################################### 00114 # Create the share directory and copy the current items to it and create 00115 # symlinks, if necessary. 00116 proc create_share_dir {share_dir share_items} { 00117 00118 variable last_items 00119 00120 # Create the sharing directory 00121 file mkdir $share_dir 00122 00123 foreach {type nspace name} [get_share_items] { 00124 00125 # Copy the relevant files to the share directory 00126 if {[lsearch $share_items $type] != -1} { 00127 foreach item [${nspace}::get_share_items $::tke_home] { 00128 set home_item [file join $::tke_home $item] 00129 set share_item [file join $share_dir $item] 00130 if {[file exists $home_item] && ![file exists $share_item]} { 00131 file copy -force $home_item $share_dir 00132 } 00133 } 00134 00135 # Otherwise, copy relevant items to home directory if we used to get the items from the share directory 00136 } elseif {([lsearch $share_items $type] == -1) && ([lsearch $last_items $type] != -1)} { 00137 foreach item [${nspace}::get_share_items $share_dir] { 00138 set home_item [file join $::tke_home $item] 00139 set share_item [file join $share_dir $item] 00140 if {[file exists $share_item]} { 00141 if {[file exists $home_item] && [file isdirectory $home_item]} { 00142 file delete -force $tname 00143 } 00144 file copy -force $share_item $::tke_home 00145 } 00146 } 00147 } 00148 00149 } 00150 00151 } 00152 00153 ###################################################################### 00154 # Performs a file transfer, removing any items that are in the target 00155 # directory that will be moved. This is used when importing/exporting 00156 # settings data (use the create_share_dir) method to perform a file share. 00157 proc file_transfer {from_dir to_dir share_items} { 00158 00159 variable data 00160 00161 # Get the list of files/directories to transfer based on the items 00162 foreach {type nspace name} [get_share_items] { 00163 set fdir [expr {(($from_dir eq "") || (($from_dir eq $data(ShareDirectory)) && ([lsearch $data(ShareItems) $type] == -1))) ? $::tke_home : $from_dir}] 00164 set tdir [expr {(($to_dir eq "") || (($to_dir eq $data(ShareDirectory)) && ([lsearch $data(ShareItems) $type] == -1))) ? $::tke_home : $to_dir}] 00165 if {[lsearch $share_items $type] != -1} { 00166 foreach item [${nspace}::get_share_items $fdir] { 00167 if {[file exists [set fname [file join $fdir $item]]]} { 00168 set tname [file join $tdir $item] 00169 if {[file exists $tname] && [file isdirectory $tname]} { 00170 file delete -force $tname 00171 } 00172 file copy -force $fname $tdir 00173 } 00174 } 00175 } 00176 } 00177 00178 } 00179 00180 ###################################################################### 00181 # Returns the sharing information. 00182 proc get_share_info {} { 00183 00184 variable data 00185 00186 # Gather the share items 00187 set items [list] 00188 foreach {type nspace name} [get_share_items] { 00189 lappend items $type [expr [lsearch $data(ShareItems) $type] != -1] 00190 } 00191 00192 return [list $data(ShareDirectory) $items] 00193 00194 } 00195 00196 ###################################################################### 00197 # Displays the export window 00198 proc create_export {{parent .}} { 00199 00200 variable widgets 00201 variable data 00202 variable items 00203 00204 toplevel .sharewin 00205 wm title .sharewin [msgcat::mc "Export Settings Data"] 00206 wm resizable .sharewin 0 0 00207 wm transient .sharewin $parent 00208 00209 ttk::frame .sharewin.f 00210 ttk::label .sharewin.f.l -text [format "%s: " [msgcat::mc "Directory"]] 00211 set widgets(directory) [ttk::entry .sharewin.f.e -width 40 -state readonly] 00212 ttk::button .sharewin.f.b -style BButton -text [format "%s..." [msgcat::mc "Browse"]] -command [list share::browse_directory] 00213 00214 pack .sharewin.f.l -side left -padx 2 -pady 2 00215 pack .sharewin.f.e -side left -padx 2 -pady 2 -fill x -expand yes 00216 pack .sharewin.f.b -side right -padx 2 -pady 2 00217 00218 ttk::frame .sharewin.lf 00219 ttk::labelframe .sharewin.lf.f -text [msgcat::mc "Settings to export"] 00220 set i 0 00221 set columns 3 00222 foreach {type nspace name} [get_share_items] { 00223 set items($type) 1 00224 grid [ttk::checkbutton .sharewin.lf.f.$type -text $name -variable share::items($type) -command [list share::handle_do_state]] -row [expr $i % $columns] -column [expr $i / $columns] -sticky news -padx 2 -pady 2 00225 incr i 00226 } 00227 00228 pack .sharewin.lf.f -side left -padx 20 -pady 2 00229 00230 ttk::frame .sharewin.bf 00231 set widgets(do) [ttk::button .sharewin.bf.do -style BButton -text [msgcat::mc "Export"] -width 6 -command [list share::do_export]] 00232 ttk::button .sharewin.bf.cancel -style BButton -text [msgcat::mc "Cancel"] -width 6 -command [list share::do_cancel] 00233 00234 pack .sharewin.bf.cancel -side right -padx 2 -pady 2 00235 pack .sharewin.bf.do -side right -padx 2 -pady 2 00236 00237 pack .sharewin.f -fill x 00238 pack .sharewin.lf -fill both -expand yes 00239 pack .sharewin.bf -fill x 00240 00241 # Handle the state of the do button 00242 handle_do_state 00243 00244 # Grab the focus 00245 ::tk::SetFocusGrab .sharewin .sharewin.f.b 00246 00247 # Center the window 00248 ::tk::PlaceWindow .sharewin widget . 00249 00250 # Wait for the window to close 00251 tkwait window .sharewin 00252 00253 # Restore the grab and focus 00254 ::tk::RestoreFocusGrab .sharewin .sharewin.f.b 00255 00256 } 00257 00258 ###################################################################### 00259 # Allows the user to use the file browser to select a directory to 00260 # import/export to. 00261 proc browse_directory {} { 00262 00263 variable data 00264 variable widgets 00265 00266 # Get the directory from the user 00267 if {[set dir [tk_chooseDirectory -parent .sharewin -initialdir [pwd]]] ne ""} { 00268 00269 # Insert the directory 00270 $widgets(directory) configure -state normal 00271 $widgets(directory) delete 0 end 00272 $widgets(directory) insert end $dir 00273 $widgets(directory) configure -state readonly 00274 00275 # Update the do button state 00276 handle_do_state 00277 00278 } 00279 00280 } 00281 00282 ###################################################################### 00283 # Handles the state of the import/export button in the import/export 00284 # window. 00285 proc handle_do_state {} { 00286 00287 variable widgets 00288 variable items 00289 00290 # Disable the button by default 00291 $widgets(do) configure -state disabled 00292 00293 if {[$widgets(directory) get] ne ""} { 00294 foreach {type nspace name} [get_share_items] { 00295 if {$items($type)} { 00296 $widgets(do) configure -state normal 00297 return 00298 } 00299 } 00300 } 00301 00302 } 00303 00304 ###################################################################### 00305 # Perform the import/export operation. 00306 proc do_export {} { 00307 00308 variable widgets 00309 variable items 00310 variable data 00311 00312 # Copy the relevant files to transfer 00313 set item_list [list] 00314 foreach {type nspace name} [get_share_items] { 00315 if {$items($type)} { 00316 lappend item_list $type 00317 } 00318 } 00319 00320 # Get the share directory 00321 set to_dir [$widgets(directory) get] 00322 00323 # Perform the file transfer 00324 if {$data(ShareDirectory) ne $to_dir} { 00325 file_transfer $data(ShareDirectory) $to_dir $item_list 00326 } 00327 00328 # Close the share window 00329 destroy .sharewin 00330 00331 } 00332 00333 ###################################################################### 00334 # Cancels the share window. 00335 proc do_cancel {} { 00336 00337 # Close the share window 00338 destroy .sharewin 00339 00340 } 00341 00342 ###################################################################### 00343 # Called on tool startup. If the share file does not exist in the home 00344 # directory, call the share wizard to help the user setup a valid share 00345 # file. 00346 proc initialize {already_running} { 00347 00348 variable data 00349 00350 if {[file exists [file join $::tke_home share.tkedat]]} { 00351 load_file 00352 } elseif {[llength [glob -nocomplain -directory $::tke_home *]] > 0} { 00353 set data(ShareDirectory) "" 00354 set data(ShareItems) [list] 00355 write_file 00356 } elseif {!$already_running} { 00357 import_share_wizard 00358 } else { 00359 puts "" 00360 puts [msgcat::mc "Another version of TKE is running and is setting things up. Exiting..."] 00361 exit 1 00362 } 00363 00364 } 00365 00366 ###################################################################### 00367 # Displays the import/share wizard which will be displayed if TKE is 00368 # started and a share.tkedat file is not found in the user's home directory. 00369 proc import_share_wizard {} { 00370 00371 variable data 00372 variable items 00373 00374 # Show the user startup 00375 lassign [startup::create] action dirname item_list 00376 00377 array set items $item_list 00378 00379 # Setup the share directory 00380 set data(ShareDirectory) [expr {($action eq "share") ? $dirname : ""}] 00381 00382 # Setup the share items list 00383 set data(ShareItems) [list] 00384 00385 if {$action ne "local"} { 00386 foreach {type nspace name} [get_share_items] { 00387 if {$items($type)} { 00388 lappend data(ShareItems) $type 00389 } 00390 } 00391 } 00392 00393 # Indicate that the share directory have changed 00394 share_changed 00395 00396 # Perform the action 00397 switch $action { 00398 copy { file_transfer $dirname $::tke_home $data(ShareItems) } 00399 share { create_share_dir $data(ShareDirectory) $data(ShareItems) } 00400 } 00401 00402 # Create the share.tkedat file 00403 write_file 00404 00405 } 00406 00407 ###################################################################### 00408 # Load the sharing file. 00409 proc load_file {} { 00410 00411 variable data 00412 00413 # Initialize the share information 00414 set data(ShareDirectory) "" 00415 set data(ShareItems) [list emmet launcher plugins prefs remote sessions snippets templates themes] 00416 00417 # Read in the share data from the file 00418 if {![catch { tkedat::read [file join $::tke_home share.tkedat] } rc]} { 00419 array set data $rc 00420 } 00421 00422 # Update all affected namespaces 00423 share_changed 00424 00425 } 00426 00427 ###################################################################### 00428 # Writes the sharing file. 00429 proc write_file {} { 00430 00431 variable data 00432 00433 tkedat::write [file join $::tke_home share.tkedat] [array get data] 0 00434 00435 } 00436 00437 }