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 }