diff --git a/frontend/README-tcltk b/frontend/README-tcltk index 2b499d96..87e2b9d3 100644 --- a/frontend/README-tcltk +++ b/frontend/README-tcltk @@ -8,6 +8,12 @@ Provided under GNU GPL version 2 or later. xorriso-tcltk is mainly a proof of concept for a frontend that operates xorriso in dialog mode. +Dependencies: +- xorriso ISO 9660 Rock Ridge filesystem manipulator and CD/DVD/BD burn program +- Tcl programming language +- Tk widget toolkit +- optionally the Tcl/Tk package BWidget + It exercises several fundamental gestures of communication: - connecting via two pipes - sending commands @@ -47,6 +53,9 @@ in order to get the particular help text for that element. There is no need to close the help window. Just click another element to get another help text. +The "Help" button in the upper right corner gives a short overview and +instructions for several common use cases. + Program start options @@ -55,7 +64,7 @@ So here is the output of xorriso-tcltk --help : ------------------------------------------------------------------------ Usage: - frontend/xorriso-tcltk [options] + /usr/bin/xorriso-tcltk [options] Options: All options must be given with two dashes ("--option") in order to distinguish them from any options of the Tcl shell. @@ -76,6 +85,9 @@ Options: --silent_start Do not issue the start message xorriso-tcltk-version. This works only if --silent_start is the first argument. + --no_bwidget + Do not try to load the Tcl/Tk package BWidget which is + a prerequisite for the "/" file browser buttons. --geometry {+|-}X{+|-}Y Sets the position of the main window. --click_to_focus diff --git a/frontend/xorriso-tcltk b/frontend/xorriso-tcltk index 595a7960..ef5dc2b3 100755 --- a/frontend/xorriso-tcltk +++ b/frontend/xorriso-tcltk @@ -42,6 +42,9 @@ proc print_usage {argv0} { puts stderr " --silent_start" puts stderr " Do not issue the start message xorriso-tcltk-version." puts stderr " This works only if --silent_start is the first argument." + puts stderr " --no_bwidget" + puts stderr " Do not try to load the Tcl/Tk package BWidget which is" + puts stderr " a prerequisite for the \"/\" file browser buttons." puts stderr " --geometry {+|-}X{+|-}Y" puts stderr " Sets the position of the main window." puts stderr " --click_to_focus" @@ -144,11 +147,14 @@ set devlist "" # Intermediate storage for messages until the GUI is up with .msglist box set pre_msglist "" -# Whether overwriting of files in ISO and on disk is allowed -set overwrite_files 1 +# Whether overwriting of files in the ISO model is allowed +set overwrite_iso_files 1 -# If overwrite_files is 1 : Wether overwriting of ISO directories is allowed -set overwrite_dirs 0 +# If overwrite_iso_files is 1: Wether overwriting of ISO directories is allowed +set overwrite_iso_dirs 0 + +# Whether overwriting of files on disk is allowed +set overwrite_disk_files 0 # The file where to log commands and replies for debugging purposes set log_file "" @@ -157,6 +163,9 @@ set log_conn stderr # Whether to log all commands and replies to the log_file set logging 0 +# The result of the most recent isofs_ls run +set isofs_ls_result "" + # xorriso specific constants @@ -238,10 +247,6 @@ proc await_all_replies {} { log_puts "$line" if {[string range "$line" 0 0] == "M"} { if {[string range "$line" 5 end] == "$mark_count"} { - - # <<< - # puts stderr "sync mark" - break } else { # outdated mark message @@ -253,16 +258,6 @@ proc await_all_replies {} { } display_busy 0 - - # <<< debug - # for {set i 0} {$i < $info_count} {incr i} { - # set line [lindex "$info_list" $i] - # puts stderr "info $i : $line" - # } - # for {set i 0} {$i < $result_count} {incr i} { - # set line [lindex "$result_list" $i] - # puts stderr "result $i : $line" - # } } @@ -294,20 +289,12 @@ proc de_pkt_line {line} { lappend result_list "$emerging_result" incr result_count display_msg "$emerging_result" - - # <<< - # puts stderr "result: $emerging_result" - set emerging_result "" } else { lappend info_list "$emerging_info" incr info_count display_msg "$emerging_info" scan_info_for_event "$emerging_info" - - # <<< - # puts stderr "info: $emerging_info" - set emerging_info "" } } @@ -370,10 +357,6 @@ proc de_sieve {} { for {set i 2} {$i < [llength "$result_list"]} {incr i} { set line "" set num_lines [lindex "$result_list" $i] - - # <<< - # puts stderr "de_sieve : num_lines = $num_lines" - for {set j 0} {$j < "$num_lines"} {incr j} { incr i set line "$line[lindex "$result_list" $i]" @@ -647,8 +630,6 @@ proc set_inquired_dev {} { # help of xorriso.) # proc inquire_dev {} { - global result_list - set disp_en_mem [set_display_msg 0] send_marked_cmd "-status -dev" handle_result_list set_inquired_dev "''" "''" 2 0 @@ -658,6 +639,45 @@ proc inquire_dev {} { } +# Parse-by-xorriso handler function for proc isofs_ls +# +proc isofs_ls_handler {} { + global result_list isofs_ls_result + + if {[lindex "$result_list" 0] == "total"} {return ""} + lappend isofs_ls_result \ + "[string range [lindex "$result_list" 0] 0 0] [lindex "$result_list" 8]" +} + + +# Produce a list of all files in a directory of the ISO model +# +proc isofs_ls {dir} { + global isofs_ls_result + + set isofs_ls_result "" + set disp_en_mem [set_display_msg 0] + send_marked_cmd "-lsl [make_text_shellsafe "$dir"]" + handle_result_list isofs_ls_handler "''" "''" 0 0 + set_display_msg "$disp_en_mem" + return "$isofs_ls_result" +} + + +# Tells the file type of an absolute path in the ISO model. +# Indicator characters like with ls -l. Empty text means non existing file. +# +proc isofs_filetype {path} { + global result_list + + set disp_en_mem [set_display_msg 0] + send_marked_cmd "-lsdl [make_text_shellsafe "$path"]" + set_display_msg "$disp_en_mem" + if {[llength "$result_list"] < 1} {return ""} + return [string range [lindex "$result_list" 0] 0 0] +} + + # Verify that the connected process runs a xorriso program that is modern # enough. This is done before sending xorriso the setup commands. # @@ -729,6 +749,7 @@ proc setup_xorriso {} { set cmd "$cmd -osirrox on" set cmd "$cmd -iso_rr_pattern off" set cmd "$cmd -disk_pattern off" + set cmd "$cmd -follow mount:limit=100" send_marked_cmd "$cmd" @@ -763,10 +784,6 @@ proc start_bulkparse {prefix separators max_words flag num_lines} { set bulk_parse_num_texts "$num_lines" if {"$bulk_parse_mode" == 1} { set cmd "-msg_op parse_bulk \"$prefix $separators $max_words $flag $num_lines\"" - - # <<< - # puts stderr "start_bulkparse : $cmd" - send_async_cmd "$cmd" # Do not wait for mark } @@ -791,10 +808,6 @@ proc submit_bulkparse {text} { if {"$bulk_parse_mode" == 0} { set cmd "-msg_op parse \"$bulk_parse_prefix $bulk_parse_separators $bulk_parse_max_words $bulk_parse_flag $num_lines\"" - - # <<< - # puts stderr "submit_bulkparse: cmd = $cmd" - send_async_cmd "$cmd" } else { log_puts ">>>>> $num_lines" @@ -804,11 +817,7 @@ proc submit_bulkparse {text} { puts $cmd_conn "$text" flush $cmd_conn - # <<< - # puts stderr "submit_bulkparse: text sent" - if {"$bulk_parse_mode" != 1} { - set loop_limit 2 while {"$result_count" < "$loop_limit"} { set ret [gets "$reply_conn" line] @@ -821,14 +830,9 @@ proc submit_bulkparse {text} { if {"$result_count" == 2} { set num_replies [lindex "$result_list" 1] set loop_limit [expr "$num_replies * 2 + 2"] - - # <<< - # puts stderr "submit_bulkparse: loop_limit = $loop_limit" - } } de_sieve - } set_display_msg "$disp_en_mem" } @@ -844,16 +848,11 @@ proc read_bulkparse {handler_proc num_texts} { if {"$bulk_parse_mode" != 1} { return ""} set disp_en_mem [set_display_msg 0] - - # <<< - # puts stderr "read_bulkparse : handler_proc = $handler_proc , num_texts = $num_texts" - for {set i 0} {"$i" < "$num_texts"} {incr i} { clear_reply_lists read_parse_reply $handler_proc } - set_display_msg "$disp_en_mem" } @@ -876,10 +875,6 @@ proc read_parse_reply {} { if {"$result_count" == 2} { set num_replies [lindex "$result_list" 1] set loop_limit [expr "$num_replies * 2 + 2"] - - # <<< - # puts stderr "submit_bulkparse: loop_limit = $loop_limit" - } } de_sieve @@ -891,7 +886,7 @@ proc read_parse_reply {} { # This is used to split the result lines of -lsl into words from which # handler proc isolist_parse_handler picks the info which it displays # in .stbox isolist . -# Note that iall parameters must be xorriso words. E.g. empty prefix or +# Note that all parameters must be xorriso words. E.g. empty prefix or # separator have to be submitted as tcl string "''" rather than "". # proc handle_result_list {handler_proc \ @@ -907,16 +902,9 @@ proc handle_result_list {handler_proc \ set chunk_size "$raw_line_count" } start_bulkparse "$prefix" "$separators" "$max_words" "$flag" "$chunk_size" - - # <<< - # puts stderr "isodir_return : chunk_size = $chunk_size" - set submit_count 0 set submit_in_chunk_count 0 foreach i "$raw_list" { - # <<< - # puts stderr "isodir_return : submit_bulkparse $i" - submit_bulkparse "$i" incr submit_count incr submit_in_chunk_count @@ -924,10 +912,6 @@ proc handle_result_list {handler_proc \ $handler_proc } if {"$bulk_parse_mode" == 1 && "$submit_in_chunk_count" == "$chunk_size"} { - - # <<< - # puts stderr "isodir_return : submit_in_chunk_count = $submit_in_chunk_count" - read_bulkparse "$handler_proc" "$chunk_size" set todo [expr "$raw_line_count" - "$submit_count"] if {"$todo" <= 0} { @@ -940,10 +924,6 @@ proc handle_result_list {handler_proc \ } start_bulkparse "$prefix" "$separators" "$max_words" "$flag" \ "$chunk_size" - - # <<< - # puts stderr "isodir_return : further chunk_size = $chunk_size" - set submit_in_chunk_count 0 } } @@ -993,7 +973,7 @@ set burn_write_defect_mgt 0 # Answer of yes/no window set answer_of_yesno "" -# The local filesystem address to be mapped into isodir_adr +# The hard disk filesystem address to be mapped into isodir_adr set insert_from_adr "" # Whether to insert with leafname of insert_from_adr underneath isodir_adr @@ -1004,7 +984,7 @@ set insert_underneath 1 # rather than isodir_adr set insert_at_selected 0 -# The local filesystem address to which to extract from isodir_adr +# The hard disk filesystem address to which to extract from isodir_adr set extract_to_adr "" # Whether to insert with leafname of insert_from_adr underneath isodir_adr @@ -1029,6 +1009,26 @@ set ack_window_is_active 0 set yesno_window_is_active 0 set help_window_is_active 0 +# Whether the help window already has a scroll bar +set help_window_has_scroll 0 + +# Whether there is the BWidget package available: 0=unknown, 1=yes, -1=banned +# +set have_bwidget 0 +set bwidget_version "" + +# Whether the .browse_disk_window is already displayed +set browse_disk_window_is_active 0 +set browse_disk_window_var "" + +# Whether the .browse_iso_window is already displayed +set browse_iso_window_is_active 0 +set browse_iso_window_var "" + +# Whether a selection event in the file browser shal trigger a Return in +# the associated text field after setting the selected value. +set browse_select_is_return 1 + # ------ GUI callback procedures ---- @@ -1042,6 +1042,7 @@ proc cmdline_return {} { global highest_cmd_sev_msg reset_highest_cmd_sev + set_display_msg 1 send_marked_cmd "$cmdline" set cmdline "" @@ -1243,8 +1244,8 @@ proc scan_for_drives {} { update idletasks # Command -devices drops all aquired drives - refresh_indev refresh_outdev + refresh_indev } @@ -1365,6 +1366,12 @@ proc isodir_return {caller} { } normalize_isodir_adr + set file_type [isofs_filetype "$isodir_adr"] + if {"$file_type" != "d" && "$file_type" != ""} { + .isolist insert end "@@@ exists but is not a directory @@@" + set isodir_is_pwd 0 + return "" + } set disp_en_mem [set_display_msg 0] set highest_cmd_sev_mem "$highest_cmd_sev" set highest_cmd_sev_msg_mem "$highest_cmd_sev_msg" @@ -1426,10 +1433,6 @@ proc pick_isodir {} { return "" } set idx [lindex "$selected" 0] - - # <<< - # puts stderr "pick_isodir: lindex \$isolist_types $idx = [lindex $isolist_types "$idx"]" - if {[lindex "$isolist_types" "$idx"] != "d"} { xorriso_tcltk_errmsg \ "xorriso-tcltk : SORRY : You may only double-click a directory" @@ -1517,15 +1520,11 @@ proc isomanip_mv {} { } } - enforce_overwrite_settings + enforce_overwrite_settings "isofs" reset_highest_cmd_sev foreach i "$selected" { set name [lindex "$isolist_names" "$i"] if {"$isodir_is_pwd" == 0} { - - # <<< - # puts stderr "isomanip_mv : isodir_is_pwd = $isodir_is_pwd" - set name [combine_dir_and_name "$isodir_adr" "$name"] } @@ -1549,6 +1548,7 @@ proc isomanip_mv {} { if {[llength "$selected"] == 1} { set isodir_return_name [path_touches_isodir "$target"] } + browse_iso_refresh isodir_return "isomanip_mv" } @@ -1590,6 +1590,7 @@ proc isomanip_mkdir {} { restore_isolist_selection } } + browse_iso_refresh } @@ -1616,6 +1617,7 @@ proc isomanip_rm_r {} { } send_marked_cmd "-rm_r [make_text_shellsafe "$name"] --" } + browse_iso_refresh isodir_return "isomanip_rm_r" } @@ -1785,19 +1787,25 @@ proc calm_drives {} { # proc burn_write_image {} { global burn_write_image_adr burn_write_close outdev_adr outdev_medium_status - global outdev_profile burn_write_tao burn_write_defect_mgt + global outdev_profile burn_write_tao burn_write_defect_mgt indev_adr + + inquire_dev + if {"$indev_adr" != ""} { + xorriso_tcltk_errmsg \ + "xorriso-tcltk : SORRY : You may not have an input drive open when writing an image file" + return "" + } if {[assert_outdev "writing an image file"] <= 0} {return ""} if {"$burn_write_image_adr" == ""} { xorriso_tcltk_errmsg \ - "xorriso-tcltk : SORRY : You have to set an image file address in the local filesystem first" + "xorriso-tcltk : SORRY : You have to set an image file address in the hard disk filesystem first" return "" } - if {"$outdev_medium_status" != "blank" && \ - "$outdev_medium_status" != "appendable"} { + if {"$outdev_medium_status" != "blank"} { xorriso_tcltk_errmsg \ - "xorriso-tcltk : SORRY : Medium in output drive is neither blank nor appendable" + "xorriso-tcltk : SORRY : You must have a blank medium in the output drive for burning an image data file" return "" } if {[file readable "$burn_write_image_adr"] == 0 || \ @@ -1812,7 +1820,7 @@ proc burn_write_image {} { set cmd "-as cdrecord -v" if {[regexp "^CD" "$outdev_profile"] == 1 && \ - "$outdev_medium_status" == "appendable"} { + ( "$outdev_medium_status" == "appendable" || "$burn_write_tao" == 1 )} { set cmd "$cmd padsize=150s" } set cmd "$cmd dev=[make_text_shellsafe "$outdev_adr"]" @@ -1881,7 +1889,7 @@ proc refresh_avail {} { # proc handle_iso_overwriting {target target_ftype from_is_dir selected_adr selected_ftype} { - global overwrite_dirs + global overwrite_iso_dirs # >>> Nicer would be: # >>> Check if any file will get overwritten. Not only the direct target. @@ -1894,7 +1902,7 @@ proc handle_iso_overwriting {target target_ftype from_is_dir "'$target'\n\nReally merge with existing ISO directory ?"] \ != 1} { return "0" } } else { - if {"$overwrite_dirs" == 1} { + if {"$overwrite_iso_dirs" == 1} { if {[window_yesno \ "'$target'\n\nReally overwrite existing ISO directory ?"] \ != 1} { return "0" } @@ -1924,12 +1932,12 @@ proc handle_iso_overwriting {target target_ftype from_is_dir # proc insert_from {} { global insert_from_adr isodir_adr isolist_names isodir_return_name - global insert_at_selected insert_underneath overwrite_dirs + global insert_at_selected insert_underneath if {[assert_iso_image 1] == 0} {return ""} if {"$insert_from_adr" == ""} { xorriso_tcltk_errmsg \ - "xorriso-tcltk : SORRY : You have to set a source address in the local filesystem first" + "xorriso-tcltk : SORRY : You have to set a source address in the hard disk filesystem first" return "" } set selected_ftype "" @@ -1946,7 +1954,7 @@ proc insert_from {} { } else { set target "$isodir_adr" } - set from_is_dir [file isdirectory "$insert_from_adr"] + set from_is_dir [localfs_isdir "$insert_from_adr"] set name [file tail "$insert_from_adr"] if {"$insert_underneath" == 1 || "$from_is_dir" == 0} { set target [combine_dir_and_name "$target" "$name"] @@ -1965,7 +1973,7 @@ proc insert_from {} { set preserve_selection 1 } reset_highest_cmd_sev - enforce_overwrite_settings + enforce_overwrite_settings "isofs" send_marked_cmd "-map [make_text_shellsafe "$insert_from_adr"] [make_text_shellsafe "$target"]" if {"$preserve_selection" == 1} { @@ -1975,10 +1983,11 @@ proc insert_from {} { if {"$preserve_selection" == 1} { restore_isolist_selection } + browse_iso_refresh } -# Copy a file out of the ISO image model to the local disk filesystem. +# Copy a file out of the ISO image model to the hard disk filesystem. # The meta data stem from the ISO model tree. The content data are usually # read from the input drive. # Called when button "Extract to disk:" is hit. @@ -1991,7 +2000,7 @@ proc extract_to {} { if {[assert_iso_image 1] == 0} {return ""} if {"$extract_to_adr" == ""} { xorriso_tcltk_errmsg \ - "xorriso-tcltk : SORRY : You have to set a target address in the local filesystem first" + "xorriso-tcltk : SORRY : You have to set a target address in the hard disk filesystem first" return "" } set sources "" @@ -2011,8 +2020,43 @@ proc extract_to {} { } else { set sources [list "$isodir_adr"] } + + # Warn of directory mergers. They can cause mess-up on hard disk. + set merge_counter 0 + set merge_source "" + set merge_target "" + foreach i "$sources" { + if {[isofs_filetype "$i"] != "d"} { + continue + } + if {"$extract_underneath" == 1} { + set name [file tail "$i"] + set target [combine_dir_and_name "$extract_to_adr" "$name"] + } else { + set target "$extract_to_adr" + } + if {[localfs_isdir "$target"] == 1} { + incr merge_counter + set merge_source "$i" + set merge_target "$target" + } + } + if {"$merge_counter" > 0} { + if {"$sources" == "/" || "$sources" == "{}"} { + if {[window_yesno "Really unpack ISO root directory into hard disk directory\n$merge_target\n?"] \ + != 1} { return "" } + } else { + set and_others "" + if {"$merge_counter" > 1} { + set and_others "and do so at $merge_counter more occasions" + } + if {[window_yesno "Really unpack ISO directory\n [lindex "$sources" 0]\n into hard disk directory\n $merge_target\n$and_others ?"] \ + != 1} { return "" } + } + } + reset_highest_cmd_sev - enforce_overwrite_settings + enforce_overwrite_settings "localfs" set disp_en_mem [set_display_msg 0] if {"$extract_auto_chmod" == 1} { send_marked_cmd "-osirrox on:sort_lba_on:auto_chmod_on" @@ -2036,26 +2080,35 @@ proc extract_to {} { } send_marked_cmd "-extract [make_text_shellsafe "$i"] [make_text_shellsafe "$target"]" } + browse_tree_populate "localfs" } # Send the currently chosen -overwrite settings of the checkbuttons -# "Overwrite files" and "Overwrite ISO dirs". +# "Overwrite ISO files", "Overwrite ISO dirs", "Overwrite disk files". # Called before operations which could overwrite files in ISO model -# or in local disk filesystem. +# or in the hard disk filesystem. # I.e. any -overwrite command sent via the "Command:" text field will not # be able to override the checkbuttons. # -proc enforce_overwrite_settings {} { - global overwrite_files overwrite_dirs +proc enforce_overwrite_settings {which_fs} { + global overwrite_iso_files overwrite_iso_dirs overwrite_disk_files - if {"$overwrite_files" == 0} { - set mode "off" - } else { - if {"$overwrite_dirs" == 0} { - set mode "nondir" + if {"$which_fs" == "isofs"} { + if {"$overwrite_iso_files" == 0} { + set mode "off" } else { + if {"$overwrite_iso_dirs" == 0} { + set mode "nondir" + } else { + set mode "on" + } + } + } else { + if {"$overwrite_disk_files" == 1} { set mode "on" + } else { + set mode "off" } } set disp_en_mem [set_display_msg 0] @@ -2113,6 +2166,272 @@ proc assert_no_changes {} { } +# ------ A primitive file tree browser for hard disk filesystem and ISO model + +# Write a directory content list into a Tree widget +# +proc browse_tree_fill_dir {tr parent children} { + + if {"$parent" == "/"} { + set parent_name root + } else { + set parent_name "$parent" + } + if {[$tr exists "$parent_name"] == 0} {return ""} + + $tr delete [$tr nodes "$parent_name"] + + foreach i "$children" { + set name [string range "$i" 2 end] + set adr [combine_dir_and_name "$parent" "$name"] + $tr insert end "$parent_name" "$adr" -text "$name" + if {[string range "$i" 0 0] == "d"} { + set dir_dummy [combine_dir_and_name "$adr" "dir_dummy"] + $tr insert end "$adr" "$dir_dummy" -text "dir_dummy" + } + } +} + + +# The command to be executed when the user selects a node. +# +proc browse_tree_select {adr_var_name tr selected} { + global extract_to_adr insert_from_adr burn_write_image_adr isodir_adr + global isomanip_move_target indev_adr outdev_adr + global browse_select_is_return + + if {[llength "$selected"] == 0} { + set value "" + } else { + set value [lindex "$selected" 0] + } + if {"$adr_var_name" == "burn_write_image_adr"} { + set burn_write_image_adr "$value" + if {"$browse_select_is_return" == 1} {burn_write_image} + } + if {"$adr_var_name" == "extract_to_adr"} { + set extract_to_adr "$value" + if {"$browse_select_is_return" == 1} {extract_to} + } + if {"$adr_var_name" == "insert_from_adr"} { + set insert_from_adr "$value" + if {"$browse_select_is_return" == 1} {insert_from} + } + if {"$adr_var_name" == "isodir_adr"} { + set isodir_adr "$value" + if {"$browse_select_is_return" == 1} {isodir_return "browse_tree_select"} + } + if {"$adr_var_name" == "isomanip_move_target"} { + set isomanip_move_target "$value" + # No browse_select_is_return because the field is shared between buttons + } + if {"$adr_var_name" == "indev_adr"} { + set indev_adr "$value" + if {"$browse_select_is_return" == 1} {indev_return} + } + if {"$adr_var_name" == "outdev_adr"} { + set outdev_adr "$value" + if {"$browse_select_is_return" == 1} {outdev_return} + } +} + + +# The command to be executed when the user closes a directory node. +# It replaces the directory content list by a single dummy item. +# +proc browse_tree_close_dir {tr name} { + browse_tree_fill_dir $tr "$name" "{? dir_dummy}" +} + + +# Delete the old content of the browse window and display the freshly +# obtained current state down to the current address in the field variable. +# +proc browse_tree_populate {which_fs} { + global browse_disk_window_var browse_iso_window_var + global browse_iso_window_is_active browse_disk_window_is_active + global extract_to_adr insert_from_adr burn_write_image_adr isodir_adr + global isomanip_move_target indev_adr outdev_adr + global browse_select_is_return + + if {"$which_fs" == "isofs"} { + if {"$browse_iso_window_is_active" == 0} {return ""} + set w {.browse_iso_window} + set open_dir_cmd "browse_iso_open_dir" + set adr_var "$browse_iso_window_var" + } else { + if {"$browse_disk_window_is_active" == 0} {return ""} + set w {.browse_disk_window} + set open_dir_cmd "browse_disk_open_dir" + set adr_var "$browse_disk_window_var" + } + + # Variable indirection + eval set adr $$adr_var + + # Install root level + $open_dir_cmd $w.tree "/" + + # Set $adr as current address + set comps [split "$adr" "/"] + # Install the stack of directories above current address + set path "/" + foreach i "$comps" { + if {"$i" == ""} { + continue + } + set path [combine_dir_and_name "$path" "$i"] + $open_dir_cmd $w.tree "$path" + catch { + $w.tree opentree "$path" 0 + $w.tree see "$path" + } + } +} + + +# Destroy the hard disk browser pop-up window. +# +proc destroy_browse_disk {w} { + global browse_disk_window_is_active + + if {"$w" != "" && "$browse_disk_window_is_active" == 1} { + destroy "$w" + } + set browse_disk_window_is_active 0 +} + + +# The command to be executed when the user opens a directory node in +# the hard disk filesystem. +# +proc browse_disk_open_dir {tr name} { + if {[localfs_isdir "$name"] != 1} {return ""} + set lslist [localfs_ls "$name"] + browse_tree_fill_dir $tr "$name" "$lslist" +} + + +# Refresh the content of a possibly displayed tree browser for hard disk +# +proc browse_disk_refresh {} { + browse_tree_populate "localfs" +} + + +# The command to be executed when the user opens a directory node in +# the ISO model. +# +proc browse_iso_open_dir {tr name} { + if {[isofs_filetype "$name"] != "d"} {return ""} + set lslist [isofs_ls "$name"] + browse_tree_fill_dir $tr "$name" "$lslist" +} + + +# Destroy the ISO browser pop-up window. +# +proc destroy_browse_iso {w} { + global browse_iso_window_is_active + + if {"$w" != "" && "$browse_iso_window_is_active" == 1} { + destroy "$w" + } + set browse_iso_window_is_active 0 +} + + +# Refresh the content of a possibly displayed tree browser for ISO model +# +proc browse_iso_refresh {} { + browse_tree_populate "isofs" +} + + +# Open a file browser window for hard disk filesystem or ISO model +# +proc browse_tree {adr_var which_fs} { + upvar $adr_var adr + global have_bwidget browse_disk_window_is_active browse_iso_window_is_active + global browse_disk_window_var browse_iso_window_var + + set button_color "grey" + + set old_geometry "" + if {"$which_fs" == "isofs"} { + set w {.browse_iso_window} + set window_is_active "$browse_iso_window_is_active" + set title_name "xorriso-tcltk ISO filesystem browser" + set open_dir_cmd "browse_iso_open_dir" + set destroy_cmd "destroy_browse_iso" + if {"$browse_iso_window_var" != "$adr_var" && "$window_is_active" == 1} { + set old_geometry [wm geometry $w] + destroy_browse_iso $w + set window_is_active 0 + } + set browse_iso_window_var "$adr_var" + set browse_iso_window_is_active 1 + } else { + set w {.browse_disk_window} + set window_is_active "$browse_disk_window_is_active" + set title_name "xorriso-tcltk disk filesystem browser" + set open_dir_cmd "browse_disk_open_dir" + set destroy_cmd "destroy_browse_disk" + if {"$browse_disk_window_var" != "$adr_var" && "$window_is_active" == 1} { + set old_geometry [wm geometry $w] + destroy_browse_disk $w + set window_is_active 0 + } + set browse_disk_window_var "$adr_var" + set browse_disk_window_is_active 1 + } + set re_use_widgets 0 + if {"$window_is_active" == 0} { + toplevel $w -borderwidth 10 -class Browse + wm title $w "$title_name" + if {"$old_geometry" != ""} { + wm geometry $w "$old_geometry" + } + } else { + set re_use_widgets 1 + } + if {"$re_use_widgets" == 0} { + # BWidget Tree + frame $w.tree_frame + frame $w.tree_frame_y + Tree $w.tree -width 80 \ + -opencmd "$open_dir_cmd $w.tree" \ + -closecmd "browse_tree_close_dir $w.tree" \ + -selectcommand "browse_tree_select $adr_var" \ + -yscrollcommand "$w.treescroll_y set" \ + -xscrollcommand "$w.treescroll_x set" + scrollbar $w.treescroll_y -command "$w.tree yview" + scrollbar $w.treescroll_x -orient horizontal -command "$w.tree xview " + pack $w.tree -in $w.tree_frame_y -side left -expand 1 -fill both + pack $w.treescroll_y -in $w.tree_frame_y -side left -fill y + pack $w.tree_frame_y $w.treescroll_x -in $w.tree_frame \ + -side top -expand 1 -fill both + +# >>> BAUSTELLE + + # >>> Need key bindings for navigation and selection + + # >>> Need help bindings + + button $w.close -text "Close" -command "$destroy_cmd $w" \ + -background "$button_color" + + # >>> Need "Up" button + + pack $w.tree_frame $w.close -side top + + } else { + raise $w + } + browse_tree_populate "$which_fs" +} + + # ------ GUI display procedures ---- @@ -2296,33 +2615,47 @@ proc window_ack {question button_color where} { # Destroy the help pop-up window. # proc destroy_help {w} { - global help_window_is_active + global help_window_is_active help_window_has_scroll if {"$w" != ""} { destroy "$w" } set help_window_is_active 0 + set help_window_has_scroll 0 +} + + +proc surround_text {text} { + return "\n\n [string map {\n "\n "} "$text"]\n" } # Pop-up a window which shows a help text and a Close button. # proc window_help {about_what button_color} { - global help_window_is_active + global help_window_is_active help_window_lines help_window_has_scroll + global help_window_border_width global .help_window - - set color "#F0F0F0" - - set re_use_widgets 0 set w {.help_window} + + # Giving the help text some distance from the border decorations + set line_width 82 + set helptext "\n\n [string map {\n "\n "} [tell_helptext "$about_what"]]\n" + + if {[count_newlines "$helptext"] >= "$help_window_lines"} { + if {"$help_window_is_active" == 1 && "$help_window_has_scroll" == 0} { + destroy_help $w + } + set help_window_has_scroll 1 + } + set re_use_widgets 0 if {"$help_window_is_active" == 0} { - toplevel $w -borderwidth 20 -class Help + toplevel $w -borderwidth "$help_window_border_width" -class Help wm title $w "xorriso-tcltk help text" set help_window_is_active 1 } else { set re_use_widgets 1 } - set helptext [tell_helptext "$about_what"] if {"$re_use_widgets" == 1} { $w.text configure -state normal $w.text delete 1.0 end @@ -2332,12 +2665,24 @@ proc window_help {about_what button_color} { # wm geometry $w +0+0 set destroy_cmd "destroy_help $w" - text $w.text -width 80 -height 20 -relief flat -borderwidth 0 + frame $w.text_frame + text $w.text -width "$line_width" -height "$help_window_lines" \ + -relief flat -borderwidth 0 $w.text insert end "$helptext" + pack $w.text -in $w.text_frame -side left -expand 1 -fill both + if {"$help_window_has_scroll" == 1} { + scrollbar $w.scroll_y -command "$w.text yview" + $w.text configure -yscrollcommand "$w.scroll_y set" + bind_listbox_keys $w.text "$help_window_lines" "text" + pack $w.scroll_y -in $w.text_frame -side left -fill y + } + button $w.close -text "Close" -command "$destroy_cmd" \ -background "$button_color" - pack $w.text -side top -expand 1 -fill both - pack $w.close -side top + pack $w.text_frame -side top -expand 1 -fill both + frame $w.middle_spacer -height 6 + frame $w.bottom_spacer -height 6 + pack $w.middle_spacer $w.close $w.bottom_spacer -side top } $w.text configure -state disabled } @@ -2363,6 +2708,26 @@ proc display_busy {state} { } +# Tries to make use of the BWidget package for getting its Tree widget +# +proc check_for_bwidget {} { + global have_bwidget bwidget_version + + if {"$have_bwidget" == 0} { + catch { + set bwidget_version [package require BWidget] + set have_bwidget 1 + } + } +} + + +proc browser_dummy {} { + window_ack \ + "The file browser cannot be used because Tcl/Tk package \"BWidget\" is not loaded" "grey" "toplevel" +} + + # ------ Building GUI components ------ # ------ GUI layout parameters ------ @@ -2394,6 +2759,12 @@ set export_msg_selection true # Wether the item lists shall export their selection set export_selection false +# The number of lines in the display of the help texts +set help_window_lines 18 + +# The distance of the help text from the help window border +set help_window_border_width 0 + # -------- GUI definition procedures @@ -2405,6 +2776,8 @@ proc init_gui {} { global .isobox .localfs global main_framerelief main_borderwidth + check_for_bwidget + # Main grouping frames frame .connection_block \ -relief "$main_framerelief" -borderwidth "$main_borderwidth" @@ -2521,14 +2894,10 @@ proc init_msgbox {} { listbox .msglist -height $msglist_lines -selectmode extended \ -yscrollcommand ".msgscroll set" \ -exportselection $export_msg_selection - bind_listbox_keys ".msglist" "$msglist_lines" + bind_listbox_keys ".msglist" "$msglist_lines" "listbox" bind_help .msglist "message box" set msglist_running 1 foreach i "$pre_msglist" { - - # <<< - # puts stderr "init_msgbox : pre_msglist : $i" - display_msg "$i" } scrollbar .msgscroll -command ".msglist yview" @@ -2618,7 +2987,7 @@ proc init_drivebox {} { listbox .drivelist -height $drivelist_lines -selectmode extended \ -yscrollcommand ".drivescroll set" \ -exportselection $export_selection - bind_listbox_keys ".drivelist" "$drivelist_lines" + bind_listbox_keys ".drivelist" "$drivelist_lines" "listbox" bind_help .drivelist "drivelist" scrollbar .drivescroll -command ".drivelist yview" @@ -2693,11 +3062,18 @@ proc init_indev {} { bind_help .indev_label "Input drive or image" entry .indev_entry -width 40 -relief sunken -bd 1 \ -textvariable indev_adr + bind_entry_keys .indev_entry {indev_return} bind_help .indev_entry "Input drive or image" label .indev_summary -width 65 -text "" -relief ridge -borderwidth 2 bind_help .indev_summary "input drive info" - pack .indev_eject .indev_label .indev_entry .indev_summary \ + create_browser_button .indev_browse_button \ + "indev_adr" "localfs" "Browse disk (indev)" + + pack .indev_eject .indev_label .indev_entry \ + -in .indev -side left -expand 1 -fill both + pack .indev_browse_button -in .indev -side left + pack .indev_summary \ -in .indev -side left -expand 1 -fill both } @@ -2718,9 +3094,14 @@ proc init_outdev {} { -textvariable outdev_adr bind_entry_keys .outdev_entry {outdev_return} bind_help .outdev_entry "Output drive or image" + create_browser_button .outdev_browse_button \ + "outdev_adr" "localfs" "Browse disk (outdev)" label .outdev_summary -width 65 -text "" -relief ridge -borderwidth 2 bind_help .outdev_summary "output drive info" - pack .outdev_eject .outdev_label .outdev_entry .outdev_summary \ + pack .outdev_eject .outdev_label .outdev_entry \ + -in .outdev -side left -expand 1 -fill both + pack .outdev_browse_button -in .outdev -side left + pack .outdev_summary \ -in .outdev -side left -expand 1 -fill both } @@ -2752,6 +3133,8 @@ proc init_burn {} { -textvariable burn_write_image_adr bind_entry_keys .burn_write_image_entry {burn_write_image} bind_help .burn_write_image_entry "Burn image file:" + create_browser_button .burn_image_browse_button \ + "burn_write_image_adr" "localfs" "Browse disk (burn image)" checkbutton .burn_write_close -text "Close" \ -indicatoron 1 -selectcolor "" \ -relief ridge -borderwidth 2 \ @@ -2775,6 +3158,7 @@ proc init_burn {} { .burn_write_defect_mgt \ .burn_write_image .burn_write_image_entry \ -in .burn -side left -expand 1 -fill both + pack .burn_image_browse_button -in .burn -side left } @@ -2795,6 +3179,8 @@ proc init_isobox {} { -textvariable isodir_adr bind_entry_keys .isodir_entry {isodir_return "isodir_entry"} bind_help .isodir_entry "ISO directory:" + create_browser_button .isodir_browse_button \ + "isodir_adr" "isofs" "Browse ISO (isodir)" button .isodir_verify -text "Verify" -command {isodir_verify} bind_help .isodir_verify "Verify" button .isodir_up -text "Up" -command {isodir_up} @@ -2805,7 +3191,7 @@ proc init_isobox {} { -in .isodir -side left pack .isodir_entry \ -in .isodir -side left -expand 1 -fill both - pack .isodir_up2 .isodir_verify \ + pack .isodir_browse_button .isodir_up2 .isodir_verify \ -in .isodir -side left frame .isolistbox -borderwidth 0 @@ -2814,7 +3200,7 @@ proc init_isobox {} { -xscrollcommand ".isoscroll_x set" \ -exportselection $export_selection bind_help .isolist "isolist" - bind_listbox_keys ".isolist" "$isolist_lines" + bind_listbox_keys ".isolist" "$isolist_lines" "listbox" scrollbar .isoscroll_y -command ".isolist yview" scrollbar .isoscroll_x -orient horizontal -command ".isolist xview" pack .isolist -in .isolistbox -side left -expand 1 -fill both @@ -2859,16 +3245,20 @@ proc init_isomanip {} { # bind_entry_keys .isomanip_move_target {isomanip_mv} bind_entry_keys .isomanip_move_target "" bind_help .isomanip_move_target "rename and mkdir target" + create_browser_button .isomanip_move_target_button \ + "isomanip_move_target" "isofs" "Browse ISO (move target)" pack .isomanip_prefix .isomanip_verify_button .isomanip_rm_r_button \ - .isomanip_move_button .isomanip_move_target .isomanip_mkdir_button \ + .isomanip_move_button .isomanip_move_target \ + -in .isomanip_move -side left -expand 1 -fill both + pack .isomanip_move_target_button -in .isomanip_move -side left + pack .isomanip_mkdir_button \ -in .isomanip_move -side left -expand 1 -fill both - pack .isomanip_move \ -in .isomanip -side top -expand 1 -fill both } -# The means for interaction of ISO image and local filesystem. +# The means for interaction of ISO image and hard disk filesystem. # proc init_localfs {} { global borderwidth @@ -2892,8 +3282,6 @@ proc init_extract {} { global .extract_button .extract_frame .extract_entry .extract_from_selected global .extract_underneath - # >>> should be some file browser instead of a button-entry pair - frame .extract_frame -borderwidth 0 button .extract_button -text "Extract to disk:" \ -width 17 \ @@ -2903,6 +3291,8 @@ proc init_extract {} { -textvariable "extract_to_adr" bind_entry_keys .extract_entry {extract_to} bind_help .extract_entry "Extract to disk:" + create_browser_button .extract_browse_button \ + "extract_to_adr" "localfs" "Browse disk (extract)" checkbutton .extract_underneath -text "Underneath" \ -indicatoron 1 -selectcolor "" \ -relief ridge -borderwidth 2 \ @@ -2920,28 +3310,29 @@ proc init_extract {} { -in .extract_frame -side left -expand 1 -fill both pack .extract_from_selected .extract_underneath \ -in .extract_frame -side right + pack .extract_browse_button -in .extract_frame -side right } # Some controls which apply to insertion, extraction, or both. # proc init_localfs_aux {} { - global borderwidth + global borderwidth have_bwidget global .localfs_aux_frame - global .overwrite_files_button .overwrite_dir_button .extract_auto_chmod + global .overwrite_iso_files_button .overwrite_dir_button .extract_auto_chmod global .avail_label .avail_label_frame .avail_button frame .localfs_aux_frame -borderwidth 0 - checkbutton .overwrite_files_button -text "Overwrite files" \ + checkbutton .overwrite_iso_files_button -text "Overwrite ISO files" \ -indicatoron 1 -selectcolor "" \ -relief ridge -borderwidth 2 \ - -variable overwrite_files \ + -variable overwrite_iso_files \ -onvalue 1 -offvalue 0 - bind_help .overwrite_files_button "Overwrite files" + bind_help .overwrite_iso_files_button "Overwrite ISO files" checkbutton .overwrite_dir_button -text "Overwrite ISO dirs" \ -indicatoron 1 -selectcolor "" \ -relief ridge -borderwidth 2 \ - -variable overwrite_dirs \ + -variable overwrite_iso_dirs \ -onvalue 1 -offvalue 0 bind_help .overwrite_dir_button "Overwrite ISO dirs" checkbutton .extract_auto_chmod -text "Enforce disk dir write access" \ @@ -2950,8 +3341,24 @@ proc init_localfs_aux {} { -variable extract_auto_chmod \ -onvalue 1 -offvalue 0 bind_help .extract_auto_chmod "Enforce disk dir write access" - pack .overwrite_files_button .overwrite_dir_button .extract_auto_chmod \ + checkbutton .overwrite_disk_files_button -text "Overwrite disk files" \ + -indicatoron 1 -selectcolor "" \ + -relief ridge -borderwidth 2 \ + -variable overwrite_disk_files \ + -onvalue 1 -offvalue 0 + bind_help .overwrite_disk_files_button "Overwrite disk files" + pack .overwrite_iso_files_button .overwrite_dir_button .extract_auto_chmod \ + .overwrite_disk_files_button \ -in .localfs_aux_frame -side left + if {"$have_bwidget" == 1} { + checkbutton .browse_select_is_return -text "File browser Return" \ + -indicatoron 1 -selectcolor "" \ + -relief ridge -borderwidth 2 \ + -variable "browse_select_is_return" \ + -onvalue 1 -offvalue 0 + bind_help .browse_select_is_return "File browser Return" + pack .browse_select_is_return -in .localfs_aux_frame -side left + } button .avail_button -text "Refresh avail:" \ -command {refresh_avail} @@ -2972,8 +3379,6 @@ proc init_insert {} { global .insert_button .insert_from_frame .insert_entry .insert_at_selected global .insert_underneath .insert_frame - # >>> should be some file browser instead of a button-entry pair - frame .insert_frame -borderwidth 0 frame .insert_from_frame -borderwidth 0 button .insert_button -text "Insert from disk:" \ @@ -2984,6 +3389,8 @@ proc init_insert {} { -textvariable "insert_from_adr" bind_entry_keys .insert_entry {insert_from} bind_help .insert_entry "Insert from disk:" + create_browser_button .insert_browse_button \ + "insert_from_adr" "localfs" "Browse disk (insert)" checkbutton .insert_underneath -text "Underneath" \ -indicatoron 1 -selectcolor "" \ -relief ridge -borderwidth 2 \ @@ -2999,6 +3406,7 @@ proc init_insert {} { pack .insert_button -in .insert_from_frame -side left pack .insert_entry \ -in .insert_from_frame -side left -expand 1 -fill both + pack .insert_browse_button -in .insert_from_frame -side left pack .insert_at_selected .insert_underneath \ -in .insert_from_frame -side right pack .insert_from_frame -in .insert_frame -side left -expand 1 -fill both @@ -3007,7 +3415,7 @@ proc init_insert {} { # Set common behavior of listboxes in respect to focus and navigation keys. # -proc bind_listbox_keys {box height} { +proc bind_listbox_keys {box height what_widget} { global click_to_focus if {"$click_to_focus" == 1} { @@ -3019,14 +3427,12 @@ proc bind_listbox_keys {box height} { } # No underlining - $box configure -activestyle none + if {"$what_widget" == "listbox"} { + $box configure -activestyle "none" + } # Need to evaluate all $box and $height at bind-time. Thus "-quotes. bind $box " - - # <<< - # puts stderr \"bind_updown_keys: K = '%K'\" - if {\"%K\" == \"Up\"} { $box yview scroll \"-1\" units } @@ -3035,9 +3441,11 @@ proc bind_listbox_keys {box height} { } if {\"%K\" == \"Prior\"} { $box yview scroll -[expr \"$height\" - 1] units +# $box yview scroll -1 pages } if {\"%K\" == \"Next\"} { $box yview scroll [expr \"$height\" - 1] units +# $box yview scroll 1 pages } if {\"%K\" == \"Home\"} { $box yview 0 @@ -3045,6 +3453,9 @@ proc bind_listbox_keys {box height} { if {\"%K\" == \"End\"} { $box yview end } + + # >>> Do i need this ? + # >>> For now: yes. It prevents double scrolling by PgUp PgDown # Prevent other bindings from being performed break " @@ -3072,16 +3483,175 @@ proc bind_help {to_what help_name} { } +# Create a "/" button and wire it with variable, fileystem type, and help text. +# +proc create_browser_button {button_name var_name which_fs help_name} { + global have_bwidget + + if {"$have_bwidget" == 1} { + button $button_name -text "/" -command "browse_tree $var_name $which_fs" + bind_help $button_name "$help_name" + } else { + button $button_name -text "/" -command "browser_dummy" + if {"$which_fs" == "localfs"} { + bind_help $button_name "Browse disk (dummy)" + } else { + bind_help $button_name "Browse ISO (dummy)" + } + } +} + + # The central storage for help texts. # proc tell_helptext {what} { - global own_version argv0 + global own_version argv0 bwidget_version if {"$what" == "Help"} { return \ "For getting particular help texts: -Click the rightmost mouse button on any button, list, or text field of the GUI. + Click the rightmost mouse button on any button, list box, or text field. + +For a help text about startup options of this frontend, execute in a shell: + + $argv0 --help + +For background info about xorriso and its commands, execute in a shell: + + man xorriso + +----------------------------------------------------------------------------- + +The GUI window is separated into three main areas: + +- The area for connection to xorriso + - shows xorriso messages, + - offers some general activities, + - displays the \"ready/busy\" state of the connection, + - and allows to toggle xorriso commands into the \"Command:\" field. + +- The area for management of drives and ISO image data files + - allows to scan for optical drives, + - to aquire them and load their ISO directory tree, + - to aquire ISO image files from hard disk as pseudo drives like DVD+RW, + - to blank CD-RW, DVD=RW, DVD+RW, BD-RE and format DVD-RW, BD-R, + - to trigger writing of ISO sessions (which get defined in the third area), + - and to burn image data files from hard disk to optical media. + +- The area for inspection, manipulation, and exploitation of the ISO model + - allows to insert directories and files from hard disk into the ISO model, + - to delete and rename file objects in the ISO model, + - to verify data files of loaded ISO directory trees by MD5, + - to extract directories and files from ISO filesystem to hard disk. + +----------------------------------------------------------------------------- + Some Use Cases +----------------------------------------------------------------------------- + +- Burn a directory as only content onto a CD, DVD or BD +- Write a directory as only content to an ISO image data file on hard disk +- Burn an image data file from hard disk onto CD, DVD or BD +- Add more data to an appendable medium or to an ISO image data file +- Extract a directory tree from an ISO filesystem to hard disk + +----------------------------------------------------------------------------- + + Burn a directory as only content onto a CD, DVD or BD + +- Click the \"Scan for drives\" button in the middle area. +- Select a drive and click the \"Pick output drive\" button. +- If the information field in the \"Output drive or Image\" line begins by + \"appendable\" or \"closed\" and if the medium is CD-RW, DVD-RW, DVD+RW, or + BD-RE then click the \"Blank\" button to erase the old data. + (Blanking of \"DVD-RW sequential recording\" will last very long.) +- Go to the \"Insert from disk:\" line in the lower area. + Either toggle in the address of the hard disk directory, + or click on the \"/\" button to the right of the text field to get + a file browser. +- Hit the Return key or click on a name in the browser to schedule + the disk directory for writing to the medium. + (You may of course insert several directories or files that way.) +- Click the \"Write ISO session\" button in the middle area. + Confirm in the \"yes/no\" window that pops up. + Burning will begin (or refuse on unsuitable medium status). +- When the \"busy\" field displays \"ready\" again, you may click \"Eject\". + Desktop drives should then put out the tray with the medium. + +----------------------------------------------------------------------------- + + Write a directory as only content to an ISO image data file on hard disk + +- Toggle the name of the intended data file in the text field of the + \"Output drive or image\" line and hit the Return key. +- If the information field in the \"Output drive or Image\" line begins by + \"appendable\" or \"closed\" then you addressed an existing data file. + Warning: Applying the \"Blank\" button to it would damage its content ! + You probably do not want this in this special use case. +- Go to the \"Insert from disk:\" line in the lower area. + Continue like in the above description for CD, DVD, and BD media. + +----------------------------------------------------------------------------- + + Burn an image data file from hard disk onto CD, DVD or BD + +- Click the \"Scan for drives\" button in the middle area. +- Select a drive and click the \"Pick output drive\" button. +- If the information field in the \"Output drive or Image\" line begins by + \"appendable\" or \"closed\" and if the medium is CD-RW, DVD-RW, DVD+RW, or + BD-RE then click the \"Blank\" button to erase the old data. + (Blanking of \"DVD-RW sequential recording\" will last very long.) +- Go to the text field beneath the \"Burn image file:\" button and toggle + the address of the image file. Or click on the \"/\" button to the right + of the field to get a file browser. +- Hit the Return key in the text field or click on a name in the browser + to initiate the burn run. + Confirm in the \"yes/no\" window that pops up. +- When the \"busy\" field displays \"ready\" again, you may click \"Eject\". + Desktop drives should then put out the tray with the medium. + +----------------------------------------------------------------------------- + + Add more data to an appendable medium or to an ISO image data file + +- Like above, \"Scan for drives\" but click button \"Pick drive for both roles\" + in order to load the directory tree of the existing ISO filesystem. + For an ISO image data file, bring its name into the input fields of both + lines \"Input drive or image\" and \"Output drive or image\". + You should now see in both info fields texts which begin by \"appendable\". +- Go to the \"Insert from disk:\" line in the lower area. + Use the means described in the first use case to add more directories or + data files. +- If you are interested in \"Delete\" or \"Rename to:\" buttons in the + bottom line of the GUI: Click them by the rightmost mouse button to see + their help texts. +- When all intended changes are done: Click \"Write ISO session\" and + confirm in the \"yes/no\" window. + +----------------------------------------------------------------------------- + + Extract a directory tree from an ISO filesystem to hard disk + +- Like above, \"Scan for drives\" but click button \"Pick input drive\" + in order to load the directory tree of the existing ISO filesystem. + For an ISO image data file, bring its name into the input field of the + lines \"Input drive or image\" and \"Output drive or image\". + You should now see in both info fields texts which begin by \"appendable\". +- Go to the \"ISO directory:\" line in the lower area. + Either toggle in the address of the directory you want to extract or + click the \"/\" button to get a file browser. +- Hit the Return key or click on a name in the browser to determine + the ISO directory for writing to the medium. +- Go to the \"Extract to disk:\" line in the lower area. + Either toggle in the address of the hard disk directory, + or click on the \"/\" button to the right of the text field to get + a file browser. +- Hit the Return key or click on a name in the browser to initiate the + extraction run. + If a \"yes/no\" window pops up, consider well whether you are up to + to shooting your own foot right now. + Enable the \"Overwrite disk files\" switch only if you are really + sure that the files from ISO are better than the ones on hard disk. ----------------------------------------------------------------------------- @@ -3091,14 +3661,11 @@ xorriso in dialog mode. It demonstrates some of xorriso's multi-session features with ISO 9660 filesystems on optical media (CD, DVD, BD) or in disk files. +Dependencies: + xorriso, Tcl language, Tk GUI toolkit, optionally Tcl/Tk package BWidget + Copyright (C) 2012, Thomas Schmitt , libburnia-project.org -Provided under GNU GPL version 2 or later. - ------------------------------------------------------------------------------ - -For a help text about startup options of this frontend, execute: - - $argv0 --help" +Provided under GNU GPL version 2 or later." } if {"$what" == "End"} { return \ @@ -3399,7 +3966,13 @@ check-reading shall be enabled when writing to formatted BD-R or BD-RE. burn a data file from hard disk onto the output drive. The address of the disk file is taken from the neighboring text field. -The medium in the drive may be blank or appendable." +The medium in the drive must be blank. + +(It is well possible to burn image files to appendable media. But the +image needs to be prepared for the address offset. Who can do that can +as well use one of the command line tools for burning the result. E.g. + xorriso -as cdrecord -v dev=/dev/sr0 -multi stream_recording=32s image.iso +)" } if {"$what" == "Extract to disk:"} { return \ @@ -3410,6 +3983,86 @@ underneath \"ISO directory:\". This copies the selected files or directory trees from the input drive to the address on hard disk which is given by the text field right of the button." + } + if {"$what" == "Browse disk (extract)"} { + return \ +"The \"/\" button in the \"Extract to disk:\" line pops up a file tree +browser to select a target address in the hard disk filesystem. + +Click on the \"+\" resp. \"-\" nodes to open resp. close directories. +Click on an item to bring it into effect with the \"Extract to disk:\" +field." + } + if {"$what" == "Browse disk (burn image)"} { + return \ +"The \"/\" button beneath the \"Burn image file\" field pops up a file +tree browser to select a source address in the hard disk filesystem. + +Click on the \"+\" resp. \"-\" nodes to open resp. close directories. +Click on an item to bring it into effect with the \"Burn image file\" +field." + } + if {"$what" == "Browse disk (insert)"} { + return \ +"The \"/\" button beneath the \"Insert from disk\" field pops up a file +tree browser to select a source address in the hard disk filesystem. + +Click on the \"+\" resp. \"-\" nodes to open resp. close directories. +Click on an item to bring it into effect with the \"Insert from disk\" +field." + + } + if {"$what" == "Browse disk (indev)"} { + return \ +"The \"/\" button in the \"Input drive or image\" line pops up a file tree +browser to select a source address in the hard disk filesystem. + +Click on the \"+\" resp. \"-\" nodes to open resp. close directories. +Click on an item to bring it into effect with the \"Input drive or image\" +field." + } + if {"$what" == "Browse disk (outdev)"} { + return \ +"The \"/\" button in the \"Output drive or image\" line pops up a file tree +browser to select a source address in the hard disk filesystem. + +Click on the \"+\" resp. \"-\" nodes to open resp. close directories. +Click on an item to bring it into effect with the \"Output drive or image\" +field." + } + if {"$what" == "Browse ISO (isodir)"} { + return \ +"The \"/\" button in the \"ISO directory\" line pops up a file tree +browser to select the current directory in the ISO filesystem model. + +Click on the \"+\" resp. \"-\" nodes to open resp. close directories. +Click on an item to bring it into effect with the \"ISO directory\" +field." + } + if {"$what" == "Browse ISO (move target)"} { + return \ +"The \"/\" button in the \"ISO selection:\" line pops up a file tree +browser to select the current directory in the ISO filesystem model. + +Click on the \"+\" resp. \"-\" nodes to open resp. close directories. +Click on an item to bring it into the address field for \"Rename to:\" +and \"Make dir\"." + } + if {"$what" == "Browse disk (dummy)"} { + return \ +"Normally this button would start a file browser to select a file or +directory on hard disk. + +But the browser cannot be displayed because Tcl/Tk package \"BWidget\" +is not loaded." + } + if {"$what" == "Browse ISO (dummy)"} { + return \ +"Normally this button would start a file browser to select a file or +directory in the ISO model. + +But the browser cannot be displayed because Tcl/Tk package \"BWidget\" +is not loaded." } if {"$what" == "Underneath (extract)"} { return \ @@ -3439,10 +4092,10 @@ The last command will fail because /tmp/from_iso already exists as directory." or only the selected items shall be copied to hard disk. " } - if {"$what" == "Overwrite files"} { + if {"$what" == "Overwrite ISO files"} { return \ -"The \"Overwrite files\" switch controls whether existing files may be -overwritten in the ISO image or by extraction on hard disk. +"The \"Overwrite ISO files\" switch controls whether existing files may be +overwritten in the ISO image. The frontend program will only detect the most obvious name collisions, but xorriso will reliably refuse to overwrite files if this is banned." @@ -3458,17 +4111,37 @@ hit directories." } if {"$what" == "Enforce disk dir write access"} { return \ -"The \"Enforce disk dir write access\" switch enables the options -osirrox +"The \"Enforce disk dir write access\" switch enables the -osirrox options \"auto_chmod_on\" and \"sort_lba_on\" which influence file extraction. \"auto_chmod_on\" allows xorriso to give itself temporariy w-permission to -all disk directories which are owned by the xorriso user. This is not -without dangers, of course, but comes in handy with restoring of backups. +all disk directories which are owned by the xorriso user. + +This is DANGEROUS, of course, but comes in handy with restoring of backups. Option \"sort_lba_on\" reduces head-moves of optical drives and thus can speed up extraction substantially. It is bound to \"auto_chmod_on\" because else it might get in trouble when restoring ISO directories which offer no w-permission." + } + if {"$what" == "Overwrite disk files"} { + return \ +"The \"Overwrite disk files\" switch controls whether existing files may be +overwritten by extraction on hard disk. + +This is DANGEROUS, of course, but comes in handy with restoring of backups. + +The frontend program will only detect the most obvious name collisions, +but xorriso will reliably refuse to overwrite files if this is banned." + } + if {"$what" == "File browser Return"} { + return \ +"The \"File browser Return\" switch controls whether a selection click in +the file browser shall also hit the Return key after setting the +selected address into the text input field. + +If the switch is disabled, then the address gets written into the field +but no further action is triggered." } if {"$what" == "Refresh avail:"} { return \ @@ -3655,6 +4328,7 @@ proc count_newlines {text} { set rest [string range "$rest" [expr "$idx" + 1] end] } } + return "$count" } @@ -3701,9 +4375,6 @@ proc normalize_isodir_adr {} { proc path_touches_isodir {path} { global isodir_adr - # <<< - # puts stderr "path_touches_isodir : '$path'" - normalize_isodir_adr set cmp_start 0 if {"$isodir_adr" == "/"} { @@ -3718,27 +4389,15 @@ proc path_touches_isodir {path} { } set l [expr {[string length "$isodir_adr"] - $cmp_start}] if {[string length "$path"] < [expr {$l + 2}]} { - - # <<< - # puts stderr "path_touches_isodir : shorter than [expr {$l + 2}]" - return "" } if {$l > 0} { if {[string range "$path" $cmp_start [expr {$l - 1}]] != \ [string range "$isodir_adr" $cmp_start end]} { - - # <<< - # puts stderr "path_touches_isodir : start does not match" - return "" } } if {[string range "$path" "$l" "$l"] != "/"} { - - # <<< - # puts stderr "path_touches_isodir : start not followed by /" - return "" } set subpath [string range "$path" [expr {$l + 1}] end] @@ -3747,10 +4406,6 @@ proc path_touches_isodir {path} { return "$subpath" } if {"$slash" == 0} { - - # <<< - # puts stderr "path_touches_isodir : subpath begins by / : '$subpath'" - return "" } return [string range "$subpath" 0 [expr {$slash - 1}]] @@ -3847,13 +4502,51 @@ proc yell_xorriso_tcltk {} { puts stderr "xorriso-tcltk $own_version : Proof of concept for GUI frontends of xorriso\n" } - + +# Tells whether an absolute path leads to a directory on hard disk +# +proc localfs_isdir {path} { + catch {file lstat "$path" stbuf} + if {[info exists stbuf] == 1} { + if {$stbuf(type) == "directory"} {return 1} + } + return 0 +} + + +# Return the list of files of a hard disk filesystem directory +# +proc localfs_ls {dir} { + + set result "" + if {[localfs_isdir "$dir"] == 0} {return ""} + set conn [open "|ls {$dir}" r] + while {1} { + set ret [gets $conn line] + if {"$ret" == -1} { + break + } + set adr [combine_dir_and_name "$dir" "$line"] + + # >>> Obtain all types, not only "d" and "?" + + if {[localfs_isdir "$adr"] == 1} { + lappend result "d $line" + } else { + lappend result "? $line" + } + } + catch {close $conn} + return "$result" +} + + # -------- start living proc setup_by_args {argv0 argv} { global cmd_pipe_adr reply_pipe_adr main_window_geometry click_to_focus - global log_file log_conn logging + global log_file log_conn logging have_bwidget global cmd_conn reply_conn global geometry stdout stdin @@ -3921,6 +4614,10 @@ proc setup_by_args {argv0 argv} { } set logging "1" } + if {"$opt" == "--no_bwidget"} { + set ok "1" + set have_bwidget "-1" + } if {"$ok" == 0} { puts stderr "$argv0 : Unknown option '$opt'" print_usage "$argv0" diff --git a/xorriso/xorriso_timestamp.h b/xorriso/xorriso_timestamp.h index 285899de..8915b6a9 100644 --- a/xorriso/xorriso_timestamp.h +++ b/xorriso/xorriso_timestamp.h @@ -1 +1 @@ -#define Xorriso_timestamP "2012.12.30.100913" +#define Xorriso_timestamP "2012.12.30.115258"