diff options
author | Andrej Shadura <andrewsh@debian.org> | 2018-05-08 15:59:31 +0200 |
---|---|---|
committer | Andrej Shadura <andrewsh@debian.org> | 2018-05-08 15:59:31 +0200 |
commit | 47aa8b00b2b11df13a100489e0f904a4947177ef (patch) | |
tree | b35c9acc778ea2f761f3c549f7bee2f4491b3144 /lib/dialogues | |
parent | 5b8466f7fae0e071c0f4eda13051c93313910028 (diff) |
Import Upstream version 1.4.7
Diffstat (limited to 'lib/dialogues')
-rw-r--r-- | lib/dialogues/errorhandler.tcl | 189 | ||||
-rw-r--r-- | lib/dialogues/fsd.tcl | 2871 | ||||
-rw-r--r-- | lib/dialogues/my_tk_messageBox.tcl | 303 | ||||
-rw-r--r-- | lib/dialogues/selectmcu.tcl | 1566 | ||||
-rw-r--r-- | lib/dialogues/tips.tcl | 402 |
5 files changed, 5331 insertions, 0 deletions
diff --git a/lib/dialogues/errorhandler.tcl b/lib/dialogues/errorhandler.tcl new file mode 100644 index 0000000..93e1cf4 --- /dev/null +++ b/lib/dialogues/errorhandler.tcl @@ -0,0 +1,189 @@ +#!/usr/bin/tclsh +# Part of MCU 8051 IDE ( http://mcu8051ide.sf.net ) + +############################################################################ +# Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 by Martin Ošmera # +# martin.osmera@gmail.com # +# # +# This program is free software; you can redistribute it and#or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation; either version 2 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program; if not, write to the # +# Free Software Foundation, Inc., # +# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # +############################################################################ + +# >>> File inclusion guard +if { ! [ info exists _ERRORHANDLER_TCL ] } { +set _ERRORHANDLER_TCL _ +# <<< File inclusion guard + +# -------------------------------------------------------------------------- +# DESCRIPTION +# Background error handler +# -------------------------------------------------------------------------- + +namespace eval ErrorHandler { + variable num_of_opened 0 ;# Int: Number of currently opened dialogues + variable count 0 ;# Int: Counter of ivokations + variable enabled 1 ;# Bool: Dialog window enabled + + ## Open dialog window + # @parm String message - Error message + # @return void + proc open_dialog {message} { + variable count ;# Int: Counter of ivokations + variable enabled ;# Bool: Dialog window enabled + variable num_of_opened ;# Int: Number of currently opened dialogues + + if {$num_of_opened > 2} { + puts stderr "ERROR MESSAGE SUPPRESED (too many error dialogues opened at the time)" + return + } + incr num_of_opened + + # Send error message to standard error output + puts stderr [string repeat # 64] + puts stderr "# PROGRAM ERROR #" + puts stderr [string repeat # 64] + puts stderr $::errorInfo + puts stderr [string repeat # 64] + + # Save log file + if {![catch {set log_file [open [file join ${::X::defaultDirectory} mcu8051ide_errors.log] a]}]} { + puts $log_file [string repeat # 64] + puts $log_file "Program version:\t${::VERSION}" + puts $log_file "Tcl version:\t\t${::tcl_version}" + puts $log_file "Tk version:\t\t${::tk_version}" + puts $log_file [string repeat - 64] + puts $log_file $::errorInfo + close $log_file + } + + # Create dialog window + if {!$enabled} {return} + incr count + set win [toplevel .error_dialog$count -bg {#EE0000} -class {Error message} -bg ${::COMMON_BG_COLOR}] + + # Create window frames + set main_frame [frame $win.main_frame] + set top_frame [frame $main_frame.top_frame -bg {#EE0000}] + set middle_frame [frame $main_frame.middle_frame] + set bottom_frame [frame $main_frame.bottom_frame] + + # Create window header + pack [label $top_frame.header_lbl \ + -text [mc "PROGRAM ERROR "] \ + -bg {#EE0000} -fg {#FFFFFF} \ + -font [font create \ + -family helvetica \ + -size [expr {int(-24 * $::font_size_factor)}] \ + -weight bold \ + ] \ + ] -side left -fill x -expand 1 + + # Create error message text and scrollbar + pack [text $middle_frame.text \ + -bg {white} -bd 0 \ + -yscrollcommand "$middle_frame.scrollbar set" \ + -width 0 -height 0 -relief flat -wrap word \ + ] -side left -fill both -expand 1 -padx 5 -pady 5 + bind $middle_frame.text <Button-1> {focus %W} + pack [ttk::scrollbar $middle_frame.scrollbar \ + -orient vertical \ + -command "$middle_frame.text yview" \ + ] -fill y -side right + + # Create text tags + $middle_frame.text tag configure tag_bold \ + -font [font create \ + -family $::DEFAULT_FIXED_FONT \ + -weight bold \ + -size [expr {int(-12 * $::font_size_factor)}] \ + ] + $middle_frame.text tag configure tag_tt \ + -font [font create \ + -family $::DEFAULT_FIXED_FONT \ + -size [expr {int(-12 * $::font_size_factor)}] \ + ] + $middle_frame.text tag configure tag_big_bold \ + -font [font create \ + -family {helvetica} \ + -weight bold \ + -size [expr {int(-17 * $::font_size_factor)}] \ + ] + + # Write error message + $middle_frame.text insert end [mc "Error log saved in: %s\n" "${::X::defaultDirectory}[file separator]mcu8051ide_errors.log"] + $middle_frame.text insert end [mc "Please send this file to %s\nor report this bug at: http://sourceforge.net/tracker/?group_id=185864&atid=914981\n\n\n" {<martin.osmera@gmail.com>}] + create_link_tag_in_text_widget $middle_frame.text + convert_all_https_to_links $middle_frame.text + $middle_frame.text tag add tag_big_bold 1.0 4.0 + $middle_frame.text insert end [mc "ERROR DETAILS:\n--------------\n"] + $middle_frame.text tag add tag_bold 6.0 8.0 + $middle_frame.text insert end $::errorInfo + $middle_frame.text tag add tag_tt 8.0 end + $middle_frame.text configure -state disabled + + # Create button frame + pack [ttk::button $bottom_frame.skip \ + -text [mc "Skip errors"] \ + -compound left \ + -command " + set ::ErrorHandler::enabled 0 + ::ErrorHandler::close_dialog $count + " \ + ] -side left + pack [ttk::button $bottom_frame.ok \ + -text [mc "Close"] \ + -style GreenBg.TButton \ + -command "::ErrorHandler::close_dialog $count" \ + ] -side right + focus -force $bottom_frame.ok + + # Pack window frames + pack $top_frame -fill x -anchor n + pack $middle_frame -fill both -expand 1 + pack $bottom_frame -fill x + pack $main_frame -fill both -expand 1 -padx 5 -pady 5 + + # Configure dialog window + set x [expr {[winfo screenwidth $win] / 2 - 225}] + set y [expr {[winfo screenheight $win] / 2 - 125}] + wm iconphoto $win ::ICONS::16::bug + wm title $win [mc "PROGRAM ERROR - MCU 8051 IDE"] + wm minsize $win 450 250 + wm geometry $win =550x250+$x+$y + wm protocol $win WM_DELETE_WINDOW "::ErrorHandler::close_dialog $count" + update + raise $win + catch {grab $win} + } + + ## Close dialog window + # @parm Int number - Dialog unique number + # @return void + proc close_dialog {number} { + variable num_of_opened ;# Int: Number of currently opened dialogues + + incr num_of_opened -1 + destroy .error_dialog$number + } +} + +# Register error handler +proc bgerror {message} { + ::ErrorHandler::open_dialog $message +} + +# >>> File inclusion guard +} +# <<< File inclusion guard diff --git a/lib/dialogues/fsd.tcl b/lib/dialogues/fsd.tcl new file mode 100644 index 0000000..fce7e24 --- /dev/null +++ b/lib/dialogues/fsd.tcl @@ -0,0 +1,2871 @@ +#!/usr/bin/tclsh +# Part of MCU 8051 IDE ( http://mcu8051ide.sf.net ) + +############################################################################ +# Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 by Martin Ošmera # +# martin.osmera@gmail.com # +# # +# This program is free software; you can redistribute it and#or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation; either version 2 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program; if not, write to the # +# Free Software Foundation, Inc., # +# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # +############################################################################ + +# >>> File inclusion guard +if { ! [ info exists _FSD_TCL ] } { +set _FSD_TCL _ +# <<< File inclusion guard + +# -------------------------------------------------------------------------- +# DESCRIPTION +# This class provides file selection dialog +# Usage: +# KIFSD::FSD <fsd_object> ;# Create dialog object +# <fsd_object> setokcmd {set filename [<fsd_object> get]} ;# Set command for Ok button +# <fsd_object> activate <some_command> ;# Show up the dialog +# +# Constructor options: +# -title String = {} ;# Dialog title +# -initialfile String = {} ;# Initial file +# -directory String = {~} ;# Initiali directory +# -multiple Bool = 0 ;# Allow selection of multiple files (get will return list instead of string) +# -filetypes List = {{All} {*}} ;# { {{Some string} {GLOB}} ... } +# -defaultmask Int = 0 ;# Number of detault mask (see -filetypes) (1st is zero) +# -modal Bool = 1 ;# Create as modal window +# -doubleclick Bool = 0 ;# Use double click to open folder instead of single click +# -autoclose Bool = 1 ;# Close dialog after pressure of Ok button +# -master Widget = . ;# Master window (wm transient $master) +# -fileson Bool = 1 ;# 1 == Select file(s); 0 == Select directory/ies +# +# Other public methods: +# set_bookmark_change_command Command ;# Set command to invoke when bookmarks changes +# deactivate ;# Deactivate the dialog +# close_dialog ;# Close dialog window but keep object alive +# get_config_array -> List ;# Get dialog configuration array for proc. load_config_array +# load_config_array List ;# Load dialog configuration array +# get_window_name -> Widget ;# Get path to dialog window +# -------------------------------------------------------------------------- + +itcl::class KIFSD::FSD { + + common bookmark_change_command {} ;# Command to invoke on bokmark change + # Font for quick navigation panel + common quick_nav_panel_font [font create \ + -family {helvetica} \ + -size [expr {int(-12 * $::font_size_factor)}] \ + -weight bold \ + ] + # Font for files listbox in mode (Short view) + common listbox_font_short [font create \ + -family {helvetica} \ + -size [expr {int(-14 * $::font_size_factor)}] \ + -weight normal \ + ] + # Font for files listbox in mode (Detailed view) and directories listbox + common listbox_font_detailed [font create \ + -family $::DEFAULT_FIXED_FONT \ + -size [expr {int(-12 * $::font_size_factor)}] \ + -weight normal \ + ] + # Font for listbox header + common listbox_header_font [font create \ + -family $::DEFAULT_FIXED_FONT \ + -size [expr {int(-12 * $::font_size_factor)}] \ + -weight bold \ + ] + + ## Values given by constructor arguments + private variable option_title {Select file} ;# Dialog title + private variable option_filetypes {{All} {*}} ;# File types + if {!$::MICROSOFT_WINDOWS} { + variable option_directory ${::env(HOME)} ;# Initial directory + } else { + variable option_directory ${::env(USERPROFILE)} ;# Initial directory + } + private variable option_master {.} ;# Window master + private variable option_fileson {1} ;# 1 == Files on (select file); 0 == Files off (select directory) + private variable option_doubleclick {0} ;# Use doble click instead of single clicks + private variable option_modal {1} ;# Open dialog windown as modal window + private variable option_initialfile {} ;# Initial file + private variable option_multiple {0} ;# Allow mulstiple selection + private variable option_defaultmask {0} ;# Index of default mask in $option_filetypes + private variable option_autoclose {1} ;# 1 == Close dialog after press of Ok button + + private variable bookmark_edit_listbox {} ;# Widget: ListBox in bookmarks editor + private variable bookmark_menu {} ;# Widget: Bookmarks menu + private variable config_menu {} ;# Widget: Configuration menu + private variable listbox_font {} ;# Font: Current font for files listbox + private variable current_directory {} ;# String: Current directory + private variable back_history {} ;# List: Backward history + private variable forward_history {} ;# List: Forward hitory + private variable ok_command {} ;# String: Ok command + private variable current_mask {*} ;# GLOB: Current fileter mask + private variable item_menu_request 0 ;# Bool: Item popup menu request + private variable current_item {} ;# String: ID of currenly selected item + private variable current_item_index 0 ;# Int: Index of currently selected item + private variable cur_listbox {} ;# String currently selected listbox {dir} or {file} + + private variable dialog_loaded 0 ;# Bool: Dialog is completely loaded + private variable win ;# Widget: Dialog window + private variable ok_button ;# Widget: Button "Ok" + private variable location_cb ;# Widget: Location ComboBox + private variable filter_cb ;# Widget: Filter ComboBox + private variable dir_combobox ;# Widget: Directory ComboBox + private variable toolbar ;# Widget: Frame containing toolbar + private variable quick_access_bar ;# Widget: Quick access bar ListBox + private variable dir_listbox_scrollbar {} ;# Widget: Directory ListBox scrollbar + private variable dir_listbox {} ;# Widget: Directory ListBox + private variable main_paned_window ;# Widget: Paned window for quick access bar and other LBs + private variable leftframe ;# Widget: Frame contaning quick access bar and its scrollbar + private variable rightframe ;# Widget: Frame containing directory & file ListBoxes + private variable right_top_right_frame ;# Widget: files ListBox + private variable right_top_left_frame ;# Widget: Frame for files ListBox + private variable right_top_frame ;# Widget: Frame for $right_paned_window + private variable right_paned_window ;# Widget: Paned window for directories ListBox and files ListBox + private variable file_listbox ;# Widget: Files ListBox + private variable file_listbox_header ;# Widget: Header for files ListBox + private variable file_listbox_frame ;# Widget: Frame for $file_listbox_header and $file_listbox + private variable file_listbox_vscrollbar {} ;# Widget: Vertical scollbar for files ListBox + private variable file_listbox_hscrollbar {} ;# Widget: Hotizontal scollbar for files ListBox + private variable right_top_right_top_frame ;# Widget: Frame for $file_listbox_frame and scrollbars + + proc static_reload {object args} { + catch [list $object reload] + } + + ## Dialog constructor + # For complite list of possible arguments see desctiption above + constructor args { + + # Configure local ttk styles + ttk::style configure FSD_RedBg.TCombobox -fieldbackground {#FFDDDD} + ttk::style configure FSD_RedBg.TEntry -fieldbackground {#FFDDDD} + + ## Parse given arguments and set appropriate object variables + set arglen [llength $args] + set arg {} + for {set i 0} {$i < $arglen} {incr i} { + set arg [lindex $args $i] + switch -- $arg { + -modal { + incr i + set option_modal [lindex $args $i] + if {![string is boolean -strict $option_modal]} { + error "-modal must have value either 0 or 1" + } + } + -doubleclick { + incr i + set option_doubleclick [lindex $args $i] + if {![string is boolean -strict $option_doubleclick]} { + error "-doubleclick must have value either 0 or 1" + } + } + -autoclose { + incr i + set option_autoclose [lindex $args $i] + if {![string is boolean -strict $option_autoclose]} { + error "-autoclose must have value either 0 or 1" + } + } + -initialfile { + incr i + set option_initialfile [lindex $args $i] + } + -multiple { + incr i + set option_multiple [lindex $args $i] + if {![string is boolean -strict $option_multiple]} { + error "-multiple must have value either 0 or 1" + } + } + -defaultmask { + incr i + set option_defaultmask [lindex $args $i] + if {![string is integer -strict $option_defaultmask]} { + error "-defaultmask must be an integer" + } + } + -title { + incr i + set option_title [lindex $args $i] + } + -filetypes { + incr i + set option_filetypes [lindex $args $i] + } + -directory { + incr i + set option_directory [lindex $args $i] + } + -master { + incr i + set option_master [lindex $args $i] + } + -fileson { + incr i + set option_fileson [lindex $args $i] + if {![string is boolean -strict $option_modal]} { + error "-fileson must have value either 0 or 1" + } + } + default { + error "Option '$arg' is not valid" + } + } + } + set args {} + set current_directory [file normalize $option_directory] + + # Cretate dialog window + create_dialog + + # Initalize window key shortcuts + create_shortcuts + + # Finalize + set dialog_loaded 1 + } + + ## Destrurtor + destructor { + catch { + # Save position of right paned window sash + if {[winfo ismapped $right_paned_window]} { + set ::KIFSD::FSD::config(right_PW_size) \ + [lindex [$right_paned_window sash coord 0] 0] + } + # Save position of main paned window sash + if {[winfo ismapped $main_paned_window]} { + set ::KIFSD::FSD::config(main_PW_size) \ + [lindex [$main_paned_window sash coord 0] 0] + } + # Save window geometry + set ::KIFSD::FSD::config(win_geometry) [wm geometry $win] + } + + # Destroy dialog window + grab release $win + destroy $win + } + + ## Create dialog GUI elements + # @return void + private method create_dialog {} { + # Determinate window name (path) + set win_base {} + if {$option_master != {.}} { + set win_base $option_master + } + append win_base .[string tolower [regsub -all {:} $this {}]] + set win $win_base + set i 0 + while [winfo exists $win] { + set win $win_base + append win $i + incr i + } + + # Create and configure dialog window + toplevel $win -bg ${::COMMON_BG_COLOR} + wm iconphoto $win ::ICONS::16::fileopen + wm withdraw $win + wm title $win $option_title + wm minsize $win 540 290 + wm protocol $win WM_DELETE_WINDOW "catch {itcl::delete object $this}" + wm transient $win $option_master + wm geometry $win ${::KIFSD::FSD::config(win_geometry)} + wm resizable $win 0 0 + raise $win + update + if {$option_modal} { + catch { + grab $win + } + } + + + create_popup_menus ;# Create popup menus + set topframe [frame $win.topframe] ;# Create frame above ListBoxes + set toolbar [frame $topframe.toobar] ;# Create toolbar frame + create_tool_bar ;# Create toolbar + + # Create directory ComboBox + set dir_combobox [ttk::combobox $topframe.dir_cb \ + -values {} \ + -exportselection 0 \ + -validate all \ + -validatecommand "::KIFSD::FSD::dir_validate $topframe.dir_cb %W %P" \ + ] + bind $dir_combobox <<ComboboxSelected>> [list $this dir_cb_modify] + bind $dir_combobox <KP_Enter> [list $this dir_cb_modify] + bind $dir_combobox <Return> [list $this dir_cb_modify] + + DynamicHelp::add $dir_combobox -text [mc "Current directory"] + pack $dir_combobox -side right -expand 1 -fill x -padx 5 + + # Create main paned window and some frames + set mainframe [frame $win.mainframe] + set main_paned_window [panedwindow $mainframe.main_paned_window \ + -orient horizontal -opaqueresize 1 -sashwidth 2 \ + -showhandle 0 -sashrelief flat \ + ] + set leftframe [frame $mainframe.leftframe] + set rightframe [frame $mainframe.rightframe] + + # Create quick access bar + set quick_access_bar [ListBox $leftframe.quick_access_bar \ + -selectfill 1 -selectbackground white -bd 1 -padx 30 -width 15 \ + -selectmode single -highlightthickness 0 -bg white -deltay 30 \ + -selectforeground black -highlightcolor {#BBBBFF} \ + ] + refresh_quick_access_bar + $quick_access_bar bindText <ButtonRelease-3> [list $this quick_access_bar_item_menu %X %Y ] + $quick_access_bar bindImage <ButtonRelease-3> [list $this quick_access_bar_item_menu %X %Y ] + $quick_access_bar bindText <Double-Button-1> [list $this quick_access_bar_doubleclick ] + $quick_access_bar bindImage <Double-Button-1> [list $this quick_access_bar_doubleclick ] + bind $quick_access_bar <<ListboxSelect>> [list $this quick_access_bar_select ] + if {[winfo exists $quick_access_bar.c]} { + bind $quick_access_bar.c <Button-5> {%W yview scroll +5 units; break} + bind $quick_access_bar.c <Button-4> {%W yview scroll -5 units; break} + bind $quick_access_bar.c <ButtonRelease-3> [list $this quick_access_bar_menu %X %Y ] + } + pack $quick_access_bar -fill both -expand 1 + + # Create right paned window + set right_top_frame [frame $rightframe.topframe] + set right_bottom_frame [frame $rightframe.bottomframe] + set right_paned_window [panedwindow $right_top_frame.right_paned_window \ + -orient horizontal -opaqueresize 1 -sashwidth 2 \ + -showhandle 0 -sashrelief flat \ + ] + set right_top_left_frame [frame $win.left_frame] + set right_top_right_frame [frame $win.right_frame] + + # Create directories ListBox + if {$option_fileson} { + set dir_listbox [ListBox $right_top_left_frame.dir_listbox \ + -bd 1 -padx 19 -selectfill 1 -width 1 -highlightcolor {#BBBBFF} \ + -selectmode single -highlightthickness 0 -bg white -deltay 18 \ + -yscrollcommand "$this dir_listbox_scroll" \ + ] + set dir_listbox_scrollbar [ttk::scrollbar \ + $right_top_left_frame.scrollbar \ + -orient vertical -command "$dir_listbox yview" \ + ] + $dir_listbox bindText <Double-Button-1> [list $this dir_listbox_doubleclick ] + $dir_listbox bindImage <Double-Button-1> [list $this dir_listbox_doubleclick ] + $dir_listbox bindText <ButtonRelease-3> [list $this dir_listbox_item_menu %X %Y ] + $dir_listbox bindImage <ButtonRelease-3> [list $this dir_listbox_item_menu %X %Y ] + bind $dir_listbox <<ListboxSelect>> [list $this dir_listbox_select ] + if {[winfo exists $dir_listbox.c]} { + bind $dir_listbox.c <Button-5> {%W yview scroll +5 units; break} + bind $dir_listbox.c <Button-4> {%W yview scroll -5 units; break} + bind $dir_listbox.c <ButtonRelease-3> [list $this dir_listbox_menu %X %Y ] + } + pack $dir_listbox -side left -fill both -expand 1 + } + + # Create files ListBox + if {$option_multiple} { + set selmode {multiple} + } else { + set selmode {single} + } + set right_top_right_top_frame [frame $right_top_right_frame.right_top_right_top_frame] + set file_listbox_frame [frame $right_top_right_top_frame.file_listbox_frame] + set file_listbox_header [text $file_listbox_frame.text \ + -width 1 -height 1 -takefocus 0 -bg white \ + -font $listbox_header_font -bd 1 -relief sunken \ + -cursor left_ptr -wrap none \ + ] + $file_listbox_header delete 1.0 end + if {!$::MICROSOFT_WINDOWS} { + $file_listbox_header insert end \ + [mc " Name Size Rights Date "] + } else { + $file_listbox_header insert end \ + [mc " Name Size Date "] + } + bindtags $file_listbox_header $file_listbox_header + $file_listbox_header configure -state disabled + set file_listbox [ListBox $file_listbox_frame.file_listbox \ + -bd 1 \ + -padx 17 \ + -width 1 \ + -height 1 \ + -bg white \ + -deltay 18 \ + -selectfill 1 \ + -selectmode $selmode \ + -highlightthickness 0 \ + -selectbackground {#88AAFF} \ + -highlightcolor {#BBBBFF} \ + -yscrollcommand "$this file_listbox_vscroll" \ + -xscrollcommand "$this file_listbox_hscroll" \ + ] + pack $file_listbox -fill both -expand 1 + if {${::KIFSD::FSD::config(detailed_view)}} { + $file_listbox configure -multicolumn 0 + set listbox_font $listbox_font_detailed + pack $file_listbox_header -before $file_listbox -fill x -expand 0 + } else { + $file_listbox configure -multicolumn 1 + set listbox_font $listbox_font_short + } + set file_listbox_vscrollbar [ttk::scrollbar \ + $right_top_right_top_frame.vscrollbar \ + -orient vertical -command "$file_listbox yview" \ + ] + set file_listbox_hscrollbar [ttk::scrollbar \ + $right_top_right_frame.hscrollbar \ + -orient horizontal \ + -command "$this file_listbox_hscrollbar_cmd" \ + ] + $file_listbox bindText <Double-Button-1> [list $this file_listbox_doubleclick] + $file_listbox bindImage <Double-Button-1> [list $this file_listbox_doubleclick] + $file_listbox bindText <ButtonRelease-3> [list $this file_listbox_item_menu %X %Y] + $file_listbox bindImage <ButtonRelease-3> [list $this file_listbox_item_menu %X %Y] + bind $file_listbox <<ListboxSelect>> [list $this file_listbox_select] + if {[winfo exists $file_listbox.c]} { + bind $file_listbox.c <Button-5> [list $this file_listbox_scroll +5 units] + bind $file_listbox.c <Button-4> [list $this file_listbox_scroll -5 units] + bind $file_listbox.c <ButtonRelease-3> [list $this file_listbox_menu %X %Y] + } + pack $file_listbox_frame -fill both -expand 1 -side left + pack $right_top_right_top_frame -fill both -expand 1 -side top + pack $right_top_frame -side top -fill both -expand 1 + + # Create Location Label+ComboBox and Filter Label+ComboBox + grid [label $right_bottom_frame.location_label \ + -text [mc "Location:"] \ + ] -sticky w -column 0 -row 0 + grid [label $right_bottom_frame.filter_label \ + -text [mc "Filter:"] \ + ] -sticky w -column 0 -row 1 + + set location_cb [ttk::combobox $right_bottom_frame.location_cb \ + -values {} \ + -exportselection 0 \ + ] + bind $location_cb <<ComboboxSelected>> "$file_listbox selection clear" + DynamicHelp::add $location_cb -text [mc "Selected file(s)"] + bind $location_cb <Key> "$file_listbox selection clear" + bind $location_cb <KP_Enter> [list $this ok] + bind $location_cb <Return> [list $this ok] + + set tmp_option_filetypes {} + foreach type $option_filetypes { + set glob_masks [lindex $type 1] + if {[regexp {^\*\.\{\w+(,\w+)*\}$} $glob_masks]} { + set glob_masks [split $glob_masks {{,}}] + set glob_masks [lreplace $glob_masks 0 0] + set glob_masks [lreplace $glob_masks end end] + set glob_masks_new [list] + foreach ext $glob_masks { + lappend glob_masks_new [format "*.%s" $ext] + } + set glob_masks [join $glob_masks_new {, }] + } + lappend tmp_option_filetypes "[lindex $type 0] ($glob_masks)" + } + set filter_cb [ttk::combobox $right_bottom_frame.filter_cb \ + -state readonly \ + -values $tmp_option_filetypes \ + -exportselection 0 \ + ] + DynamicHelp::add $right_bottom_frame.filter_cb -text [mc "Filter"] + set tmp_option_filetypes {} + foreach type $option_filetypes { + lappend tmp_option_filetypes [lindex $type 1] + } + set option_filetypes $tmp_option_filetypes + $filter_cb current $option_defaultmask + set current_mask [lindex $option_filetypes $option_defaultmask] + bind $filter_cb <<ComboboxSelected>> [list $this filter_cb_modify] + grid $location_cb -sticky ew -column 1 -row 0 + grid $filter_cb -sticky ew -column 1 -row 1 + + if {!$option_fileson} { + $filter_cb configure -state disabled + } + + # Create buttons "Ok" and "Cancel" + set ok_button [ttk::button $right_bottom_frame.ok_button\ + -text [mc "Ok"] \ + -compound left \ + -width 8 \ + -image ::ICONS::16::ok \ + -command [list $this ok] \ + ] + grid $ok_button -sticky w -column 2 -row 0 -padx 7 -pady 2 + grid [ttk::button $right_bottom_frame.cancel_button \ + -text [mc "Cancel"] \ + -compound left \ + -width 8 \ + -image ::ICONS::16::button_cancel \ + -command "itcl::delete object $this" \ + ] -sticky w -column 2 -row 1 -padx 7 -pady 2 + + grid columnconfigure $right_bottom_frame 1 -weight 1 + + pack $right_bottom_frame -side bottom -fill x -expand 0 -anchor w + + pack $topframe -side top -fill x -padx 12 -pady 10 + pack $mainframe -side bottom -fill both -expand 1 -padx 12 + + # Adjust paned windows to current configuration + quick_access_panel_onoff + separate_folders_onoff + + # Finalize + $location_cb set $option_initialfile + focus -force $location_cb + catch { + $location_cb.e selection range 0 end + } + } + + ## Create dialog toolbar + # @return void + private method create_tool_bar {} { + set si 0 + foreach item { + {up "Parent folder" {1uparrow} + {up}} + {back "Back" {1leftarrow} + {back}} + {forward "Forward" {1rightarrow} + {forward}} + {reload "Reload" {reload} + {reload}} + {separator} + {newdir "New folder" {folder_new} + {newdir}} + {separator} + {short "Short view" {view_icon} + {short_view}} + {detail "Detailed view" {view_detailed} + {detail_view}} + {separator} + {bookmark "Bookmarks" {bookmark} + {bookmark_menu}} + {configure "Configure" {configure} + {config_menu}} + } \ + { + # Create separator + if {$item == {separator}} { + pack [ttk::separator $toolbar.sep$si \ + -orient vertical \ + ] -side left -padx 4 -fill both -expand 1 + incr si + continue + } + + # Create button + if {[lindex $item 0] == {bookmark}} { + set buttonWidget [ttk::menubutton $toolbar.[lindex $item 0] \ + -image ::ICONS::22::[lindex $item 2] \ + -menu $bookmark_menu \ + -style Flat.TMenubutton \ + ] + } elseif {[lindex $item 0] == {configure}} { + set buttonWidget [ttk::menubutton $toolbar.[lindex $item 0] \ + -image ::ICONS::22::[lindex $item 2] \ + -menu $config_menu \ + -style Flat.TMenubutton \ + ] + } else { + set buttonWidget [ttk::button $toolbar.[lindex $item 0] \ + -command "$this [lindex $item 3]" \ + -style Flat.TButton \ + -image ::ICONS::22::[lindex $item 2] \ + ] + } + DynamicHelp::add $buttonWidget -text [mc [lindex $item 1]] + + # Pack it + pack $buttonWidget -side left -padx 2 + } + + # Disable button for manipulating history + $toolbar.back configure -state disabled + $toolbar.forward configure -state disabled + + # Pack toolbar frame + pack $toolbar -side left -expand 0 -fill none + } + + + ## Create dialog popup menus + # @return void + private method create_popup_menus {} { + # Create configuration menu + set config_menu [menu $win.config_menu -tearoff 0] + + ## Create menu: Configuration -> Sorting + set sorting_menu [menu $win.config_menu.sorting_menu -tearoff 0] + # Entry: "By name" + $sorting_menu add radiobutton -label [mc "By name"] \ + -variable ::KIFSD::FSD::config(sorting) \ + -indicatoron 0 -compound left -image ::ICONS::raoff -selectimage ::ICONS::raon \ + -value {name} -underline 3 -command [list $this reload] + # Entry: "By date" + $sorting_menu add radiobutton -label [mc "By date"] \ + -variable ::KIFSD::FSD::config(sorting) \ + -indicatoron 0 -compound left -image ::ICONS::raoff -selectimage ::ICONS::raon \ + -value {date} -underline 3 -command [list $this reload] + # Entry: "By size" + $sorting_menu add radiobutton -label [mc "By size"] \ + -variable ::KIFSD::FSD::config(sorting) \ + -indicatoron 0 -compound left -image ::ICONS::raoff -selectimage ::ICONS::raon \ + -value {size} -underline 3 -command [list $this reload] + $sorting_menu add separator + # Entry: "Reverse" + $sorting_menu add checkbutton -label [mc "Reverse"] \ + -variable ::KIFSD::FSD::config(reverse_sorting) \ + -indicatoron 0 -compound left -image ::ICONS::choff -selectimage ::ICONS::chon \ + -command "$this reload" -underline 0 + # Entry: "Folders first" + $sorting_menu add checkbutton -label [mc "Folders first"] \ + -variable ::KIFSD::FSD::config(folders_first) \ + -indicatoron 0 -compound left -image ::ICONS::choff -selectimage ::ICONS::chon \ + -command "$this reload" -underline 0 + # Entry: "Case insensitive" + $sorting_menu add checkbutton -label [mc "Case insensitive"] \ + -variable ::KIFSD::FSD::config(case_insensitive) \ + -indicatoron 0 -compound left -image ::ICONS::choff -selectimage ::ICONS::chon \ + -command "$this reload" -underline 0 + + ## Create entries for configuraion menu (accessable from toolbar) + # Entry: "Sorting" + $win.config_menu add cascade -label [mc "Sorting"] -underline 1 -menu $sorting_menu -image ::ICONS::16::sort_incr -compound left + $win.config_menu add separator + # Entry: "Short view" + $win.config_menu add command -label [mc "Short view"] -compound left \ + -accelerator "F6" -command "$this short_view" -underline 0 \ + -image ::ICONS::16::view_icon + # Entry: "Detailed view" + $win.config_menu add command -label [mc "Detailed view"] -compound left \ + -accelerator "F7" -command "$this detail_view" -underline 0 \ + -image ::ICONS::16::view_detailed + $win.config_menu add separator + # Entry: "Show hidden files" + $win.config_menu add checkbutton -label [mc "Show hidden files"] \ + -accelerator "F8" -variable ::KIFSD::FSD::config(show_hidden_files) \ + -indicatoron 0 -compound left -image ::ICONS::choff -selectimage ::ICONS::chon \ + -command "$this reload" -underline 5 + # Entry: "Quick access navigation panel" + $win.config_menu add checkbutton -label [mc "Quick access navigation panel"] \ + -accelerator "F9" -variable ::KIFSD::FSD::config(quick_access_panel) \ + -indicatoron 0 -compound left -image ::ICONS::choff -selectimage ::ICONS::chon \ + -command "$this quick_access_panel_onoff" -underline 0 + # Entry: "Separate folders" + $win.config_menu add checkbutton -label [mc "Separate folders"] \ + -accelerator "F12" -variable ::KIFSD::FSD::config(separate_folders) \ + -indicatoron 0 -compound left -image ::ICONS::choff -selectimage ::ICONS::chon \ + -command "$this separate_folders_onoff" -underline 9 + if {!$option_fileson} { + $win.config_menu entryconfigure [mc "Separate folders"] -state disabled + $sorting_menu entryconfigure [mc "Folders first"] -state disabled + $sorting_menu entryconfigure [mc "By size"] -state disabled + } + + ## Create bookmarks menu (accessable from toolbar) + set bookmark_menu [menu $win.bookmark_menu -tearoff 0] + # Entry: "Add bookmark" + $bookmark_menu add command -label [mc "Add bookmark"] \ + -command "$this add_bookmark" \ + -underline 0 -image ::ICONS::16::bookmark_add -compound left + # Entry: "Edit bookmarks" + $bookmark_menu add command -label [mc "Edit bookmarks"] -compound left \ + -command "$this edit_bookmarks" -underline 0 -image ::ICONS::16::bookmark + $bookmark_menu add separator + refresh_bookmarks + + ## Create ListBox item menu + menu $win.listbox_menu -tearoff 0 + # Entry: "Up" + $win.listbox_menu add command -label [mc "Up"] -compound left \ + -underline 0 -command [list $this up] \ + -image ::ICONS::16::up + # Entry: "Back" + $win.listbox_menu add command -label [mc "Back"] -compound left \ + -underline 0 -command [list $this back] \ + -image ::ICONS::16::left -state disabled + # Entry: "Forward" + $win.listbox_menu add command -label [mc "Forward"] -compound left \ + -underline 0 -command [list $this forward] \ + -image ::ICONS::16::right -state disabled + $win.listbox_menu add separator + # Entry: "Rename" + $win.listbox_menu add command -label [mc "Rename"] \ + -underline 0 -command [list $this rename_item_command] \ + -compound left -image ::ICONS::16::edit + # Entry: "Delete" + $win.listbox_menu add command -label [mc "Delete"] \ + -underline 0 -command [list $this delete_item_command] \ + -compound left -image ::ICONS::16::editdelete + # Entry: "New folder" + $win.listbox_menu add command -label [mc "New folder"] \ + -accelerator "F10" \ + -underline 0 -command [list $this newdir] \ + -compound left -image ::ICONS::16::folder_new + # Entry: "Bookmark folder" + $win.listbox_menu add command -label [mc "Bookmark folder"] \ + -underline 0 -command [list $this item_bookmark_add] \ + -compound left -image ::ICONS::16::bookmark_add + $win.listbox_menu add separator + # Entry: "Properties" + $win.listbox_menu add command -label [mc "Properties"] \ + -underline 0 -command [list $this properties_item_command] + + ## Create quick access bar popup menu + menu $win.quick_access_panel_menu -tearoff 0 + # Entry: "Add entry" + $win.quick_access_panel_menu add command -label [mc "Add entry"] \ + -underline 0 -image ::ICONS::16::filenew -compound left \ + -command "$this quick_access_panel_add_entry" + $win.quick_access_panel_menu add separator + # Entry: "Hide panel" + $win.quick_access_panel_menu add command -label [mc "Hide panel"] \ + -underline 0 -image ::ICONS::16::2leftarrow -compound left \ + -accelerator "F9" -command " + set ::KIFSD::FSD::config(quick_access_panel) \ + \[expr {!\${::KIFSD::FSD::config(quick_access_panel)}}\] + $this quick_access_panel_onoff" + + ## Create quick access bar ITEM popup menu + menu $win.quick_access_panel_item_menu -tearoff 0 + # Entry: "Move up" + $win.quick_access_panel_item_menu add command -label [mc "Move up"] \ + -underline 0 -image ::ICONS::16::1uparrow -compound left \ + -command "$this quick_access_panel_up" + # Entry: "Move down" + $win.quick_access_panel_item_menu add command -label [mc "Move down"] \ + -underline 0 -image ::ICONS::16::1downarrow -compound left \ + -command "$this quick_access_panel_down" + $win.quick_access_panel_item_menu add separator + # Entry: "Edit entry" + $win.quick_access_panel_item_menu add command -label [mc "Edit entry"] \ + -underline 0 -image ::ICONS::16::edit -compound left \ + -command "$this quick_access_panel_edit_entry" + $win.quick_access_panel_item_menu add separator + # Entry: "Add entry" + $win.quick_access_panel_item_menu add command -label [mc "Add entry"] \ + -underline 0 -image ::ICONS::16::filenew -compound left \ + -command "$this quick_access_panel_add_entry" + # Entry: "Remove entry" + $win.quick_access_panel_item_menu add command -label [mc "Remove entry"]\ + -underline 0 -image ::ICONS::16::editdelete -compound left \ + -command "$this quick_access_panel_remove_entry" + $win.quick_access_panel_item_menu add separator + # Entry: "Hide panel" + $win.quick_access_panel_item_menu add command -label [mc "Hide panel"] \ + -underline 0 -image ::ICONS::16::2leftarrow -compound left \ + -accelerator "F9" \ + -command " + set ::KIFSD::FSD::config(quick_access_panel) \ + \[expr {!\${::KIFSD::FSD::config(quick_access_panel)}}\] + $this quick_access_panel_onoff" + } + + ## Define key shortcuts for the dialog + # @return void + private method create_shortcuts {} { + bind $win <Key-F5> "$this reload; break" + bind $win <Key-F6> "$this short_view; break" + bind $win <Key-F7> "$this detail_view; break" + bind $win <Key-F8> " + set ::KIFSD::FSD::config(show_hidden_files) \ + \[expr {!\${::KIFSD::FSD::config(show_hidden_files)}}\] + $this reload + break + " + bind $win <Key-F9> " + set ::KIFSD::FSD::config(quick_access_panel) \ + \[expr {!\${::KIFSD::FSD::config(quick_access_panel)}}\] + $this quick_access_panel_onoff + break + " + bind $win <Key-F10> "$this newdir; break" + if {$option_fileson} { + bind $win <Key-F12> " + set ::KIFSD::FSD::config(separate_folders) \ + \[expr {!\${::KIFSD::FSD::config(separate_folders)}}\] + $this separate_folders_onoff + break + " + } + } + + ## Change current directory + # This function checks for directory validity + # @parm String dir - New directory + # @return void + public method change_directory {dir} { + if {$::MICROSOFT_WINDOWS} { + # Transform for instance "C:" to "C:/" + if {[regexp {^\w+:$} $dir]} { + append dir {/} + } + } + + # Check if the specified directory is valid + if {![file exists $dir] || ![file isdirectory $dir]} { + tk_messageBox \ + -parent $win \ + -type ok \ + -icon warning \ + -title [mc "Invalid folder"] \ + -message [mc "The specified folder does not exist:\n%s" $dir] + return + } + set dir [file normalize $dir] + + # Adjust history + if {$dir != $current_directory} { + lappend back_history $current_directory + set forward_history {} + $win.listbox_menu entryconfigure [mc "Forward"] -state disabled + $win.listbox_menu entryconfigure [mc "Back"] -state normal + $toolbar.forward configure -state disabled + $toolbar.back configure -state normal + } + + # Option separate_folders ON + FSnotifications::forget $current_directory + FSnotifications::watch $dir [list KIFSD::FSD::static_reload $this] + set current_directory $dir + if {${::KIFSD::FSD::config(separate_folders)} && $option_fileson} { + # Fill up directory ListBox with directories + $dir_listbox delete [$dir_listbox items] + foreach folder [dir_cmd $dir 1] { + if {$folder == {..}} { + set image {up} + } else { + set image {fileopen} + } + $dir_listbox insert end #auto \ + -text $folder \ + -image ::ICONS::16::$image \ + -font $listbox_font_short + } + + # Fill up file ListBox with files + $file_listbox delete [$file_listbox items] + foreach file [file_cmd $dir $current_mask] { + if {${::KIFSD::FSD::config(detailed_view)}} { + set filename [lindex $file 1] + set file [lindex $file 0] + } else { + set filename $file + } + $file_listbox insert end #auto \ + -text $file \ + -image ::ICONS::16::ascii \ + -font $listbox_font \ + -data [list $filename {}] + } + # Option separate_folders OFF + } else { + # Option folders_first ON or option_fileson OFF + $file_listbox delete [$file_listbox items] + if {!$option_fileson || ${::KIFSD::FSD::config(folders_first)}} { + # Fill up files ListBox with directories + foreach folder [dir_cmd $dir] { + if {${::KIFSD::FSD::config(detailed_view)}} { + set fullname [lindex $folder 1] + set folder [lindex $folder 0] + } else { + set fullname $folder + } + + if {$folder == {..}} { + set image {up} + set fullname $folder + } else { + set image {fileopen} + } + + $file_listbox insert end #auto \ + -text $folder \ + -image ::ICONS::16::$image \ + -font $listbox_font \ + -data [list {} $fullname] + } + # Option: option_fileson ON + if {$option_fileson} { + # Fill up files ListBox with files + foreach file [file_cmd $dir $current_mask] { + if {${::KIFSD::FSD::config(detailed_view)}} { + set filename [lindex $file 1] + set file [lindex $file 0] + } else { + set filename $file + } + + $file_listbox insert end #auto \ + -text $file \ + -image ::ICONS::16::ascii \ + -font $listbox_font \ + -data [list $filename {}] + } + } + # Option NOT ( folders_first ON or option_fileson OFF ) + } else { + # Fill up files ListBox with files and directories + foreach file [dir_file_cmd $dir $current_mask] { + set filename {} + set folder {} + if {${::KIFSD::FSD::config(detailed_view)}} { + set fullname [lindex $file {0 1}] + set text [lindex $file {0 0}] + } else { + set fullname [lindex $file 0] + set text $fullname + } + + switch -- [lindex $file 1] { + u { + set image {up} + set folder {..} + } + d { + set image {fileopen} + set folder $fullname + } + f { + set image {ascii} + set filename $fullname + } + } + + $file_listbox insert end #auto \ + -text $text \ + -image ::ICONS::16::$image \ + -font $listbox_font \ + -data [list $filename $folder] + } + } + } + + # Fill up location ComboBox with available files or directories + if {$option_fileson} { + $location_cb configure -values [file_cmd $dir $current_mask 1] + } else { + $location_cb configure -values [dir_cmd $dir 1] + } + $location_cb set {} + + # Fill up directory ComboBox + set values {} + set folder $dir + while {1} { + lappend values $folder + if {$folder == [file separator]} {break} + if {$::MICROSOFT_WINDOWS} { + if {[regexp {^\w+:[\\\/]?$} $folder]} {break} + } + set folder [file normalize [file join $folder {..}]] + } + foreach folder [dir_cmd $dir 1] { + if {$folder == {..}} {continue} + lappend values [file join $dir $folder] + } + if {$::MICROSOFT_WINDOWS} { ;# Include drive letters on Microsoft Windows + foreach drive_letter {A B C D E F G H I J K L M N O P Q R S T U V W X Y Z} { + if {[file exists "${drive_letter}:/"]} { + lappend values "${drive_letter}:/" + } + } + } + + $dir_combobox configure -values $values + $dir_combobox current 0 + $dir_combobox icursor end + + # Enable / Disable button "Up (Parent folder)" + if {$dir == {/} || $dir == "\\"} { + $toolbar.up configure -state disabled + $win.listbox_menu entryconfigure [mc "Up"] -state disabled + } else { + $toolbar.up configure -state normal + $win.listbox_menu entryconfigure [mc "Up"] -state normal + } + } + + ## This function shoul be called after Filter ComboBox change + # @return void + public method filter_cb_modify {} { + set current_mask [lindex $option_filetypes [$filter_cb current]] + reload + } + + ## Show / Hide quick access bar according to configuration variable quick_access_panel + # @return void + public method quick_access_panel_onoff {} { + # Show the panel + if {${::KIFSD::FSD::config(quick_access_panel)}} { + pack $main_paned_window -fill both -expand 1 + $main_paned_window add $leftframe + $main_paned_window add $rightframe + $main_paned_window paneconfigure $leftframe -minsize 100 + $main_paned_window paneconfigure $rightframe -minsize 300 + if {$dialog_loaded} {update} + $main_paned_window sash place 0 ${::KIFSD::FSD::config(main_PW_size)} 0 + if {$dialog_loaded} {update} + # Hide the panel + } else { + if {[winfo ismapped $main_paned_window]} { + set ::KIFSD::FSD::config(main_PW_size) \ + [lindex [$main_paned_window sash coord 0] {0 0}] + $main_paned_window forget $leftframe + $main_paned_window forget $rightframe + pack forget $main_paned_window + } + pack $rightframe -fill both -expand 1 -padx 5 + } + } + + ## Show / Hide folders ListBox according to configuration variable separate_folders + # This function will show folders ListBox only if option_fileson == 1 + # @return void + public method separate_folders_onoff {} { + # Show folders ListBox + if {${::KIFSD::FSD::config(separate_folders)} && $option_fileson} { + pack $right_paned_window -fill both -expand 1 + $right_paned_window add $right_top_left_frame + $right_paned_window add $right_top_right_frame + $right_paned_window paneconfigure $right_top_left_frame -minsize 150 + $right_paned_window paneconfigure $right_top_right_frame -minsize 200 + if {$dialog_loaded} {update} + $right_paned_window sash place 0 ${::KIFSD::FSD::config(right_PW_size)} 0 + if {$dialog_loaded} {update} + + # Hide folders ListBox + } else { + if {[winfo ismapped $right_paned_window]} { + set ::KIFSD::FSD::config(right_PW_size) \ + [lindex [$right_paned_window sash coord 0] {0 0}] + $right_paned_window forget $right_top_left_frame + $right_paned_window forget $right_top_right_frame + pack forget $right_paned_window + } + pack $right_top_right_frame -expand 1 -fill both -in $right_top_frame + } + + # Refresh files and folders ListBoxes + change_directory $current_directory + } + + ## Invoke bookmark menu + # @return void + public method bookmark_menu {} { + set x [winfo rootx $toolbar.bookmark] + set y [winfo rooty $toolbar.bookmark] + incr y [winfo height $toolbar.bookmark] + tk_popup $win.bookmark_menu $x $y + } + + ## Invoke configuration menu + # @return void + public method config_menu {} { + set x [winfo rootx $toolbar.configure] + set y [winfo rooty $toolbar.configure] + incr y [winfo height $toolbar.configure] + tk_popup $win.config_menu $x $y + } + + ## Scroll folders ListBox and (Un)Map its scrollbar + # @parm Float frac0 - 1st fraction + # @parm Float frac0 - 2nd fraction + # @return void + public method dir_listbox_scroll {frac0 frac1} { + # Hide scrollbar + if {$frac0 == 0 && $frac1 == 1} { + if {[winfo ismapped $dir_listbox_scrollbar]} { + pack forget $dir_listbox_scrollbar + } + # Show scrollbar + } else { + if {![winfo ismapped $dir_listbox_scrollbar]} { + pack $dir_listbox_scrollbar -fill y -expand 1 -after $dir_listbox + } + $dir_listbox_scrollbar set $frac0 $frac1 + } + } + + ## Switch to mode "Short View" + # @return void + public method short_view {} { + if {!${::KIFSD::FSD::config(detailed_view)}} {return} + set ::KIFSD::FSD::config(detailed_view) 0 + $file_listbox configure -multicolumn 1 + set listbox_font $listbox_font_short + pack forget $file_listbox_header + reload + } + + ## Switch to mode "Detailed View" + # @return void + public method detail_view {} { + if {${::KIFSD::FSD::config(detailed_view)}} {return} + set ::KIFSD::FSD::config(detailed_view) 1 + $file_listbox configure -multicolumn 0 + set listbox_font $listbox_font_detailed + pack $file_listbox_header -before $file_listbox -fill x -expand 0 + reload + } + + ## Bookmark current folder + # @return void + public method add_bookmark {} { + lappend ::KIFSD::FSD::config(bookmarks) $current_directory + $bookmark_menu add command \ + -label $current_directory -compound left \ + -image ::ICONS::16::fileopen \ + -command "$this change_directory {$current_directory}" + uplevel #0 $bookmark_change_command + } + + ## Invoke bookmark editor + # @return void + public method edit_bookmarks {} { + # Create dialog window + set dialog [toplevel $win.edit_bookmarks -class {Edit bookmarks} -bg ${::COMMON_BG_COLOR}] + + # Create top frame (ListBox containing bookmarks and its scrollbar) + set top_frame [frame $dialog.top_frame] + set bookmark_edit_listbox [ListBox $top_frame.listbox \ + -yscrollcommand "$top_frame.scrollbar set" \ + -bg white -selectfill 1 -selectmode single \ + -highlightcolor {#BBBBFF} \ + ] + $bookmark_edit_listbox bindText <Double-1> "$this edit_bookmarks_edit" + pack $bookmark_edit_listbox -side left -fill both -expand 1 + pack [ttk::scrollbar $top_frame.scrollbar \ + -orient vertical \ + -command "$bookmark_edit_listbox yview" \ + ] -fill y -expand 1 + + # Fill up ListBox with defined bookmarks + foreach item ${::KIFSD::FSD::config(bookmarks)} { + $bookmark_edit_listbox insert end #auto -text $item + } + + ## Create bottom frame (buttons) + set bottom_frame [frame $dialog.bottom_frame] + # Button: "Remove" + pack [ttk::button $bottom_frame.remove \ + -text [::mc "Remove"] \ + -compound left \ + -image ::ICONS::16::editdelete \ + -command "$this edit_bookmarks_remove" \ + -width 8 \ + ] -side left -padx 2 + # Button: "Edit" + pack [ttk::button $bottom_frame.edit \ + -text [::mc "Edit"] \ + -compound left \ + -image ::ICONS::16::edit \ + -command "$this edit_bookmarks_edit" \ + -width 8 \ + ] -side left -padx 2 + # Button: "Up" + pack [ttk::button $bottom_frame.up \ + -text [::mc "Up"] \ + -compound left \ + -image ::ICONS::16::up \ + -command "$this edit_bookmarks_up" \ + -width 8 \ + ] -side left -padx 2 + # Button: "Down" + pack [ttk::button $bottom_frame.down \ + -text [::mc "Down"] \ + -compound left \ + -image ::ICONS::16::down \ + -command "$this edit_bookmarks_down" \ + -width 8 \ + ] -side left -padx 2 + # Button: "Ok" + pack [ttk::button $bottom_frame.ok \ + -text [::mc "Ok"] \ + -compound left \ + -image ::ICONS::16::ok \ + -width 8 \ + -command " + $this bookmark_edit_ok + grab release $dialog + destroy $dialog + " \ + ] -side right -padx 2 + # Button: "Cancel" + pack [ttk::button $bottom_frame.cancel \ + -text [::mc "Cancel"] \ + -compound left \ + -image ::ICONS::16::button_cancel \ + -width 8 \ + -command " + grab release $dialog + destroy $dialog + " \ + ] -side right -padx 2 + + # Pack dialog frames (top and bottom) + pack $top_frame -side top -fill both -expand 1 -pady 5 -padx 5 + pack $bottom_frame -side top -after $top_frame -fill x -expand 0 -pady 5 -padx 5 + + # Configure dialog window + wm iconphoto $dialog ::ICONS::16::bookmark + wm title $dialog "Edit bookmarks" + wm minsize $dialog 550 240 + wm geometry $dialog 550x340 + wm protocol $dialog WM_DELETE_WINDOW " + grab release $dialog + destroy $dialog + " + if {[winfo ismapped $win]} { + wm transient $dialog $win + } else { + wm transient $dialog . + } + grab $dialog + raise $dialog + tkwait window $dialog + } + + ## Auxiliary procedure for bookmark editor + # Remove current bookmark + # @return void + public method edit_bookmarks_remove {} { + set item [$bookmark_edit_listbox selection get] + if {$item == {}} {return} + $bookmark_edit_listbox delete $item + } + + ## Auxiliary procedure for bookmark editor + # Edit current bookmark + # @return void + public method edit_bookmarks_edit args { + set item [$bookmark_edit_listbox selection get] + if {$item == {}} {return} + set text [$bookmark_edit_listbox edit $item \ + [$bookmark_edit_listbox itemcget $item -text]] + if {$text == {}} {return} + $bookmark_edit_listbox itemconfigure $item -text $text + } + + ## Auxiliary procedure for bookmark editor + # Move current bookmark up + # @return void + public method edit_bookmarks_up {} { + set item [$bookmark_edit_listbox selection get] + if {$item == {}} {return} + if { + ![$bookmark_edit_listbox index $item] + || + ([llength [$bookmark_edit_listbox items]] < 2) + } then { + return + } + $bookmark_edit_listbox move $item [expr {[$bookmark_edit_listbox index $item] - 1}] + } + + ## Auxiliary procedure for bookmark editor + # Move current bookmark down + # @return void + public method edit_bookmarks_down {} { + set item [$bookmark_edit_listbox selection get] + if {$item == {}} {return} + if { + [$bookmark_edit_listbox index $item] + >= + ([llength [$quick_access_bar items]] - 1) + } then { + return + } + $bookmark_edit_listbox move $item [expr {[$bookmark_edit_listbox index $item] + 1}] + } + + ## Auxiliary procedure for bookmark editor + # Confirm bookmark edit dialog + # @return void + public method bookmark_edit_ok {} { + set ::KIFSD::FSD::config(bookmarks) {} + foreach item [$bookmark_edit_listbox items] { + lappend ::KIFSD::FSD::config(bookmarks) \ + [$bookmark_edit_listbox itemcget $item -text] + } + refresh_bookmarks + uplevel #0 $bookmark_change_command + } + + ## Reload items to bookmarks menu + # @return void + private method refresh_bookmarks {} { + if {[$bookmark_menu index end] > 2} { + $bookmark_menu delete 3 end + } + foreach dir ${::KIFSD::FSD::config(bookmarks)} { + $bookmark_menu add command \ + -label $dir -compound left \ + -image ::ICONS::16::fileopen \ + -command "$this change_directory {$dir}" + } + } + + ## Set command to execute when bookmark list changes + # @parm String command - Command to invoke from root namespace + # @return void + proc set_bookmark_change_command {command} { + set bookmark_change_command $command + } + + ## Unmap dialog window (but keep object alive) + # @return void + public method deactivate {} { + wm withdraw $win + } + + ## Activate (map) dialog window + # And wait until window is unmapped + # @return void + public method activate {} { + wm resizable $win 1 1 + wm deiconify $win + update idletasks + if {[winfo ismapped $right_paned_window]} { + $right_paned_window sash place 0 ${::KIFSD::FSD::config(right_PW_size)} 0 + } + if {[winfo ismapped $main_paned_window]} { + $main_paned_window sash place 0 ${::KIFSD::FSD::config(main_PW_size)} 0 + } + tkwait window $win + } + + ## Get selected item(s) + # @return String/List - Full path(s) to selected item(s) + public method get {} { + # Return List + if {$option_multiple} { + set result {} + foreach item [$file_listbox selection get] { + lappend result [file join $current_directory \ + [lindex [$file_listbox itemcget $item -data] 0]] + } + if {$result == {}} { + lappend result [file join $current_directory [$location_cb get]] + } + return $result + # Return String + } else { + return [file join $current_directory [$location_cb get]] + } + } + + ## Destroy dialog object + # @return void + public method close_dialog {} { + catch { + itcl::delete object $this + } + } + + ## Set command to invoke from root namespace on action "Ok" + # @parm String command - Command (with arguments) + # @return void + public method setokcmd {cmd} { + set ok_command $cmd + } + + ## Ok action - command for button "Ok" + # @return void + public method ok {} { + if {$option_autoclose} { + wm withdraw $win + set ok_command_tmp $ok_command + set ok_command {} + uplevel #0 $ok_command_tmp + close_dialog + } else { + uplevel #0 $ok_command + } + } + + ## Command for files ListBox horizontal scrollbar + # Takes any list of arguments (see code) + # @return void + public method file_listbox_hscrollbar_cmd args { + eval "$file_listbox xview $args" + eval "$file_listbox_header xview $args" + } + + ## Scroll files ListBox vertically + # This function manages scrollbar visibility + # @parm Float frac0 - 1st fraction (see Tk manual) + # @parm Float frac1 - 2nd fraction (see Tk manual) + # @return void + public method file_listbox_vscroll {frac0 frac1} { + # Hide scrollbar + if {$frac0 == 0 && $frac1 == 1} { + if {[winfo ismapped $file_listbox_vscrollbar]} { + pack forget $file_listbox_vscrollbar + update + } + + # Show scrollbar + } else { + if {![winfo ismapped $file_listbox_vscrollbar]} { + pack $file_listbox_vscrollbar \ + -after $file_listbox_frame \ + -fill y -expand 1 + update + } + $file_listbox_vscrollbar set $frac0 $frac1 + } + } + + ## Scroll files ListBox horizontaly + # This function manages scrollbar visibility + # @parm Float frac0 - 1st fraction (see Tk manual) + # @parm Float frac1 - 2nd fraction (see Tk manual) + # @return void + public method file_listbox_hscroll {frac0 frac1} { + + # Hide scrollbar + if {$frac0 == 0 && $frac1 == 1} { + if {[winfo ismapped $file_listbox_hscrollbar]} { + pack forget $file_listbox_hscrollbar + update + } + + # Show scrollbar + } else { + if {![winfo ismapped $file_listbox_hscrollbar]} { + pack $file_listbox_hscrollbar \ + -after $right_top_right_top_frame \ + -side bottom -fill x -expand 0 + update + } + catch { + $file_listbox_hscrollbar set $frac0 $frac1 + } + } + } + + ## Event handler for quick access bar ListBox, event <<ListboxSelect>> + # @return void + public method quick_access_bar_select {} { + if {$option_doubleclick} {return} + catch { + change_directory \ + [$quick_access_bar itemcget \ + [$quick_access_bar selection get] -data] + } + } + + ## Event handler for quick access bar ListBox, item event <Double-1> + # @parm String item - Item identifier + # @return void + public method quick_access_bar_doubleclick {item} { + if {!$option_doubleclick} {return} + catch { + change_directory \ + [$quick_access_bar itemcget \ + [$quick_access_bar selection get] -data] + } + } + + ## Event handler for directories ListBox, item event <Double-1> + # @parm String item - Item identifier + # @return void + public method dir_listbox_doubleclick {item} { + # Abort if dirs ListBox widget is no longer available + if {![winfo exists $dir_listbox]} { + return + } + if {!$option_doubleclick} {return} + catch { + change_directory [file join $current_directory \ + [$dir_listbox itemcget $item -text]] + } + } + + ## Event handler for directories ListBox, event <<ListboxSelect>> + # @return void + public method dir_listbox_select {} { + # Abort if dirs ListBox widget is no longer available + if {![winfo exists $dir_listbox]} { + return + } + if {$option_doubleclick} {return} + catch { + change_directory [file normalize [file join $current_directory \ + [$dir_listbox itemcget [$dir_listbox selection get] -text]]] + } + } + + ## Event handler for files ListBox, item event <Double-1> + # @parm String item - Item identifier + # @return void + public method file_listbox_doubleclick {item} { + # Abort if files ListBox widget is no longer available + if {![winfo exists $file_listbox]} { + return + } + + # Item directory or {} if it's a file + if {[catch { + set folder [lindex [$file_listbox itemcget $item -data] 1] + }]} then { + return + } + + if {!$option_fileson} { + if {$folder != {}} { + change_directory [file join $current_directory $folder] + } + return + } + + if {$option_doubleclick && !${::KIFSD::FSD::config(separate_folders)}} { + if {$folder != {}} { + change_directory [file join $current_directory $folder] + } + } + + if {!$option_doubleclick && ($folder == {})} { + ok + } + } + + ## Scroll files listbox + # Arguments are passed to yview or xview command + # @return void + public method file_listbox_scroll args { + if {${::KIFSD::FSD::config(detailed_view)}} { + set cmd {yview} + } else { + set cmd {xview} + } + eval "$file_listbox.c $cmd scroll $args" + } + + ## Event handler for files ListBox, event <<ListboxSelect>> + # @return void + public method file_listbox_select {} { + set selection [$file_listbox selection get] + + # Change directory if the item represents a directory + if {$option_fileson && !${::KIFSD::FSD::config(separate_folders)}} { + set folder [$file_listbox itemcget [lindex $selection end] -data] + set folder [lindex $folder 1] + if {$folder != {}} { + if {!$option_doubleclick} { + change_directory [file join $current_directory $folder] + } + return + } + } + + # Change content of location ComboBox if item is a file + if {[llength $selection] == 1} { + set index [lindex [$file_listbox itemcget $selection -data] [expr {$option_fileson ? 0 : 1}]] + if {$index != {..}} { + set index [lsearch -ascii [$location_cb cget -values] $index] + if {$index != -1} { + $location_cb current $index + } + } + } elseif {[llength $selection] > 1} { + set text {} + foreach item $selection { + append text "\"" + append text [lindex [$file_listbox itemcget $item -data] [expr {$option_fileson ? 0 : 1}]] + append text "\" " + } + $location_cb set $text + } + } + + ## Reload content of quick access bar ListBox + # @return void + private method refresh_quick_access_bar {} { + # Remove existing items + $quick_access_bar delete [$quick_access_bar items] + + # Create new items + foreach item ${::KIFSD::FSD::config(quick_access_bar_data)} { + # Determinate item icon + switch -- [lindex $item 0] { + 0 {set image hdd_unmount} + 1 {set image folder_home} + 2 {set image desktop} + 3 {set image bookmark_folder} + } + # Insert item + $quick_access_bar insert end #auto \ + -font $quick_nav_panel_font \ + -image ::ICONS::22::$image \ + -text [lindex $item 1] \ + -data [lindex $item 2] \ + } + } + + ## Invoke popup menu for ListBox of Quick access bar + # @parm Int x - Relative position of mouse pointer + # @parm Int y - Relative position of mouse pointer + # @return void + public method quick_access_bar_menu {x y} { + if {$item_menu_request} { + set item_menu_request 0 + return + } + catch { + tk_popup $win.quick_access_panel_menu $x $y + } + } + + ## Invoke popup menu for particular item in ListBox of Quick access bar + # @parm Int x - Relative position of mouse pointer + # @parm Int y - Relative position of mouse pointer + # @parm String item - Item identifier + # @return void + public method quick_access_bar_item_menu {x y item} { + set item_menu_request 1 + set current_item $item + set current_item_index [$quick_access_bar index $item] + set len [llength [$quick_access_bar items]] + + # Enable / Disabled entry "Move down" + if {$current_item_index >= ($len - 1)} { + $win.quick_access_panel_item_menu entryconfigure [mc "Move down"] -state disabled + } else { + $win.quick_access_panel_item_menu entryconfigure [mc "Move down"] -state normal + } + + # Enable / Disabled entry "Move up" + if {!$current_item_index || ($len < 2)} { + $win.quick_access_panel_item_menu entryconfigure [mc "Move up"] -state disabled + } else { + $win.quick_access_panel_item_menu entryconfigure [mc "Move up"] -state normal + } + + # Invoke the menu + tk_popup $win.quick_access_panel_item_menu $x $y + } + + ## Move current item in quick access bar down + # @return void + public method quick_access_panel_down {} { + # Check if the item is not the topmost one + if {$current_item_index >= ([llength [$quick_access_bar items]] - 1)} { + return + } + + set ::KIFSD::FSD::config(quick_access_bar_data) [lreplace \ + ${::KIFSD::FSD::config(quick_access_bar_data)} \ + $current_item_index [expr {$current_item_index + 1}] \ + [lindex ${::KIFSD::FSD::config(quick_access_bar_data)} \ + [expr {$current_item_index + 1}]] \ + [lindex ${::KIFSD::FSD::config(quick_access_bar_data)} \ + $current_item_index] + ] + refresh_quick_access_bar + } + + ## Move current item in quick access bar up + # @return void + public method quick_access_panel_up {} { + # Check if the item is not the bottommost one + if {!$current_item_index || ([llength [$quick_access_bar items]] < 2)} { + return + } + + set ::KIFSD::FSD::config(quick_access_bar_data) [lreplace \ + ${::KIFSD::FSD::config(quick_access_bar_data)} \ + [expr {$current_item_index - 1}] $current_item_index \ + [lindex ${::KIFSD::FSD::config(quick_access_bar_data)} \ + $current_item_index] \ + [lindex ${::KIFSD::FSD::config(quick_access_bar_data)} \ + [expr {$current_item_index - 1}]] + ] + refresh_quick_access_bar + } + + ## Invoke dialog to add entry to quick access bar + # @return void + public method quick_access_panel_add_entry {} { + set data [qa_panel_dialog "Add entry" {3} [::mc "New entry"] {~}] + if {![string length [lindex $data 1]]} {return} + if {![string length [lindex $data 2]]} {return} + lappend ::KIFSD::FSD::config(quick_access_bar_data) $data + refresh_quick_access_bar + } + + ## Invoke dialog to edit current entry in quick access bar + # @return void + public method quick_access_panel_edit_entry {} { + set data [lindex ${::KIFSD::FSD::config(quick_access_bar_data)} $current_item_index] + set data [qa_panel_dialog "Edit entry" [lindex $data 0] [lindex $data 1] [lindex $data 2]] + if {![string length [lindex $data 1]]} {return} + if {![string length [lindex $data 2]]} {return} + set ::KIFSD::FSD::config(quick_access_bar_data) [lreplace \ + ${::KIFSD::FSD::config(quick_access_bar_data)} \ + $current_item_index $current_item_index $data \ + ] + refresh_quick_access_bar + } + + ## Select icon in quick access bar edit dialog + # @parm Int index - Icon index [0; 4] + # @return void + public method qa_panel_dialog_icon {index} { + for {set i 0} {$i < 4} {incr i} { + ${win}.qa_panel_dialog.labelframe.button_$i configure -style Flat.TButton + } + ${win}.qa_panel_dialog.labelframe.button_$index configure -style TButton + set ::KIFSD::FSD::qa_panel_dialog_icon $index + } + + ## EntryBox validator + # If the content was an empty string then set entry background color to red + # @parm Widget widget - EntryBox widget + # @parm String content - EntryBox content + # @return Bool - Always 1 + proc not_empty_entry_validator {widget content} { + if {![string length $content]} { + $widget configure -style StringNotFound.TEntry + } else { + $widget configure -style TEntry + } + return 1 + } + + ## Invoke dialog for editing entries in the quick access bar + # Auxiliary procedure for: + # * quick_access_panel_add_entry + # * quick_access_panel_edit_entry + # @parm String title - Dialog title + # @parm Int icon - Icon number [0;3] + # @parm String name - Item name + # @parm String url - Target URL + # @return List - {new_icon_number new_name new_url} + private method qa_panel_dialog {title icon name url} { + # Create dialog window + set dialog [toplevel ${win}.qa_panel_dialog -class {Configuration dialog} -bg ${::COMMON_BG_COLOR}] + + # Set dialog variables + set ::KIFSD::FSD::qa_panel_dialog_icon $icon + set ::KIFSD::FSD::qa_panel_dialog_name_entry $name + set ::KIFSD::FSD::qa_panel_dialog_url_entry $url + + ## Create main frame (Name: and URL:) + set mid_frame [frame $dialog.middle] + # Label: "Name" + grid [label $mid_frame.name_lbl \ + -text [::mc "Name"] \ + ] -row 0 -column 0 -sticky w + # Label: "URL" + grid [label $mid_frame.url_lbl \ + -text [::mc "URL"] \ + ] -row 1 -column 0 -sticky w + # EntryBox: "Name" + grid [ttk::entry $mid_frame.name_entry \ + -width 1 \ + -validate all \ + -validatecommand "::KIFSD::FSD::not_empty_entry_validator %W %P" \ + -textvariable ::KIFSD::FSD::qa_panel_dialog_name_entry \ + ] -row 0 -column 1 -sticky we + # EntryBox: "URL" + grid [ttk::entry $mid_frame.url_entry \ + -width 1 \ + -validate all \ + -textvariable ::KIFSD::FSD::qa_panel_dialog_url_entry \ + -validatecommand "::KIFSD::FSD::dir_validate {} %W %P" \ + ] -row 1 -column 1 -sticky we + grid columnconfigure $mid_frame 1 -weight 1 + pack $mid_frame -padx 10 -pady 5 -fill x -expand 1 + + # Create frame for selecting icon + pack [ttk::labelframe $dialog.labelframe \ + -text [::mc "Icon"] \ + ] -fill none -expand 1 -anchor w -padx 10 + foreach icon {hdd_unmount folder_home desktop bookmark_folder} index {0 1 2 3} { + pack [ttk::button $dialog.labelframe.button_$index \ + -image ::ICONS::22::$icon \ + -command "$this qa_panel_dialog_icon $index" \ + -width 6 \ + -style Flat.TButton \ + ] -side left -padx 5 -pady 5 + } + $dialog.labelframe.button_${::KIFSD::FSD::qa_panel_dialog_icon} \ + configure -style TButton + + ## Create bottom frame (Buttons "Ok" and "Cancel") + set bot_frame [frame $dialog.bot] + # Button: "Ok" + pack [ttk::button $bot_frame.ok \ + -text [::mc "Ok"] \ + -compound left \ + -image ::ICONS::16::ok \ + -command " + if \[string length \${::KIFSD::FSD::qa_panel_dialog_name_entry}\] { + if \[string length \${::KIFSD::FSD::qa_panel_dialog_url_entry}\] { + grab release $dialog + destroy $dialog + } + }" \ + ] -side left -fill none -expand 0 -padx 2 + # Button: "Cancel" + pack [ttk::button $bot_frame.cancel \ + -text [::mc "Cancel"] \ + -compound left \ + -image ::ICONS::16::button_cancel \ + -command " + set ::KIFSD::FSD::qa_panel_dialog_url_entry {} + set ::KIFSD::FSD::qa_panel_dialog_name_entry {} + set ::KIFSD::FSD::qa_panel_dialog_icon {} + grab release $dialog + destroy $dialog" \ + ] -side left -fill none -expand 0 -padx 2 + pack $bot_frame -anchor e -padx 10 -pady 5 + + # Configure dialog window + wm title $dialog $title + wm resizable $dialog 0 0 + wm geometry $dialog 380x160 + wm protocol $dialog WM_DELETE_WINDOW " + set ::KIFSD::FSD::qa_panel_dialog_url_entry {} + set ::KIFSD::FSD::qa_panel_dialog_name_entry {} + set ::KIFSD::FSD::qa_panel_dialog_icon {} + grab release $dialog + destroy $dialog + " + wm transient $dialog $win + grab $dialog + raise $dialog + focus -force $mid_frame.name_entry + tkwait window $dialog + + # Return results + return [list \ + ${::KIFSD::FSD::qa_panel_dialog_icon} \ + ${::KIFSD::FSD::qa_panel_dialog_name_entry} \ + ${::KIFSD::FSD::qa_panel_dialog_url_entry} \ + ] + } + + ## Remove entry from quick access bar (popup menu action) + # @return void + public method quick_access_panel_remove_entry {} { + set ::KIFSD::FSD::config(quick_access_bar_data) \ + [lreplace ${::KIFSD::FSD::config(quick_access_bar_data)} \ + $current_item_index $current_item_index] + refresh_quick_access_bar + } + + ## Invoke popup menu for directories ListBox + # @parm Int x - Relative position of mouse pointer + # @parm Int y - Relative position of mouse pointer + # @return void + public method dir_listbox_menu {x y} { + if {$item_menu_request} { + set item_menu_request 0 + return + } + foreach entry {Rename Delete Properties {Bookmark folder}} { + $win.listbox_menu entryconfigure [mc $entry] -state disabled + } + tk_popup $win.listbox_menu $x $y + } + + ## Invoke popup menu for item in directories ListBox + # @parm Int x - Relative position of mouse pointer + # @parm Int y - Relative position of mouse pointer + # @parm String item - Item identifier + # @return void + public method dir_listbox_item_menu {x y item} { + set item_menu_request 1 + foreach entry {Rename Delete Properties {Bookmark folder}} { + $win.listbox_menu entryconfigure [mc $entry] -state normal + } + set cur_listbox {dir} + set current_item $item + set current_item_index [$dir_listbox index $item] + tk_popup $win.listbox_menu $x $y + } + + ## Invoke popup menu for files ListBox + # @parm Int x - Relative position of mouse pointer + # @parm Int y - Relative position of mouse pointer + # @return void + public method file_listbox_menu {x y} { + if {$item_menu_request} { + set item_menu_request 0 + return + } + foreach entry {Rename Delete Properties} { + $win.listbox_menu entryconfigure [mc $entry] -state disabled + } + $win.listbox_menu entryconfigure [mc {Bookmark folder}] -state normal + tk_popup $win.listbox_menu $x $y + } + + ## Invoke popup menu for item in files ListBox + # @parm Int x - Relative position of mouse pointer + # @parm Int y - Relative position of mouse pointer + # @parm String item - Item identifier + # @return void + public method file_listbox_item_menu {x y item} { + set item_menu_request 1 + set current_item $item + set current_item_index [$dir_listbox index $item] + foreach entry {Rename Delete Properties {Bookmark folder}} { + $win.listbox_menu entryconfigure [mc $entry] -state normal + } + set cur_listbox {file} + set current_item $item + set current_item_index [$dir_listbox index $item] + tk_popup $win.listbox_menu $x $y + } + + ## Remove selected file or directory + # @return void + public method delete_item_command {} { + # Determinate URL to delete + if {$cur_listbox == {dir}} { + set filename [$dir_listbox itemcget $current_item -text] + } else { + set data [$file_listbox itemcget $current_item -data] + if {[lindex $data 0] == {}} { + set filename [lindex $data 1] + } else { + set filename [lindex $data 0] + } + } + if {$filename == {}} {return} + + # Invoke confirmation dialog + if {[tk_messageBox \ + -parent $win \ + -type yesno \ + -icon question \ + -title [::mc "Delete file"] \ + -message [::mc "Do you really want to delete file:\n%s" $filename]] + == + {yes} + } then { + # Delete file/directory (+ invoke error dialog) + if {[catch {file delete -force -- [file join $current_directory $filename]}]} { + tk_messageBox \ + -parent $win \ + -type ok \ + -icon warning \ + -title [::mc "Permission denied"] \ + -message [::mc "Unable to remove file:\n%s" $filename] + } + } + reload + } + + ## Bookmark selected folder + # @return void + public method item_bookmark_add {} { + set tmp $current_directory + if {$cur_listbox == {dir}} { + set current_directory [file join $current_directory \ + [$dir_listbox itemcget $current_item -text]] + } + add_bookmark + set current_directory $tmp + } + + ## Rename selected file or directory + # @return void + public method rename_item_command {} { + if {$cur_listbox == {dir}} { + set listbox $dir_listbox + } else { + set listbox $file_listbox + } + + # Determinate old and new name + set original [$listbox itemcget $current_item -text] + set newname [$listbox edit $current_item \ + [$listbox itemcget $current_item -text]] + if {$newname == {}} { + return + } + + # Adjust old and new name + set original [file join $current_directory $original] + set newname [file join $current_directory $newname] + + # Rename file + if {[catch {file rename -force $original $newname}]} { + tk_messageBox \ + -parent $win \ + -type ok \ + -icon warning \ + -title [::mc "Permission denied"] \ + -message [::mc "Unable to rename file:\n%s" $original] + } + reload + } + + ## Invoke item properties dialog + # @return void + public method properties_item_command {} { + # Determinate item name, type (File or Directory) + if {$cur_listbox == {dir}} { + set name [$dir_listbox itemcget $current_item -text] + set type "Directory" + } else { + set name [$file_listbox itemcget $current_item -data] + if {[lindex $name 0] == {}} { + set name [lindex $name 1] + set type "Directory" + } else { + set name [lindex $name 0] + set type "File" + } + } + + # Determinate full name + set fullname [file join $current_directory $name] + if {![file exists $fullname]} { + tk_messageBox \ + -parent $win \ + -type ok \ + -icon warning \ + -title [::mc "Unknown Error"] \ + -message [::mc "This file apparently does not exist"] + return + } + # Determinate size + set size [file size $fullname] + append size { B} + # Determinate time of the last mofication + set modified [clock format [file mtime $fullname] -format {%D %R}] + # Determinate time of the last access + set accessed [clock format [file atime $fullname] -format {%D %R}] + # Determinate group, owner and permissions + if {!$::MICROSOFT_WINDOWS} { ;# Microsoft Windows has no file rights (compatible with posix rights) + set perms [file attributes $fullname] + set group [lindex $perms 1] + set owner [lindex $perms 3] + set perms [lindex $perms 5] + set perms [string range $perms {end-3} end] + foreach var {ur uw ux gr gw gx or ow ox} \ + mask {0400 0200 0100 040 020 010 04 02 01} \ + { + set ::KIFSD::FSD::item_properties($var) [expr {($perms & $mask) > 0}] + } + } + + # Create dialog window and Notebook + set dialog [toplevel $win.properties_dialog -class {Configuration dialog} -bg ${::COMMON_BG_COLOR}] + set nb [NoteBook $dialog.nb -bg ${::COMMON_BG_COLOR}] + $nb insert end general -text "General" + if {!$::MICROSOFT_WINDOWS} { ;# Microsoft Windows has no file rights (compatible with posix rights) + $nb insert end permission -text "Permissions" + } + $nb raise general + + ## Create GUI elements for tag "General" + set frame [frame [$nb getframe general].frame] + pack $frame -side top -anchor n -fill x -expand 1 + # Name: + set row 0 + grid [label $frame.lbl_$row \ + -text [::mc "Name:"] -anchor w \ + -font $listbox_font_short \ + ] -column 0 -row $row -sticky w -pady 3 + set ::KIFSD::FSD::item_properties(name) $name + grid [ttk::entry $frame.val_lbl_$row \ + -validate all \ + -textvariable ::KIFSD::FSD::item_properties(name) \ + -validatecommand "::KIFSD::FSD::not_empty_entry_validator %W %P" \ + ] -column 1 -row $row -sticky w -pady 3 + # Type, Location, Size, Modified, Accessed + incr row + foreach lbl [list "Type" "Location" "Size" "Modified" "Accessed"] \ + value [list $type $current_directory $size $modified $accessed] \ + { + grid [label $frame.lbl_$row \ + -text "[::mc $lbl]:" -anchor w \ + -font $listbox_font_short \ + ] -column 0 -row $row -sticky w -pady 3 + grid [label $frame.val_lbl_$row \ + -text $value -anchor w \ + ] -column 1 -row $row -sticky w -pady 3 + incr row + } + grid columnconfigure $frame 0 -minsize 100 + + ## Create GUI elements for tag "Permissions" + if {!$::MICROSOFT_WINDOWS} { ;# Microsoft Windows has no file rights (compatible with posix rights) + set frame [$nb getframe permission] + set ap_frame [ttk::labelframe $frame.ap_frame \ + -text [::mc "Access permissions"] \ + ] + set i 0 + foreach text [list "Class" "Read" "Write" "Exec" "Owner" "Group" "Others"] \ + row {0 0 0 0 1 2 3} \ + col {0 1 2 3 0 0 0} \ + { + grid [label $ap_frame.lbl_$i \ + -text [::mc $text] \ + -justify center \ + ] -row $row -column $col -sticky w -padx 4 -pady 4 + incr i + } + foreach var {ur uw ux gr gw gx or ow ox} \ + row {1 1 1 2 2 2 3 3 3} \ + col {1 2 3 1 2 3 1 2 3} \ + { + grid [checkbutton $ap_frame.check_$i \ + -variable ::KIFSD::FSD::item_properties($var) + ] -row $row -column $col + incr i + } + + grid columnconfigure $ap_frame 0 -minsize 70 + grid columnconfigure $ap_frame 0 -weight 1 + pack $ap_frame -side top -fill x -expand 1 -padx 5 -pady 5 -anchor nw + + set own_frame [ttk::labelframe $frame.own_frame \ + -text [::mc "Ownership"] \ + ] + grid [label $own_frame.owner_lbl \ + -text [::mc "Owner"] -font $listbox_font_short \ + ] -row 0 -column 0 -padx 10 -pady 3 -sticky w + grid [label $own_frame.owner_val_lbl \ + -text $owner -anchor w \ + ] -row 0 -column 1 -padx 10 -pady 3 -sticky we + grid [label $own_frame.group_lbl \ + -text [::mc "Group"] -font $listbox_font_short \ + ] -row 1 -column 0 -padx 10 -pady 3 -sticky w + grid [label $own_frame.group_val_lbl \ + -text $group -anchor w \ + ] -row 1 -column 1 -padx 10 -pady 3 -sticky we + grid columnconfigure $own_frame 0 -minsize 70 + grid columnconfigure $own_frame 1 -weight 1 + pack $own_frame -side top -fill x -expand 1 -padx 5 -pady 5 + } + + # Create bottom frame (buttons: "Ok" and "Cancel") + set bottom_frame [frame $dialog.bottom_frame] + pack [ttk::button $bottom_frame.ok \ + -text [::mc "Ok"] \ + -compound left \ + -image ::ICONS::16::ok \ + -command "$this properties_ok $dialog $fullname" \ + ] -side left -padx 2 + pack [ttk::button $bottom_frame.cancel \ + -text [::mc "Cancel"] \ + -compound left \ + -image ::ICONS::16::button_cancel \ + -command " + grab release $dialog + destroy $dialog + " \ + ] -side left -padx 2 + + # Pack notebook and bottom frame + pack $nb -fill both -expand 1 -padx 10 -pady 5 + pack $bottom_frame -anchor e -after $nb -padx 10 -pady 5 + + # Configure dialog window + wm title $dialog [::mc "Item properties"] + wm minsize $dialog 280 320 + wm protocol $dialog WM_DELETE_WINDOW " + grab release $dialog + destroy $dialog" + wm transient $dialog $win + grab $dialog + raise $dialog + tkwait window $dialog + } + + ## Confirm item properties dialog + # @parm Widget dialog - Dialog window + # @parm String file - File URL + # @return void + public method properties_ok {dialog file} { + set error 0 + set perm 0 + + if {!$::MICROSOFT_WINDOWS} { ;# Microsoft Windows has no file rights (compatible with posix rights) + foreach var {ur uw ux gr gw gx or ow ox} \ + val {256 128 64 32 16 8 4 2 1} { + if {$::KIFSD::FSD::item_properties($var)} { + incr perm $val + } + } + if {[catch {file attributes $file -permissions "0[format {%o} $perm]"}]} { + set error 1 + tk_messageBox \ + -type ok \ + -icon warning \ + -parent $dialog \ + -title [::mc "Permission denied"] \ + -message [::mc "Unable to change permissions for file:\n%s" [file tail $file]] + } + } + set dir [file dirname $file] + + if {${::KIFSD::FSD::item_properties(name)} != [file tail $file]} { + if {[catch { + file rename -force -- \ + $file [file join $dir \ + ${::KIFSD::FSD::item_properties(name)}]}] + } then { + set error 1 + tk_messageBox \ + -type ok \ + -icon warning \ + -parent $dialog \ + -title [::mc "Permission denied"] \ + -message [::mc "Unable to rename file:%s" "\n[file tail $file]\n\t=>\n${::KIFSD::FSD::item_properties(name)}"] + } + reload + } + + if {!$error} { + grab release $dialog + destroy $dialog + } + } + + ## Validate EntryBox containing directory location (set background color: red/white) + # @parm widget combobox - ComboBox widget or {} + # @parm Widget widget - EntryBox widget + # @parm String content - EntryBox content + # @return Bool - Always 1 + proc dir_validate {combobox widget content} { + if {![file exists $content] || ![file isdirectory $content]} { + if {$combobox != {}} { + $combobox configure -style FSD_RedBg.TCombobox + } else { + $widget configure -style FSD_RedBg.TEntry + } + } else { + if {$combobox != {}} { + $combobox configure -style TCombobox + } else { + $widget configure -style TEntry + } + + # Fill directory location combobox + if {$combobox != {}} { + set folder $content + set values {} + while {1} { + lappend values $folder + if {$folder == [file separator]} {break} + if {$::MICROSOFT_WINDOWS} { + if {[regexp {^\w+:[\\\/]?$} $folder]} {break} + } + set folder [file normalize [file join $folder {..}]] + } + foreach folder [::KIFSD::FSD::dir_cmd $content 1] { + if {$folder == {..}} {continue} + lappend values [file join $content $folder] + } + if {$::MICROSOFT_WINDOWS} { ;# Include drive letters on Microsoft Windows + foreach drive_letter {A B C D E F G H I J K L M N O P Q R S T U V W X Y Z} { + if {[file exists "${drive_letter}:/"]} { + lappend values "${drive_letter}:/" + } + } + } + $combobox configure -values $values + } + } + return 1 + } + + ## Reload content of directories ListBox and files ListBox + # @param List args - all arguments are ignored + # @return void + public method reload {args} { + update idletasks + change_directory $current_directory + } + + ## Modify command for directory ComboBox + # @return void + public method dir_cb_modify {} { + change_directory [$dir_combobox get] + } + + ## Go to parrent folder + # @return void + public method up {} { + change_directory [file normalize [file join $current_directory {..}]] + } + + ## Go back in history + # @return void + public method back {} { + # Determinate new folder + set folder [lindex $back_history end] + if {$folder == {}} {return} + + # Adjust backward and forward history + set back_history [lreplace $back_history end end] + lappend forward_history $current_directory + + # Make backup copy of backward and forward history + set tmp_forw_hist $forward_history + set tmp_back_hist $back_history + + # Change current directory + change_directory $folder + + # Restore backward and forward history + set forward_history $tmp_forw_hist + set back_history $tmp_back_hist + + # Enable / Disable buttons "Back" and "Forward" + if {![llength $back_history]} { + $toolbar.back configure -state disabled + $win.listbox_menu entryconfigure [mc "Back"] -state disabled + } else { + $toolbar.back configure -state normal + $win.listbox_menu entryconfigure [mc "Back"] -state normal + } + $win.listbox_menu entryconfigure [mc "Forward"] -state normal + $toolbar.forward configure -state normal + } + + ## Go forward in history + # @return void + public method forward {} { + # Determinate new folder + set folder [lindex $forward_history end] + if {$folder == {}} {return} + + # Adjust backward and forward history + set forward_history [lreplace $forward_history end end] + lappend back_history $current_directory + + # Make backup copy of backward and forward history + set tmp_forw_hist $forward_history + set tmp_back_hist $back_history + + # Change current directory + change_directory $folder + + # Restore backward and forward history + set forward_history $tmp_forw_hist + set back_history $tmp_back_hist + + # Enable / Disable buttons "Back" and "Forward" + if {![llength $forward_history]} { + $toolbar.forward configure -state disabled + $win.listbox_menu entryconfigure [mc "Forward"] -state disabled + } else { + $toolbar.forward configure -state normal + $win.listbox_menu entryconfigure [mc "Forward"] -state normal + } + $toolbar.back configure -state normal + $win.listbox_menu entryconfigure [mc "Back"] -state normal + } + + ## Invoke dialog to create a new directory + # @return void + public method newdir {} { + # Create dialog window + set dialog [toplevel $win.new_dir -class {New directory} -bg ${::COMMON_BG_COLOR}] + + # Create dialog header and EntryBox + pack [label $dialog.header -justify left -text [mc "Create new folder in:\n%s" $current_directory]] \ + -side top -anchor w -padx 15 -pady 5 + pack [ttk::entry $dialog.entry \ + ] -side top -fill x -expand 1 -padx 5 -pady 5 + + # Bind button enter to confirmation action + bind $dialog.entry <Return> "[list $this create_new_folder]; break" + bind $dialog.entry <KP_Enter> "[list $this create_new_folder]; break" + + # Create bottom frame (Buttons: "Clear", "Ok" and "Cancel") + set button_frame [frame $dialog.bottom] + pack [ttk::button $button_frame.clear \ + -text [mc "Clear"] \ + -compound left \ + -image ::ICONS::16::clear_left \ + -command "$dialog.entry delete 0 end" \ + ] -side left -expand 0 -padx 2 + pack [ttk::button $button_frame.ok \ + -text [mc "Ok"] \ + -compound left \ + -image ::ICONS::16::ok \ + -command [list $this create_new_folder] \ + ] -side left -expand 0 -padx 2 + pack [ttk::button $button_frame.cancel \ + -text [mc "Cancel"] \ + -compound left \ + -image ::ICONS::16::button_cancel \ + -command " + grab release $dialog + destroy $dialog + " \ + ] -side left -expand 0 -padx 2 + pack $button_frame -side bottom -anchor e -expand 0 -padx 5 -pady 5 + + # Configure dialog window + wm iconphoto $dialog ::ICONS::16::folder_new + wm title $dialog [mc "New folder"] + wm resizable $dialog 1 0 + wm minsize $dialog 340 120 + wm geometry $dialog 340x120 + wm protocol $dialog WM_DELETE_WINDOW " + grab release $dialog + destroy $dialog + " + wm transient $dialog $win + grab $dialog + raise $dialog + focus -force $dialog.entry + tkwait window $dialog + } + + ## Confirm dialog "Create new folder" + # @return void + public method create_new_folder {} { + set dialog ${win}.new_dir + set folder [$dialog.entry get] + set error 0 + + if {$folder == {}} { + set error 1 + } + if {$error || [catch {file mkdir [file join $current_directory $folder]}]} { + tk_messageBox \ + -parent $dialog \ + -icon warning \ + -type ok \ + -title [mc "Unable to create folder"] \ + -message [mc "Unable to create the specified folder"] + } else { + grab release $dialog + destroy $dialog + reload + } + } + + ## Sort the given list of strings + # This procedure is closely related to inner logic of this + #+ class and it is difficult to properly explain its function + # @parm List items - List to sort + # @return void + proc sort_items {items} { + # Determinate sorting order + if {${::KIFSD::FSD::config(reverse_sorting)}} { + set order "-decreasing" + } else { + set order "-increasing" + } + + if {${::KIFSD::FSD::config(sorting)} == {name}} { + if {${::KIFSD::FSD::config(case_insensitive)}} { + set method "-dictionary" + } else { + set method "-ascii" + } + return [lsort $method $order $items] + } else { + if {${::KIFSD::FSD::config(sorting)} == {size}} { + # Sort by size + set index 2 + } else { + # Sort by date + set index 1 + } + set items [lsort -index $index $order $items] + + set result {} + foreach file $items { + lappend result [lindex $file 0] + } + return $result + } + } + + ## Get unsorted list of subdirectories in the given directory + # @parm String dir - Directory + # @return List - List of relative URLs + proc get_dirs_simple {dir} { + # Search for directories + set result [list] + catch { ;# For Microsoft Windows it has to be enclosed by catch + set result [glob -nocomplain -tails -directory $dir -types d *] + } + + # Include hidden directories + if {${::KIFSD::FSD::config(show_hidden_files)}} { + catch { ;# For Microsoft Windows it has to be enclosed by catch + set result [concat $result [glob -nocomplain -tails -directory $dir -types {d hidden} *]] + } + + # Filter "." and ".." + set foo_idx [lsearch $result {..}] + if {$foo_idx != -1} { + set result [lreplace $result $foo_idx $foo_idx] + set foo_idx [lsearch $result {.}] + if {$foo_idx != -1} { + set result [lreplace $result $foo_idx $foo_idx] + } + } + } + + return $result + } + + ## Get unsorted list of subdirectories in the given directory + # @parm String dir - Directory + # @return List - {{relative_URL mtime size_in_B} ... } + proc get_dirs_extended {dir} { + set result {} + + # Search for directories + catch { ;# For Microsoft Windows it has to be enclosed by catch + foreach file [glob -nocomplain -tails -directory $dir -types d *] { + lappend result [list $file [file mtime [file join $dir $file]] 0] + } + } + + # Include hidden directories + if {${::KIFSD::FSD::config(show_hidden_files)}} { + catch { ;# For Microsoft Windows it has to be enclosed by catch + foreach file [glob -nocomplain -tails -directory $dir -types {d hidden} *] { + # Filter "." and ".." + if {$file == {.} || $file == {..}} { + continue + } + # Translate to full URL + lappend result [list $file [file mtime [file join $dir $file]] 0] + } + } + } + + return $result + } + + ## Get unsorted list of files in the given directory matching the given GLOB + # @parm String dir - Directory + # @parm GLOB mask - Glob expression + # @return List - List of relative URLs + proc get_files_simple {dir mask} { + set result [list] + catch { ;# For Microsoft Windows it has to be enclosed by catch + set result [glob -nocomplain -tails -directory $dir -types f $mask] + } + if {${::KIFSD::FSD::config(show_hidden_files)}} { + catch { ;# For Microsoft Windows it has to be enclosed by catch + set result [concat $result \ + [glob -nocomplain -tails -directory $dir -types {f hidden} $mask]] + } + } + return $result + } + + ## Get unsorted list of files in the given directory matching the given GLOB + # @parm String dir - Directory + # @parm GLOB mask - Glob expression + # @return List - {{relative_URL mtime size_in_B} ... } + proc get_files_extended {dir mask} { + set result {} + + # Search for files matching the given GLOB + catch { ;# For Microsoft Windows it has to be enclosed by catch + foreach file [glob -nocomplain -tails -directory $dir -types f $mask] { + if {[catch { + lappend result [list \ + $file \ + [file mtime [file join $dir $file]] \ + [file size [file join $dir $file]] \ + ] + }]} then { + lappend result [list $file 0 0] + } + } + } + + # Include hidden files + if {${::KIFSD::FSD::config(show_hidden_files)}} { + catch { ;# For Microsoft Windows it has to be enclosed by catch + foreach file [glob -nocomplain -tails -directory $dir -types {f hidden} $mask] { + if {[catch { + lappend result [list \ + $file \ + [file mtime [file join $dir $file]] \ + [file size [file join $dir $file]] \ + ] + }]} then { + lappend result [list $file 0 0] + } + } + } + } + return $result + } + + ## Get list of items to load to directories ListBox + # @parm String dir - Source directory + # @parm Bool no_detail=0 - No details + # @return List - {text text ...} + proc dir_cmd {dir {no_detail 0}} { + # Normalize directory and determinate its parent + set dir [file normalize $dir] + if {$dir != {/}} { + set parent {..} + } else { + set parent {} + } + + + if {${::KIFSD::FSD::config(sorting)} == {name}} { + set result [sort_items [get_dirs_simple $dir]] + } else { + set result [sort_items [get_dirs_extended $dir]] + } + + if {!$no_detail && ${::KIFSD::FSD::config(detailed_view)}} { + return [concat $parent [add_details $result $dir]] + } else { + return [concat $parent $result] + } + } + + ## Get list of items to load to files ListBox + # @parm String dir - Source directory + # @parm GLOB mask - GLOB expression which must match each returned file + # @parm Bool no_detail=0 - Detailed view + # @return List - {text text ...} + proc file_cmd {dir mask {no_detail 0}} { + if {${::KIFSD::FSD::config(sorting)} == {name}} { + set result [sort_items [get_files_simple $dir $mask]] + } else { + set result [sort_items [get_files_extended $dir $mask]] + } + if {!$no_detail && ${::KIFSD::FSD::config(detailed_view)}} { + return [add_details $result $dir] + } else { + return $result + } + } + + ## Adjust list of files/directories returned by proc. file_cmd to + #+ format required to display in detailed view mode + # @parm List filelist - List returned by procedure file_cmd + # @parm String dir - Directory + # @return List - {{text text text ... } ... } + proc add_details {filelist dir} { + set result {} + foreach filename $filelist { + set line $filename + set fullfilename [file join $dir $filename] + if {[string length $line] > 31} { + set line [string range $line 0 27] + append line {...} + } + if {[catch { + append line [string repeat { } [expr {35 - [string length $line]}]] + set size [file size $fullfilename] + if {$size < 1024} { + append size { B} + } elseif {$size < 1048576} { + set size [expr {($size * 10) / 1024}] + if {$size > 1023} { + set size [expr {$size / 10}] + } else { + set size [string range $size 0 {end-1}].[string range $size end end] + } + append size { kB} + } elseif {$size < 1073741824} { + set size [expr {($size * 10) / 1048576}] + if {$size > 1023} { + set size [expr {$size / 10}] + } else { + set size [string range $size 0 {end-1}].[string range $size end end] + } + append size { MB} + } elseif {$size < 1099511627776} { + set size [expr {($size * 10) / 1073741824}] + if {$size > 1023} { + set size [expr {$size / 10}] + } else { + set size [string range $size 0 {end-1}].[string range $size end end] + } + append size { GB} + } else { + set size {>1TB} + } + }]} then { + append line { - ---- -------- -----} + } else { + if {!$::MICROSOFT_WINDOWS} { + append line [string repeat { } [expr {8 - [string length $size]}]] $size " " \ + [string range [lindex [file attributes $fullfilename] 5] {end-3} end] " " \ + [clock format [file mtime $fullfilename] -format {%D %R}] + } else { + append line [string repeat { } [expr {8 - [string length $size]}]] $size " " \ + [clock format [file mtime $fullfilename] -format {%D %R}] + } + } + lappend result [list $line $filename] + } + return $result + } + + ## Get list of items to load to files ListBox (mode "Separate folders" OFF) + # @parm String dir - Source directory + # @parm GLOB mask - GLOB expression which must match each returned file + # @return List - {text text ...} + proc dir_file_cmd {dir mask} { + set dir [file normalize $dir] + set result {} + + # Determinate list of directories + if {${::KIFSD::FSD::config(sorting)} == {name}} { + set result [concat [get_dirs_simple $dir] [get_files_simple $dir $mask]] + } else { + set result [concat [get_dirs_extended $dir] [get_files_extended $dir $mask]] + } + if {$dir != {/}} { + set parent [list [list {..} {u}]] + } else { + set parent {} + } + set tmp_result {} + + # Determinate list of files + if {${::KIFSD::FSD::config(detailed_view)}} { + foreach item [sort_items $result] { + if {![file exists [file join $dir $item]]} {continue} + if {[file isdirectory [file join $dir $item]]} { + lappend tmp_result [concat [add_details [list $item] $dir] d] + } else { + lappend tmp_result [concat [add_details [list $item] $dir] f] + } + } + } else { + foreach item [sort_items $result] { + if {![file exists [file join $dir $item]]} {continue} + if {[file isdirectory [file join $dir $item]]} { + lappend tmp_result [list $item d] + } else { + lappend tmp_result [list $item f] + } + } + } + return [concat $parent $tmp_result] + } + + ## Get configuration list for procedure load_config_array + # @return List - (List which specifies bookmarks, settings and such things) + proc get_config_array {} { + return [regsub -all "\n" [array get ::KIFSD::FSD::config] { }] + } + + ## Load configuration list returned by procedure get_config_array + # @parm List config - (List which specifies bookmarks, settings and such things) + # @return void + proc load_config_array {config} { + if {$config == {}} { + return + } + + if {[catch { + array set ::KIFSD::FSD::config $config + }]} then { + puts stderr "KI File Selection Dialog: Unable to load the given configuration string -- using default" + return 0 + } else { + return 1 + } + } + + ## Get descriptor of dialog window + # @return Widget - Dialog window + public method get_window_name {} { + return $win + } + + ## Determinate path to the "Desktop" folder. + # @return String - The path, e.g. "~/Arbeitsfläche" in case of German Ubuntu. + proc get_desktop_dir {} { + if {![catch { + set f [open "~/.config/user-dirs.dirs" "r"] + }]} then { + while {![eof $f]} { + set l [gets $f] + if {[string first "XDG_DESKTOP_DIR=" $l] != -1} { + if {[regexp {"[^\"]+"} $l d]} { + set d [string range $d 1 end-1] + regsub {\$HOME} $d {~} d + return $d + } + } + } + close $f + } + return "~/Desktop" + } +} + +## Text variables for dialog "Edit entry in Quick access bar" +set KIFSD::FSD::qa_panel_dialog_url_entry {} ;# Entry URL +set KIFSD::FSD::qa_panel_dialog_name_entry {} ;# Entry name +set KIFSD::FSD::qa_panel_dialog_icon {} ;# Icon number [0;3] + +## Dialog configuration array (these values are daults) + # Invalid configuration list may cause program error ! +array set KIFSD::FSD::config { + win_geometry {720x380} + detailed_view 0 + separate_folders 1 + quick_access_panel 1 + sorting name + reverse_sorting 0 + folders_first 1 + case_insensitive 1 + show_hidden_files 0 + right_PW_size 200 + main_PW_size 180 + bookmarks {} +} + +if {$::MICROSOFT_WINDOWS} { + set KIFSD::FSD::config(quick_access_bar_data) [subst { + {0 {System Drive ${::env(SystemDrive)}} {${::env(SystemDrive)}}} + {1 {Documents and Settings} {${::env(USERPROFILE)}}} + }] +} else { + set KIFSD::FSD::config(quick_access_bar_data) [subst { + {0 {/} {/}} + {0 {Removable media} {/media}} + {1 {Home} {~}} + {2 {Desktop} {[KIFSD::FSD::get_desktop_dir]}} + }] +} + +# >>> File inclusion guard +} +# <<< File inclusion guard diff --git a/lib/dialogues/my_tk_messageBox.tcl b/lib/dialogues/my_tk_messageBox.tcl new file mode 100644 index 0000000..0698e9e --- /dev/null +++ b/lib/dialogues/my_tk_messageBox.tcl @@ -0,0 +1,303 @@ +#!/usr/bin/tclsh +# Part of MCU 8051 IDE ( http://mcu8051ide.sf.net ) + +############################################################################ +# Copyright (C) 2009, 2010, 2011, 2012 by Martin Ošmera # +# martin.osmera@gmail.com # +# # +# This program is free software; you can redistribute it and#or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation; either version 2 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program; if not, write to the # +# Free Software Foundation, Inc., # +# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # +############################################################################ + +# >>> File inclusion guard +if { ! [ info exists _MY_TK_MESSAGEBOX_TCL ] } { +set _MY_TK_MESSAGEBOX_TCL _ +# <<< File inclusion guard + +# -------------------------------------------------------------------------- +# DESCRIPTION +# Replacement for Tk's tk_messageBox. Usage is the same as tk_messageBox, +# except for one thing, this one supports also "-icon ok". +# -------------------------------------------------------------------------- + +## This namespace implements dialog itself, but does not contain the + # "tk_messageBox" function to invoke it. This function is defined onwards, but + # still in this file. +namespace eval my_tk_messageBox { + ## Namespace variables + variable return_value {} ;# String: Dialog return value (e.g. "abort") + variable dialog ;# Widget: Dialog toplevel window + variable count 0 ;# Int: Counter of object instances + # Buttons available in the dialog + variable available_buttons { + abort retry ignore + ok cancel yes + no + } + # Icons for available buttons + variable button_icons { + button_cancel reload forward + ok cancel ok + no + } + + ## Invoke the dialog + # @parm Int arg_default - Number of default button (0..2) + # @parm String arg_icon - Dialog icon (one of values mentioned in variable button_icons) + # @parm String arg_message - Message to display to the user + # @parm Widget arg_parent - GUI parent + # @parm String arg_title - Dialog title + # @parm String arg_type - Name of big icon displyed beside the message (one of {error info question warning ok}) + # @return String - Dialog return value, name of pressed button + proc create {arg_default arg_icon arg_message arg_parent arg_title arg_type} { + variable return_value {} ;# String: Dialog return value (e.g. "abort") + variable button_icons ;# Icons for available buttons + variable available_buttons ;# Buttons available in the dialog + variable dialog ;# Widget: Dialog toplevel window + variable count ;# Int: Counter of object instances + + set dialog [toplevel .my_tk_messageBox_${count}] + set buttons [list] + incr count + + # Translate icon name + switch -- $arg_icon { + {error} { + set iconphoto {cancel} + set arg_icon {messagebox_critical} + } + {info} { + set iconphoto {info} + set arg_icon {messagebox_info} + } + {question} { + set iconphoto {help} + set arg_icon {help} + } + {warning} { + set iconphoto {status_unknown} + set arg_icon {messagebox_warning} + } + {ok} { + set iconphoto {ok} + set arg_icon {button_ok} + } + } + + # Determinate list of buttons + switch -- $arg_type { + {abortretryignore} { + set buttons [list abort retry ignore] + } + {ok} { + set buttons [list ok] + } + {okcancel} { + set buttons [list ok cancel] + } + {retrycancel} { + set buttons [list retry cancel] + } + {yesno} { + set buttons [list yes no] + } + {yesnocancel} { + set buttons [list yes no cancel] + } + } + + # Adjuts argument "default" + if {$arg_default == {}} { + set arg_default [lindex $buttons 0] + } elseif {[lsearch -ascii -exact $buttons $arg_default] == -1} { + error "my_tk_messageBox: Invalid value of agument -default, must be one of: $buttons" + } + + # Create top frame (dialog icon and text of the message) + set top_frame [frame $dialog.top] + pack [label $top_frame.img -image ::ICONS::32::$arg_icon] -side left -padx 5 + pack [label $top_frame.txt -text $arg_message -wraplength 300 -justify left] -side left -fill both -padx 5 + + # Create bottom bar with dialog buttons + set bottom_frame [frame $dialog.bottom] + foreach button $buttons { + set button_icon [lindex $button_icons [ \ + lsearch $available_buttons $button \ + ]] + + set text [string toupper [string index $button 0]] + append text [string range $button 1 end] + + pack [ttk::button $bottom_frame.button_${button} \ + -text [mc $text] -compound left \ + -image ::ICONS::16::$button_icon \ + -command "::my_tk_messageBox::button_press $button" \ + ] -side left -padx 2 + bind $bottom_frame.button_${button} <Return> "::my_tk_messageBox::button_press $button" + bind $bottom_frame.button_${button} <KP_Enter> "::my_tk_messageBox::button_press $button" + bind $bottom_frame.button_${button} <Escape> " + grab release $dialog + destroy $dialog + set ::my_tk_messageBox::return_value {} + " + } + + # Pack window frames + pack $top_frame -expand 1 -pady 10 -padx 5 + pack $bottom_frame -padx 5 -pady 10 + + # Window manager options -- modal window + wm iconphoto $dialog ::ICONS::16::$iconphoto + wm title $dialog $arg_title + wm state $dialog normal + focus -force $bottom_frame.button_${arg_default} + if {$arg_parent != {}} { + wm transient $dialog $arg_parent + } + wm protocol $dialog WM_DELETE_WINDOW " + grab release $dialog + destroy $dialog + set ::my_tk_messageBox::return_value {} + " + update + catch { + grab $dialog + } + + # Wait for user response + tkwait window $dialog + + # Destroy dialog and return name of pressed button + catch { + grab release $dialog + destroy $dialog + } + return $return_value + } + + ## Handles button press + # @parm String value - Name of pressed button + # @return void + proc button_press {value} { + variable return_value ;# String: Dialog return value (e.g. "abort")g + variable dialog ;# Widget: Dialog toplevel window + + grab release $dialog + destroy $dialog + set return_value $value + } + + ## Load needed images from the specified directory + # @parm String directory - Source directory + # @return void + proc load_images {directory} { + foreach subdir {16x16 32x32} ns {16 32} icons { + {cancel info help status_unknown ok button_cancel reload forward no} + {messagebox_critical messagebox_info help messagebox_warning button_ok} + } \ + { + foreach icon $icons { + set filename [file join $directory {../icons} $subdir "${icon}.png"] + + if {[catch { + image create photo ::ICONS::${ns}::${icon} -format png -file $filename + } result]} then { + puts stderr {} + puts -nonewline stderr $result + image create photo ::ICONS::${ns}::${icon} + } + } + } + } +} + +## Replacement for Tk's command "tk_messageBox" + # Usage is the same as "tk_messageBox" ... +proc my_tk_messageBox args { + set length [llength $args] + if {$length % 2} { + error "my_tk_messageBox: Odd number of arguments given" + } + + set arg_default {} + set arg_icon {info} + set arg_message {} + set arg_parent {} + set arg_title {} + set arg_type {} + + for {set i 0; set j 1} {$i < $length} {incr i 2; incr j 2} { + set attr [lindex $args $i] + set val [lindex $args $j] + + switch -- $attr { + {-default} { + set arg_default $val + } + {-icon} { + if {[lsearch -ascii -exact {error info question warning ok} $val] == -1} { + error "my_tk_messageBox: Invalid message box icon: $val" + } + set arg_icon $val + } + {-message} { + set arg_message $val + } + {-parent} { + if {![winfo exists $val]} { + error "my_tk_messageBox: Window $val does not exist." + } + set arg_parent $val + } + {-title} { + set arg_title $val + } + {-type} { + if {[lsearch -ascii -exact {abortretryignore ok okcancel retrycancel yesno yesnocancel} $val] == -1} { + error "my_tk_messageBox: Invalid message box type: $val" + } + set arg_type $val + } + default { + error "my_tk_messageBox: Unknown argument: $attr" + } + } + } + + if {![string length $arg_message]} { + error "my_tk_messageBox: No message box text specified" + } + if {![string length $arg_title]} { + if {![string length $arg_icon]} { + set arg_title {Message} + } else { + set arg_title [string toupper [string index $arg_icon 0]] + append arg_title [string range $arg_icon 1 end] + } + } + if {![string length $arg_type]} { + set arg_type {ok} + } + + return [my_tk_messageBox::create $arg_default $arg_icon $arg_message $arg_parent $arg_title $arg_type] +} + +# Replace Tk's command "tk_messageBox" +rename tk_messageBox old_tk_messageBox +rename my_tk_messageBox tk_messageBox + +# >>> File inclusion guard +} +# <<< File inclusion guard diff --git a/lib/dialogues/selectmcu.tcl b/lib/dialogues/selectmcu.tcl new file mode 100644 index 0000000..584a59f --- /dev/null +++ b/lib/dialogues/selectmcu.tcl @@ -0,0 +1,1566 @@ +#!/usr/bin/tclsh +# Part of MCU 8051 IDE ( http://mcu8051ide.sf.net ) + +############################################################################ +# Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 by Martin Ošmera # +# martin.osmera@gmail.com # +# # +# This program is free software; you can redistribute it and#or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation; either version 2 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program; if not, write to the # +# Free Software Foundation, Inc., # +# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # +############################################################################ + +# >>> File inclusion guard +if { ! [ info exists _SELECTMCU_TCL ] } { +set _SELECTMCU_TCL _ +# <<< File inclusion guard + +# -------------------------------------------------------------------------- +# DESCRIPTION +# This namespace implements "MCU selection" dialog. +# +# Usage: +# +# Invocation of MCU selection dialog +# ================================== +# SelectMCU::activate <parent> <initial_mcu_type xram_cap xcode_cap> +# -> {mcu_type xdata xcode} +# +# +# Other functions +# ================================== +# +# SelectMCU::get_available_processors +# -> List of available processors (e.g. {80C51 AT89C52 AT89C4051}) +# +# SelectMCU::get_processor_details processor_type +# -> List of MCU definition (see proc. xml_data_parser1) +# -------------------------------------------------------------------------- + +namespace eval SelectMCU { + # String: Path to MCUs definition file + variable definition_file "${::ROOT_DIRNAME}/data/mcus.xml" + # List: available MCU vendors + variable vendors [list [mc "all"] "Atmel" "Intel"] + variable selected_mcu {} ;# List: Dialog return value {mcu_type xdata xcode} + variable definition_data {} ;# List: Values gained from $definition_file + variable local_definition_data {} ;# List: Basically the same as $definition_data but containing only the shown items + variable mcu_names {} ;# List: Available processors (and show in the list) + variable maximum_xcode 0x10000 ;# Int: Maximum external program memory (0x10000 - internal) + variable vendor [mc "all"] ;# String: Selected vendor + + ## Variables related to GUI + variable parent ;# Widget: Dialog parent (another window) + variable win ;# Widget: Dialog window + variable search_bar ;# Widget: Search bar entry box + variable search_bar_clear ;# Widget: Search bar clear button + variable listbox_widget ;# Widget: List box containing available MCUs + variable value_lbl_uart ;# Widget: Label "UART:" - value + variable value_lbl_voltage ;# Widget: Label "Operating voltage:" - value + variable value_lbl_interrupts ;# Widget: Label "Interrupts:" - value + variable value_lbl_timers ;# Widget: Label "Timers:" - value + variable value_lbl_vendor ;# Widget: Label "Vendor" - value + variable more_details_text ;# Widget: TextWidget "More details:" + variable more_details_scrollbar ;# Widget: Scrollbar for $more_details_text + variable details_xdata_aval ;# Widget: Frame containing scale and spinbox for XDATA memory + variable details_xdata_note ;# Widget: Frame containing label "NOT available" for XDATA memory + variable details_xcode_aval ;# Widget: Frame containing scale and spinbox for XCODE memory + variable details_xcode_nota ;# Widget: Frame containing label "NOT available" for XCODE memory + variable name_label ;# Widget: Label containing name of selected MCU + variable image_label ;# Widget: Label with image for selected MCU + variable xdata_scale ;# Widget: Scale for XDATA memory + variable xdata_spinbox ;# Widget: SpinBox for XDATA memory + variable xcode_scale ;# Widget: Scale for XCODE memory + variable xcode_spinbox ;# Widget: SpinBox for XCODE memory + + ## Variables related to XML parser + variable current_element ;# String: Current XML element + variable expected ;# String: Expected next XML element + variable take_data ;# Bool: Take element data on next parsing cycle + variable mcu_definition ;# List: Definition of MCU currently being parsed + variable current_mcu ;# String: Name of MCU currently being parsed + + ## Fonts used in the selection dialog + if {$::GUI_AVAILABLE} { + # ListBox containing available fonts + variable listbox_widget_font [font create \ + -family $::DEFAULT_FIXED_FONT \ + -size [expr {int(-14 * $::font_size_factor)}] \ + -weight bold \ + ] + # ListBox header -- label widget above the ListBox + variable listbox_header_font [font create \ + -family $::DEFAULT_FIXED_FONT \ + -size [expr {int(-14 * $::font_size_factor)}] \ + ] + # Label with the MCU name + variable name_font [font create \ + -family {helvetica} \ + -size [expr {int(-20 * $::font_size_factor)}] \ + -weight bold \ + ] + # Labels like "Vendor:", "UART:", "Timers:", etc. + variable normal_font [font create \ + -family {helvetica} \ + -size [expr {int(-12 * $::font_size_factor)}] \ + ] + # Labels with values like for "Vendor:", "Timers:", etc. + variable bold_font [font create \ + -family {helvetica} \ + -size [expr {int(-12 * $::font_size_factor)}] \ + -weight bold \ + ] + } + + ## Invoke MCU selection dialog + # @parm Widget Parent - Dialog parent (some window) + # @parm String initial - {Initial_MCU Initial_XDATA Initial_XCODE} + # @return List - {mcu_type xdata xcode} or {} + proc activate {Parent initial} { + variable parent ;# Widget: Dialog parent (another window) + variable selected_mcu ;# List: Dialog return value {mcu_type xdata xcode} + variable mcu_definition ;# List: Definition of MCU currently being parsed + variable win ;# Widget: Dialog window + variable search_bar ;# Widget: Search bar entry box + + # Initialize NS variables + set parent $Parent + set selected_mcu {} + set mcu_definition {} + + # Load MCU definition file + if {![load_definition]} {return} + set mcu_definition {} + + create_gui ;# Create dialog GUI elements + fill_gui ;# Initialize GUI elements + + # Finalize window creation + wm iconphoto $win ::ICONS::16::kcmmemory + wm title $win [mc "Choose MCU - MCU 8051 IDE"] + if {$::font_size_factor > 1.0} { + wm minsize $win 870 500 + } else { + wm minsize $win 720 500 + } + wm protocol $win WM_DELETE_WINDOW { + ::SelectMCU::cancel + } + wm transient $win $parent + raise $win + catch { + grab $win + } + + # Initialize search bar + $search_bar insert end [lindex $initial 0] + focus -force $search_bar + $search_bar selection range 0 end + + set selected_mcu [lindex $initial 0] + + # Initialize XDATA & XCODE scales + if {[lindex $initial 1]} { + set ::SelectMCU::xdata_ena 1 + set ::SelectMCU::xdata_value [lindex $initial 1] + } else { + set ::SelectMCU::xdata_ena 0 + set ::SelectMCU::xdata_value 0 + xdata_disena + } + if {[lindex $initial 2]} { + set ::SelectMCU::xcode_ena 1 + set ::SelectMCU::xcode_value [lindex $initial 2] + } else { + set ::SelectMCU::xcode_ena 0 + set ::SelectMCU::xcode_value 0 + xcode_disena + } + + # Wait until the window is destroyed + tkwait window $win + + # Create resulting string + if {$selected_mcu == {}} { + set result {} + } else { + if {${::SelectMCU::xdata_ena}} { + set xdata ${::SelectMCU::xdata_value} + } else { + set xdata 0 + } + if {${::SelectMCU::xcode_ena}} { + set xcode ${::SelectMCU::xcode_value} + } else { + set xcode 0 + } + + if {$xdata == {}} { + set xdata 0 + } + if {$xcode == {}} { + set xcode 0 + } + set result [list $selected_mcu $xdata $xcode] + } + + return $result + } + + ## Load MCU definitions into the ListBox + # @return void + proc fill_gui {} { + variable definition_data ;# List: Values gained from $definition_file + variable local_definition_data ;# List: Basically the same as $definition_data but containing only the shown items + variable listbox_widget ;# Widget: List box containing available MCUs + variable listbox_widget_font ;# ListBox containing available fonts + variable mcu_names ;# List: available processors + variable vendor ;# String: Selected vendor + + set mcu_names {} + set local_definition_data {} + + # Iterate over defined MCUs + foreach mcu $definition_data { + # Filter specific vendors + if {$vendor != [mc "all"] && [lindex $mcu 11] != $vendor} { + continue + } + + lappend local_definition_data $mcu + + # MCU type + set mcu_type [lindex $mcu 0] + lappend mcu_names $mcu_type + set text $mcu_type + set len [string length $mcu_type] + append text [string repeat { } [expr {24 - $len}]] + + # Size of program memory + set str [lindex $mcu 3] + append str { KB} + set len [string length $str] + append text [string repeat { } [expr {8 - $len}]] $str + + # Size of internal data memory + set str [expr {[lindex $mcu 5] + [lindex $mcu 10]}] + append str { B} + set len [string length $str] + append text [string repeat { } [expr {13 - $len}]] $str + + # Number of IO lines + processor frequency + set str [lindex $mcu 6] + set len [string length $str] + append text [string repeat { } [expr {12 - $len}]] $str { } [lindex $mcu 4] + + # Insert the text into the ListBox + $listbox_widget insert end #auto \ + -text $text -data $mcu_type \ + -font $listbox_widget_font \ + -image ::ICONS::16::kcmmemory + } + } + + ## Create GUI elements of the selection dialog window + # @return void + proc create_gui {} { + variable win ;# Widget: Dialog window + variable search_bar ;# Widget: Search bar entry box + variable search_bar_clear ;# Widget: Search bar clear button + variable listbox_widget ;# Widget: List box containing available MCUs + variable value_lbl_uart ;# Widget: Label "UART:" - value + variable value_lbl_voltage ;# Widget: Label "Operating voltage:" - value + variable value_lbl_interrupts ;# Widget: Label "Interrupts:" - value + variable value_lbl_timers ;# Widget: Label "Timers:" - value + variable value_lbl_vendor ;# Widget: Label "Vendor" - value + variable more_details_text ;# Widget: TextWidget "More details:" + variable more_details_scrollbar ;# Widget: Scrollbar for $more_details_text + variable details_xdata_aval ;# Widget: Frame containing scale and spinbox for XDATA memory + variable details_xdata_note ;# Widget: Frame containing label "NOT available" for XDATA memory + variable details_xcode_aval ;# Widget: Frame containing scale and spinbox for XCODE memory + variable details_xcode_nota ;# Widget: Frame containing label "NOT available" for XCODE memory + variable listbox_widget_font ;# ListBox containing available fonts + variable listbox_header_font ;# ListBox header -- label widget above the ListBox + variable bold_font ;# Labels with values like for "Vendor:", "Timers:", etc. + variable normal_font ;# Labels like "Vendor:", "UART:", "Timers:", etc. + variable name_font ;# Label with the MCU name + variable name_label ;# Widget: Label containing name of selected MCU + variable image_label ;# Widget: Label with image for selected MCU + variable xdata_scale ;# Widget: Scale for XDATA memory + variable xdata_spinbox ;# Widget: Scale for XDATA memory + variable xcode_spinbox ;# Widget: SpinBox for XCODE memory + variable xcode_scale ;# Widget: Scale for XCODE memory + variable vendors ;# List: available MCU vendors + + # Create toplevel window + set win [toplevel .selectmcu_dialog -class {Select MCU} -bg ${::COMMON_BG_COLOR}] + + # Create search bar widgets (but don't pack them) + set search_bar_frame [frame $win.search_bar_frame] + set search_bar_clear [ttk::button $search_bar_frame.clear_but \ + -image ::ICONS::16::clear_left \ + -command ::SelectMCU::clear_search_bar \ + -state disabled \ + -style Flat.TButton \ + ] + DynamicHelp::add $search_bar_frame.clear_but \ + -text [mc "Clear search bar"] + set search_bar [ttk::entry $search_bar_frame.search_bar \ + -validate all \ + -validatecommand {::SelectMCU::search %P} \ + ] + DynamicHelp::add $search_bar \ + -text [mc "Search bar, enter something like \"C4051\""] + + # Create ListBox and its scrollbar + set top_frame [frame $win.top_frame] + set top_left_frame [frame $top_frame.left -bd 1 -relief sunken] + set top_left_top_frame [frame $top_left_frame.top] + set listbox_widget [ListBox $top_left_frame.listbox \ + -selectfill 1 -bg {#FFFFFF} -bd 0 -height 0 \ + -selectbackground {#CCCCFF} -selectmode single \ + -selectforeground {#0000AA} \ + -highlightcolor {#BBBBFF} \ + -highlightthickness 0 -padx 20 -deltay 20 \ + -yscrollcommand "$top_frame.scrollbar set" \ + ] + if {[winfo exists $listbox_widget.c]} { + bind $listbox_widget.c <Button-5> {%W yview scroll +5 units; break} + bind $listbox_widget.c <Button-4> {%W yview scroll -5 units; break} + } + bind $listbox_widget <<ListboxSelect>> {::SelectMCU::select_item} + $listbox_widget bindImage <Double-1> {::SelectMCU::close_window;#} + $listbox_widget bindText <Double-1> {::SelectMCU::close_window;#} + set tree_scrollbar [ttk::scrollbar $top_frame.scrollbar \ + -orient vertical \ + -command [list $listbox_widget yview] \ + ] + # Create ListBox header + pack [label $top_left_frame.header \ + -font $listbox_header_font \ + -bg {#DDDDDD} -bd 0 -padx 25 \ + -justify left -anchor w \ + -text [mc "Processor Type\t\tCODE/PMEM IDATA/IRAM GPIO Frequency"] \ + ] -fill x + + # Create remaining parts of top frame and pack them + pack [label $search_bar_frame.search_label \ + -text [mc "Search:"] \ + ] -side left -padx 5 + pack $search_bar -fill x -expand 1 -side left + pack $search_bar_clear -after $search_bar -side left + pack [label $search_bar_frame.vendor_label \ + -text [mc " Vendor:"] \ + ] -side left -padx 5 -after $search_bar_clear + pack [ttk::combobox $search_bar_frame.vendor_cb \ + -state readonly \ + -textvariable {::SelectMCU::vendor} \ + -values $vendors \ + ] -side left -padx 5 -after $search_bar_frame.vendor_label + bind $search_bar_frame.vendor_cb <<ComboboxSelected>> {::SelectMCU::change_vendor} + pack $search_bar_frame -fill x -pady 10 -padx 5 + + # Pack all frames except the bottom frame and the details frame + pack $top_left_top_frame -fill x + pack $listbox_widget -fill both -expand 1 + pack $top_left_frame -fill both -expand 1 -side left + pack $tree_scrollbar -fill y -after $top_left_frame -side right + pack $top_frame -fill both -expand 1 -padx 5 + + # Create parts of details frame + set details_frame [frame $win.details_frame] + set details_left [frame $details_frame.left] + set details_middle [frame $details_frame.middle -width 300] + set details_right [frame $details_frame.right] + set details_middle_top [frame $details_middle.top] + set details_middle_bottom [frame $details_middle.bottom] + + # Left side + set name_label [label $details_left.name \ + -text "" -font $name_font \ + ] + set image_label [label $details_left.image \ + -image [image create photo] -text { } \ + -width 200 -height 200 -compound left \ + ] + DynamicHelp::add $image_label -text [mc "One of available packages for selected microcontroller"] + pack $name_label -fill x + pack $image_label -padx 5 + + # General features + set i 0 + foreach text {{Vendor:} {UART:} {Operating voltage:} {Interrupt sources:} {Timers:}} { + grid [label $details_middle_top.lbl_$i \ + -text [mc $text] \ + -justify left \ + -font $normal_font \ + ] -row $i -column 0 -sticky w + incr i + } + set value_lbl_vendor [label $details_middle_top.value_lbl_vendor \ + -justify left -anchor w -font $bold_font \ + ] + set value_lbl_uart [label $details_middle_top.value_lbl_uart \ + -justify left -anchor w -font $bold_font \ + ] + set value_lbl_voltage [label $details_middle_top.value_lbl_voltage \ + -justify left -anchor w -font $bold_font \ + ] + set value_lbl_interrupts [label $details_middle_top.value_lbl_interr \ + -justify left -anchor w -font $bold_font \ + ] + set value_lbl_timers [label $details_middle_top.value_lbl_timers \ + -justify left -anchor w -font $bold_font \ + ] + grid $value_lbl_vendor -row 0 -column 1 -sticky we + grid $value_lbl_uart -row 1 -column 1 -sticky we + grid $value_lbl_voltage -row 2 -column 1 -sticky we + grid $value_lbl_interrupts -row 3 -column 1 -sticky we + grid $value_lbl_timers -row 4 -column 1 -sticky we + grid columnconfigure $details_middle_top 0 -minsize 140 + grid columnconfigure $details_middle_top 1 -weight 1 + + # Details + set more_details_text [text $details_middle_bottom.text \ + -yscrollcommand ::SelectMCU::details_scrollbar_set \ + -width 0 -heigh 0 -bd 0 -relief flat -font $bold_font \ + -highlightthickness 0 -state disabled -bg ${::COMMON_BG_COLOR} \ + -cursor left_ptr -fg {#555555} -wrap word \ + ] + set more_details_scrollbar [ttk::scrollbar \ + $details_middle_bottom.scrollbar \ + -command "$more_details_text yview" \ + -orient vertical \ + ] + pack $more_details_text -side left -fill both -expand 1 + + # Pack general & details frames + pack $details_middle_top -fill both -pady 10 + pack $details_middle_bottom -fill both -expand 1 + + # Cretate XDATA and XCODE scales and such + set details_right_top [ttk::labelframe $details_right.top \ + -text [mc "External RAM (XDATA)"] \ + ] + set details_right_bottom [ttk::labelframe $details_right.bottom \ + -text [mc "External ROM/FLASH (XCODE)"] \ + ] + + set details_xdata_note [label $details_right_top.not_available \ + -text [mc "NOT available"] -fg {#FF8888} \ + ] + set details_xdata_aval [frame $details_right_top.available] + pack [checkbutton $details_xdata_aval.checkbutton \ + -variable ::SelectMCU::xdata_ena \ + -text [mc "Enable"] \ + -command ::SelectMCU::xdata_disena \ + ] -anchor w + DynamicHelp::add $details_xdata_aval.checkbutton \ + -text [mc "Connect external data memory"] + set details_right_top_btm [frame $details_xdata_aval.btm] + set xdata_scale [ttk::scale $details_right_top_btm.scale \ + -orient horizontal \ + -variable ::SelectMCU::xdata_value \ + -from 0 -to 0x10000 \ + -command " + set ::SelectMCU::xdata_value \[expr {int(\${::SelectMCU::xdata_value})}\] + $details_right_top_btm.spinbox selection range 0 end + #" \ + ] + DynamicHelp::add $details_right_top_btm.scale \ + -text [mc "Amount of external data memory"] + pack $xdata_scale -fill x -side left -expand 1 -padx 2 + set xdata_spinbox [ttk::spinbox $details_right_top_btm.spinbox \ + -textvariable ::SelectMCU::xdata_value \ + -width 5 -from 0 -to 0x10000 \ + -validate all \ + -validatecommand {::SelectMCU::validate_xdata %P} \ + ] + DynamicHelp::add $details_right_top_btm.spinbox \ + -text [mc "Amount of external data memory"] + pack $xdata_spinbox -side right -after $details_right_top_btm.scale + pack $details_right_top_btm -fill both -expand 1 + + set details_xcode_nota [label $details_right_bottom.not_available \ + -text [mc "NOT available"] -fg {#FF8888} \ + ] + set details_xcode_aval [frame $details_right_bottom.available] + pack [checkbutton $details_xcode_aval.checkbutton \ + -variable ::SelectMCU::xcode_ena \ + -text [mc "Enable"] \ + -command ::SelectMCU::xcode_disena \ + ] -anchor w + DynamicHelp::add $details_xcode_aval.checkbutton \ + -text [mc "Connect external program memory"] + set details_right_bottom_btm [frame $details_xcode_aval.btm] + set xcode_scale [ttk::scale $details_right_bottom_btm.scale \ + -orient horizontal \ + -variable ::SelectMCU::xcode_value \ + -from 0 -to 0x10000 \ + -command " + set ::SelectMCU::xcode_value \[expr {int(\${::SelectMCU::xcode_value})}\] + #" \ + ] + DynamicHelp::add $details_right_bottom_btm.scale \ + -text [mc "Amount of total program memory minus internal program memory"] + pack $xcode_scale -fill x -side left -expand 1 -padx 2 + set xcode_spinbox [ttk::spinbox $details_right_bottom_btm.spinbox \ + -textvariable ::SelectMCU::xcode_value \ + -width 5 -from 0 -to 0x10000 \ + -validate all \ + -validatecommand {::SelectMCU::validate_xcode %P} \ + ] + DynamicHelp::add $details_right_bottom_btm.spinbox \ + -text [mc "Amount of total program memory minus internal program memory"] + pack $xcode_spinbox -side right -after $details_right_bottom_btm.scale + pack $details_right_bottom_btm -fill both -expand 1 + + grid $details_right_top -row 0 -column 0 -sticky wens -padx 5 -pady 10 + grid $details_right_bottom -row 1 -column 0 -sticky wens -padx 5 -pady 10 + grid rowconfigure $details_right 0 -minsize 100 + grid rowconfigure $details_right 1 -minsize 100 + grid columnconfigure $details_right 0 -weight 1 -minsize 180 + + # Pack parts of details frame + pack $details_left -side left + pack $details_middle -side left -fill both -expand 1 -padx 15 -pady 10 + pack $details_right -side right -fill y -after $details_middle -padx 5 + pack $details_frame -fill x -padx 5 -pady 10 + + # Create and pack 'OK' and 'CANCEL' buttons + set buttonFrame [frame $win.buttonFrame] + pack [ttk::button $buttonFrame.ok \ + -text [mc "Ok"] \ + -compound left \ + -image ::ICONS::16::ok \ + -command {::SelectMCU::close_window} \ + ] -side left -padx 2 + pack [ttk::button $buttonFrame.cancel \ + -text [mc "Cancel"] \ + -compound left \ + -image ::ICONS::16::button_cancel \ + -command {::SelectMCU::cancel} \ + ] -side left -padx 2 + pack [ttk::separator $win.sep -orient horizontal] -fill x + pack $buttonFrame -side bottom -after $details_frame -anchor e -padx 5 -pady 5 + } + + ## Close MCU selection dialog and force its return value to an empty string + # @return void + proc close_window {} { + variable definition_data ;# List: Values gained from $definition_file + variable mcu_names ;# List: available processors + variable win ;# Widget: Dialog window + + set definition_data {} + set mcu_names {} + grab release $win + destroy $win + } + + ## Load MCU database + # @return void + proc load_definition {} { + variable parent ;# Widget: Dialog parent (another window) + variable definition_file ;# String: Path to MCUs definition file + variable definition_data ;# List: Values gained from $definition_file + variable mcu_definition ;# List: Definition of MCU currently being parsed + variable expected ;# String: Expected next XML element + variable take_data ;# Bool: Take element data on next parsing cycle + + # Initialize NS variables + set definition_data {} + set mcu_definition {} + set expected {mcus} + set current_element {} + set take_data 0 + + # Open definition file + if {[catch { + set file [open $definition_file {r}] + }]} then { + tk_messageBox \ + -parent $parent \ + -type ok \ + -icon warning \ + -title mcus.xml \ + -message [mc "Unable to open file containing supported MCUs,\nplease check your installation"] + return 0 + } + + # Create XML parser + set parser [::xml::parser -final 1 -ignorewhitespace 1 \ + -elementstartcommand {::SelectMCU::xml_data_parser0_element} \ + -characterdatacommand {::SelectMCU::xml_data_parser0_data} \ + ] + + # Start XML parser + if {[catch { + $parser parse [read $file] + if {$mcu_definition != {}} { + foreach val $mcu_definition { + if {$val == {}} { + error "Incomplete definition for [lindex $mcu_definition 0]" + } + } + lappend definition_data $mcu_definition + } + } result]} then { + set definition_data {} + tk_messageBox \ + -parent $parent \ + -icon warning \ + -type ok \ + -title [mc "Error"] \ + -message [mc "MCUs database file is corrupted (code:600),\nplease check your installation"] + puts stderr $result + close $file + return 0 + } + + # Close file and free parser + close $file + $parser free + return 1 + } + + ## Get list of MCUs defined in the database + # @return List - Defined processors (e.g. {8051 AT89C2051 ...}) + proc get_available_processors {} { + variable definition_data ;# List: Values gained from $definition_file + variable definition_file ;# String: Path to MCUs definition file + variable expected ;# String: Expected next XML element + + # Initialize NS variables + set expected {mcus} + set definition_data {} ;# <-- Result will be stored here + + # Open database file + if {[catch { + set file [open $definition_file {r}] + }]} then { + puts stderr "Unable to open file containing supported MCUs, please check your installation" + return {} + } + + # Create XML parser + set parser [::xml::parser -final 1 -ignorewhitespace 1 \ + -elementstartcommand {::SelectMCU::xml_data_parser2_element} \ + ] + + # Start XML parser + if {[catch { + $parser parse [read $file] + } result]} then { + set definition_data {} + puts stderr "MCUs database file is corrupted (code:641),\nplease check your installation" + puts stderr $result + } + + # Close file and free parser + close $file + $parser free + return $definition_data + } + + ## Gain detail description for the given processor + # @parm String mcu_name - Processor type (e.g. AT89C51RC) + # @return List - (see proc. xml_data_parser1) + proc get_processor_details {mcu_name} { + variable definition_file ;# String: Path to MCUs definition file + variable definition_data ;# List: Values gained from $definition_file + variable expected ;# String: Expected next XML element + variable take_data ;# Bool: Take element data on next parsing cycle + variable mcu_definition ;# List: Definition of MCU currently being parsed + variable current_mcu ;# String: Name of MCU currently being parsed + + # Initialize NS variables + set mcu_definition $mcu_name + set expected {mcus} + set definition_data {} ;# <-- Result will be stored here + set take_data 0 + set current_mcu {} + + # Open database file + if {[catch { + set file [open $definition_file {r}] + }]} then { + puts stderr "Unable to open file containing supported MCUs, please check your installation" + return {} + } + + # Create XML parser + set parser [::xml::parser -final 1 -ignorewhitespace 1 \ + -elementstartcommand {::SelectMCU::xml_data_parser1_element} \ + -characterdatacommand {::SelectMCU::xml_data_parser1_data} \ + ] + + # Start XML parser + if {[catch { + $parser parse [read $file] + } result]} then { + set definition_data {} + puts stderr "MCUs database file is corrupted (code:688),\nplease check your installation" + puts stderr $result + } + + # Close file and free parser + close $file + $parser free + return $definition_data + } + + ## XML parser handler for procedure get_available_processors -- Takes XML tags + # @parm String arg1 - name of the element + # @parm List attrs - list of attributes '{attr0 val0 attr1 val1 ...}' + # @return void + proc xml_data_parser2_element {arg1 attrs} { + variable definition_data ;# List: Values gained from $definition_file + variable expected ;# String: Expected next XML element + + # Check for consistent formatting + if {$arg1 != $expected} { + error "Bad element `$arg1'" + } + + switch -- $arg1 { + {mcus} { + set expected {mcu} + } + {mcu} { + set expected {timers} + set len [llength $attrs] + + # Search for attribute "name" + for {set i 0} {$i < $len} {incr i 2} { + set val [lindex $attrs $i] + if {$val == {name}} { + # Append MCU name to result + incr i + lappend definition_data [lindex $attrs $i] + break + } + } + } + {timers} {set expected {more}} + {more} {set expected {bits}} + {bits} {set expected {writeonly}} + {writeonly} {set expected {sfr}} + {sfr} {set expected {mcu}} + } + } + + + ## XML parser handler for procedure get_processor_details -- Takes XML data + # @parm String arg1 - content of the element + # @return void + proc xml_data_parser1_data {arg1} { + variable definition_data ;# List: Values gained from $definition_file + variable expected ;# String: Expected next XML element + variable mcu_definition ;# List: Definition of MCU currently being parsed + variable current_element ;# String: Current XML element + variable take_data ;# Bool: Take element data on next parsing cycle + + # Take data only if they were expected + if {!$take_data} {return} + set take_data 0 + + # Take data section only for 1 processor + if {$mcu_definition == {} || ![llength $definition_data]} { + return + } + + # Adjust data string + set arg1 [string trim $arg1] + regsub {\s+} $arg1 { } arg1 + + # Validate and store data + switch -- $current_element { + {bits} { ;# Incomplete registers + if {![regexp {([0-9A-Fa-f]{4})?(\s+[0-9A-Fa-f]{4})*} $arg1]} { + error "MCUs database file corrupted" + } + lset definition_data 18 $arg1 + } + {writeonly} { ;# Write only registers + if {![regexp {([0-9A-Fa-f]{2})?(\s+[0-9A-Fa-f]{2})*} $arg1]} { + error "MCUs database file corrupted" + } + lset definition_data 19 $arg1 + } + {sfr} { ;# available special function registers and bit addressable bits in SFR + lset definition_data 43 $arg1 + + set mcu_definition {} ;# This is the last tag + } + } + } + + ## XML parser handler for procedure get_processor_details -- Takes XML tags + # @parm String arg1 - name of the element + # @parm List attrs - list of attributes '{attr0 val0 attr1 val1 ...}' + # @return void + proc xml_data_parser1_element {arg1 attrs} { + variable definition_data ;# List: Values gained from $definition_file + variable expected ;# String: Expected next XML element + variable mcu_definition ;# List: Definition of MCU currently being parsed + variable current_mcu ;# String: Name of MCU currently being parsed + variable current_element ;# String: Current XML element + variable take_data ;# Bool: Take element data on next parsing cycle + + set take_data 0 + + ## Take tag attributes + set current_element $arg1 + if {$arg1 != $expected} { + error "Bad element `$arg1'" + } + switch -- $arg1 { + {mcus} { + set expected {mcu} + } + {mcu} { + set expected {timers} + set len [llength $attrs] + + for {set i 0} {$i < $len} {incr i 2} { + set val [lindex $attrs $i] + if {$val == {name}} { + incr i + set current_mcu [lindex $attrs $i] + if {$mcu_definition != $current_mcu} { + return + } + } + } + + set definition_data [list \ + {} {} {} {} {} {} {} {} \ + {} {} {} {} {} {} {} {} \ + {} {} {} {} {} {} {} {} \ + {} {} {} {} {} {} {} {} \ + {} {} {} {} {} {} {} {} \ + {} {} {} {} \ + ] + + for {set i 0} {$i < $len} {incr i} { + switch -- [lindex $attrs $i] { + {xdata} { + incr i + xml_dp1_attr_yes_no 0 [lindex $attrs $i] + } + {xcode} { + incr i + xml_dp1_attr_yes_no 1 [lindex $attrs $i] + } + {code} { + incr i + set val [lindex $attrs $i] + if {![string is digit -strict $val] || $val < 0 || $val > 0x10000} { + error "MCUs database file corrupted" + } + lset definition_data 2 $val + } + {ram} { + incr i + set val [lindex $attrs $i] + if {![string is digit -strict $val] || $val < 0 || $val > 256} { + error "MCUs database file corrupted" + } + lset definition_data 3 $val + } + {portbits} { + incr i + set val [lindex $attrs $i] + if {![string is digit -strict $val] || $val < 0 || $val > 256} { + error "MCUs database file corrupted" + } + lset definition_data 4 $val + } + {uart} { + incr i + xml_dp1_attr_yes_no 5 [lindex $attrs $i] + } + {timer2} { + incr i + xml_dp1_attr_yes_no 6 [lindex $attrs $i] + } + {watchdog} { + incr i + xml_dp1_attr_yes_no 7 [lindex $attrs $i] + } + {eram} { + incr i + set val [lindex $attrs $i] + if {![string is digit -strict $val] || $val < 0 || $val > 0x10000} { + error "MCUs database file corrupted" + } + lset definition_data 8 $val + } + {dualdtpr} { + incr i + xml_dp1_attr_yes_no 9 [lindex $attrs $i] + } + {auxr} { + incr i + xml_dp1_attr_yes_no 10 [lindex $attrs $i] + } + {t2mod} { + incr i + xml_dp1_attr_yes_no 11 [lindex $attrs $i] + } + {port0} { + incr i + set val [lindex $attrs $i] + if {$val != {} && ![regexp {^[01]{8}$} $val]} { + error "MCUs database file corrupted" + } + lset definition_data 12 $val + } + {port1} { + incr i + set val [lindex $attrs $i] + if {$val != {} && ![regexp {^[01]{8}$} $val]} { + error "MCUs database file corrupted" + } + lset definition_data 13 $val + } + {port2} { + incr i + set val [lindex $attrs $i] + if {$val != {} && ![regexp {^[01]{8}$} $val]} { + error "MCUs database file corrupted" + } + lset definition_data 14 $val + } + {port3} { + incr i + set val [lindex $attrs $i] + if {$val != {} && ![regexp {^[01]{8}$} $val]} { + error "MCUs database file corrupted" + } + lset definition_data 15 $val + } + {port4} { + incr i + set val [lindex $attrs $i] + if {$val != {} && ![regexp {^[01]{8}$} $val]} { + error "MCUs database file corrupted" + } + lset definition_data 16 $val + } + {pof} { + incr i + xml_dp1_attr_yes_no 17 [lindex $attrs $i] + } + {gf0} { + incr i + xml_dp1_attr_yes_no 20 [lindex $attrs $i] + } + {gf1} { + incr i + xml_dp1_attr_yes_no 21 [lindex $attrs $i] + } + {pd} { + incr i + xml_dp1_attr_yes_no 22 [lindex $attrs $i] + } + {idl} { + incr i + xml_dp1_attr_yes_no 23 [lindex $attrs $i] + } + {smod0} { + incr i + xml_dp1_attr_yes_no 24 [lindex $attrs $i] + } + {iph} { + incr i + xml_dp1_attr_yes_no 25 [lindex $attrs $i] + } + {acomparator} { + incr i + xml_dp1_attr_yes_no 26 [lindex $attrs $i] + } + {euart} { + incr i + xml_dp1_attr_yes_no 27 [lindex $attrs $i] + } + {clkreg} { + incr i + xml_dp1_attr_yes_no 28 [lindex $attrs $i] + } + {pwdex} { + incr i + xml_dp1_attr_yes_no 29 [lindex $attrs $i] + } + {spi} { + incr i + xml_dp1_attr_yes_no 30 [lindex $attrs $i] + } + {wdtcon} { + incr i + xml_dp1_attr_yes_no 31 [lindex $attrs $i] + } + {eeprom} { + incr i + set val [lindex $attrs $i] + if {![string is digit -strict $val]} { + error "MCUs database file corrupted" + } + lset definition_data 32 $val + } + {intelpe} { + incr i + xml_dp1_attr_yes_no 33 [lindex $attrs $i] + } + {pwm} { + incr i + xml_dp1_attr_yes_no 34 [lindex $attrs $i] + } + {x2reset} { + incr i + xml_dp1_attr_yes_no 35 [lindex $attrs $i] + } + {ckcon} { + incr i + xml_dp1_attr_yes_no 36 [lindex $attrs $i] + } + {auxr1gf3} { + incr i + xml_dp1_attr_yes_no 37 [lindex $attrs $i] + } + {ao} { + incr i + xml_dp1_attr_yes_no 38 [lindex $attrs $i] + } + {wdtprg} { + incr i + xml_dp1_attr_yes_no 39 [lindex $attrs $i] + } + {hddptr} { + incr i + xml_dp1_attr_yes_no 40 [lindex $attrs $i] + } + {auxrwdidle} { + incr i + xml_dp1_attr_yes_no 41 [lindex $attrs $i] + } + {auxrdisrto} { + incr i + xml_dp1_attr_yes_no 42 [lindex $attrs $i] + } + default { + incr i + } + } + } + } + {timers} { + set expected {more} + } + {more} { + set expected {bits} + } + {bits} { + if {$mcu_definition == $current_mcu} { + set take_data 1 + } + set expected {writeonly} + } + {writeonly} { + if {$mcu_definition == $current_mcu} { + set take_data 1 + } + set expected {sfr} + } + {sfr} { + if {$mcu_definition == $current_mcu} { + set take_data 1 + } + set expected {mcu} + } + } + } + + ## Auxiliary procedure for xml_data_parser1 + # Invoke error if the given value was neither "yes" nor "no" + # @parm Int index - Index in list $definition_data + # @parm String value - Value to set in $definition_data + # @return void + proc xml_dp1_attr_yes_no {index value} { + variable definition_data ;# List: Values gained from $definition_file + + if {$value != {yes} && $value != {no}} { + error "MCUs database file corrupted" + } + lset definition_data $index $value + } + + ## XML parser handler for procedure load_definition -- takes XML tags + # @parm String arg1 - name of the element + # @parm List attrs - list of attributes '{attr0 val0 attr1 val1 ...}' + # @return void + proc xml_data_parser0_element {arg1 attrs} { + variable definition_data ;# List: Values gained from $definition_file + variable expected ;# String: Expected next XML element + variable current_element ;# String: Current XML element + variable take_data ;# Bool: Take element data on next parsing cycle + variable mcu_definition ;# List: Definition of MCU currently being parsed + variable vendors ;# List: available MCU vendors + + if {$arg1 != $expected} { + error "Bad element `$arg1'" + } + set current_element $arg1 + switch -- $arg1 { + {mcus} { + set expected {mcu} + } + {mcu} { + if {$mcu_definition != {}} { + foreach val $mcu_definition { + if {$val == {}} { + error "Incomplete definition for [lindex $mcu_definition 0]" + } + } + lappend definition_data $mcu_definition + } + set expected {timers} + set mcu_definition [list {} {} {} {} {} {} {} {} {} {} {} {}] + for {set i 0} {$i < [llength $attrs]} {incr i} { + switch -- [lindex $attrs $i] { + {name} { + incr i + set val [lindex $attrs $i] + if {![string is alnum -strict $val]} { + error "MCU name must match ^\[\w\d\]+$" + } + lset mcu_definition 0 $val + } + {xdata} { + incr i + set val [lindex $attrs $i] + if {$val != {yes} && $val != {no}} { + error "Attribute XDATA must have value \"yes\" or \"no\"" + } + lset mcu_definition 1 $val + } + {xcode} { + incr i + set val [lindex $attrs $i] + if {$val != {yes} && $val != {no}} { + error "Attribute XCODE must have value \"yes\" or \"no\"" + } + lset mcu_definition 2 $val + } + {code} { + incr i + set val [lindex $attrs $i] + if {![string is digit -strict $val] || $val < 0 || $val > 65536} { + error "CODE memory capacity must be an integer value \[0;65536\]" + } + lset mcu_definition 3 $val + } + {frequency} { + incr i + set val [lindex $attrs $i] + if {[string length $val] > 16 || ![string is print $val]} { + error "Attribute FREQUENCY must be printable string (max. 16 characters)" + } + lset mcu_definition 4 $val + } + {ram} { + incr i + set val [lindex $attrs $i] + if {![string is digit -strict $val] || $val < 0 || $val > 256} { + error "RAM capacity must be an integer value \[0;256\]" + } + lset mcu_definition 5 $val + } + {portbits} { + incr i + set val [lindex $attrs $i] + if {![string is digit -strict $val] || $val < 0 || $val > 256} { + error "Attribute PORTBITS must be an integer value \[0;256\]" + } + lset mcu_definition 6 $val + } + {uart} { + incr i + set val [lindex $attrs $i] + if {$val != {yes} && $val != {no}} { + error "Attribute UART must be either \"yes\" or \"no\"" + } + lset mcu_definition 7 $val + } + {interrupts} { + incr i + set val [lindex $attrs $i] + if {![string is digit -strict $val] || $val < 0 || $val > 99} { + error "Attribute INTERRUPTS must be an integer value \[0;99\]" + } + lset mcu_definition 8 $val + } + {voltage} { + incr i + set val [lindex $attrs $i] + if {[string length $val] > 11 || ![string is print $val]} { + error "Attribute VOLTAGE must be printable string (max. 11 characters)" + } + lset mcu_definition 9 $val + } + {eram} { + incr i + set val [lindex $attrs $i] + if {![string is digit -strict $val] || $val < 0 || $val > 65536} { + error "ERAM capacity must be an integer value \[0;65536\]" + } + lset mcu_definition 10 $val + } + {vendor} { + incr i + set val [lindex $attrs $i] + if {[lsearch $vendors $val] == -1} { + error "Undefined vendor \"$val\"" + } + lset mcu_definition 11 $val + } + default { + incr i + } + } + } + } + {timers} { + set expected {more} + set take_data 1 + } + {more} { + set expected {bits} + set take_data 1 + } + {bits} { + set expected {writeonly} + set take_data 0 + } + {writeonly} { + set expected {sfr} + set take_data 0 + } + {sfr} { + set expected {mcu} + set take_data 0 + } + } + } + + ## XML parser handler for procedure load_definition -- takes data section + # @parm String arg1 - content of the element + # @return void + proc xml_data_parser0_data {arg1} { + variable definition_data ;# List: Values gained from $definition_file + variable expected ;# String: Expected next XML element + variable current_element ;# String: Current XML element + variable take_data ;# Bool: Take element data on next parsing cycle + variable mcu_definition ;# List: Definition of MCU currently being parsed + variable vendors ;# List: available MCU vendors + + if {!$take_data} { + return + } + + set take_data 0 + + regsub -all {^\s+} $arg1 {} arg1 + regsub -all {\s+$} $arg1 {} arg1 + regsub -all -line {^\t+} $arg1 {} arg1 + + switch -- $current_element { + {timers} { + lappend mcu_definition $arg1 + } + {more} { + lappend mcu_definition $arg1 + } + } + } + + ## Event handler for ListBox with list of processors + # Handles <<ListboxSelect>> -- Change contents of details frame + # @parm String - "noclear" == do not clear search EntryBox + # @return void + proc select_item args { + variable definition_data ;# List: Values gained from $definition_file + variable local_definition_data ;# List: Basically the same as $definition_data but containing only the shown items + variable selected_mcu ;# List: Dialog return value {mcu_type xdata xcode} + variable listbox_widget ;# Widget: List box containing available MCUs + variable value_lbl_uart ;# Widget: Label "UART:" - value + variable value_lbl_voltage ;# Widget: Label "Operating voltage:" - value + variable value_lbl_interrupts ;# Widget: Label "Interrupts:" - value + variable value_lbl_timers ;# Widget: Label "Timers:" - value + variable value_lbl_vendor ;# Widget: Label "Vendor" - value + variable more_details_text ;# Widget: TextWidget "More details:" + variable details_xdata_aval ;# Widget: Frame containing scale and spinbox for XDATA memory + variable details_xdata_note ;# Widget: Frame containing label "NOT available" for XDATA memory + variable details_xcode_aval ;# Widget: Frame containing scale and spinbox for XCODE memory + variable details_xcode_nota ;# Widget: Frame containing label "NOT available" for XCODE memory + variable name_label ;# Widget: Label containing name of selected MCU + variable image_label ;# Widget: Label with image for selected MCU + variable xcode_spinbox ;# Widget: SpinBox for XCODE memory + variable xcode_scale ;# Widget: Scale for XCODE memory + variable maximum_xcode ;# Int: Maximum external program memory (0x10000 - internal) + + # Get MCU definition for the selected processor + set mcu [lindex $local_definition_data \ + [$listbox_widget index [$listbox_widget selection get]] \ + ] + set mcu_name [lindex $mcu 0] + if {$selected_mcu == $mcu_name} { + return + } + set selected_mcu $mcu_name + set maximum_xcode [expr {0x10000 - ([lindex $mcu 3] * 1024)}] + + # Configure detail labels + $name_label configure -text $mcu_name + $value_lbl_vendor configure -text [lindex $mcu 11] + $value_lbl_uart configure -text [lindex $mcu 7] + $value_lbl_voltage configure -text [lindex $mcu 9] + $value_lbl_interrupts configure -text [lindex $mcu 8] + $value_lbl_timers configure -text [lindex $mcu 12] + + # Configure details text + $more_details_text configure -state normal + $more_details_text delete 1.0 end + foreach line [split [lindex $mcu 13] "\n"] { + $more_details_text image create end -image ::ICONS::16::bookmark -padx 2 -pady 2 + $more_details_text insert end $line + $more_details_text insert end "\n" + } + $more_details_text configure -state disabled + + # Configure XDATA scale + if {[lindex $mcu 1] != {yes}} { + if {[winfo ismapped $details_xdata_aval]} { + pack forget $details_xdata_aval + } + pack $details_xdata_note -fill both -expand 1 + } else { + if {[winfo ismapped $details_xdata_note]} { + pack forget $details_xdata_note + } + pack $details_xdata_aval -fill both -expand 1 -padx 2 + } + # Configure XCODE scale + if {[lindex $mcu 2] != {yes}} { + if {[winfo ismapped $details_xcode_aval]} { + pack forget $details_xcode_aval + } + pack $details_xcode_nota -fill both -expand 1 + } else { + $xcode_spinbox configure -to $maximum_xcode + $xcode_scale configure -to $maximum_xcode + if {[winfo ismapped $details_xcode_nota]} { + pack forget $details_xcode_nota + } + pack $details_xcode_aval -fill both -expand 1 -padx 2 + } + + # Clear search bar + if {$args != {noclear}} { + clear_search_bar + } + + # Load image + set image [$image_label cget -image] + $image_label configure \ + -fg {#888888} \ + -text [mc "Loading image ..."] \ + -image ::ICONS::16::exec + if {$image != {} && $image != {::ICONS::16::no} && $image != {::ICONS::16::exec}} { + image delete $image + } + update + if {[catch { + $image_label configure -text { } -image [image create photo \ + -format png -file "${::ROOT_DIRNAME}/icons/mcu/$mcu_name.png" + ] + }]} then { + $image_label configure \ + -fg {#DD0000} \ + -text [mc " Image not found"] \ + -image ::ICONS::16::no + } + } + + ## Set scrollbar for details text + # If frac0 == 0 && frac1 == 1 -> hide scrollbar + # @parm Float frac0 - Fraction of the topmost visible area + # @parm Float frac1 - Fraction of the bottommost visible area + # @return void + proc details_scrollbar_set {frac0 frac1} { + variable more_details_scrollbar ;# Widget: Scrollbar for $more_details_text + variable more_details_text ;# Widget: TextWidget "More details:" + + # Hide scrollbar + if {$frac0 == 0 && $frac1 == 1} { + if {[winfo ismapped $more_details_scrollbar]} { + pack forget $more_details_scrollbar + } + # Show scrollbar + } else { + if {![winfo ismapped $more_details_scrollbar]} { + pack $more_details_scrollbar \ + -side right -fill y \ + -after $more_details_text + } + $more_details_scrollbar set $frac0 $frac1 + } + } + + ## Clear search entry box + # @return void + proc clear_search_bar {} { + variable search_bar ;# Widget: Search bar entry box + $search_bar delete 0 end + } + + ## Search for the give string in the listbox of available processors + # Primary purpose is validator for search entry box, it also + #+ ajusts search entry box background color + # @parm String string - Part of MCU name + # @return Bool - allways 1 + proc search {string} { + variable search_bar ;# Widget: Search bar entry box + variable search_bar_clear ;# Widget: Search bar clear button + variable listbox_widget ;# Widget: List box containing available MCUs + variable mcu_names ;# List: Available processors (and show in the list) + + # Search for empty string -> abort + if {![string length $string]} { + $search_bar_clear configure -state disabled + $search_bar configure -style TEntry + return 1 + } + + $search_bar_clear configure -state normal + + # Do a case-insensitive search + set string [string toupper $string] + + set i 0 + foreach mcu $mcu_names { + if {[string first $string [string toupper $mcu]] != -1} { + $search_bar configure -style StringFound.TEntry + set item [$listbox_widget items $i] + $listbox_widget selection set $item + $listbox_widget see $item + select_item noclear + return 1 + } + incr i + } + + $search_bar configure -style StringNotFound.TEntry + return 1 + } + + ## Close MCU selection dialog and discart its result + # @return void + proc cancel {} { + variable selected_mcu ;# List: Dialog return value {mcu_type xdata xcode} + set selected_mcu {} + close_window + } + + ## Disable/Enable XDATA memory + # @return void + proc xdata_disena {} { + variable xdata_scale ;# Widget: Scale for XDATA memory + variable xdata_spinbox ;# Widget: Scale for XDATA memory + + if {${::SelectMCU::xdata_ena}} { + $xdata_scale state !disabled + $xdata_spinbox configure -state normal + } else { + $xdata_scale state disabled + $xdata_spinbox configure -state disabled + } + } + + ## Disable/Enable XCODE memory + # @return void + proc xcode_disena {} { + variable xcode_spinbox ;# Widget: SpinBox for XCODE memory + variable xcode_scale ;# Widget: Scale for XCODE memory + + if {${::SelectMCU::xcode_ena}} { + $xcode_scale state !disabled + $xcode_spinbox configure -state normal + } else { + $xcode_scale state disabled + $xcode_spinbox configure -state disabled + } + } + + ## Validate XDATA memory spinbox + # @parm String string - String to validate + # @return Bool - Validation result + proc validate_xdata {string} { + if {![string is digit $string]} { + return 0 + } + if {$string == {}} { + return 1 + } + if {$string < 0 || $string > 0x10000} { + return 0 + } + + return 1 + } + + ## Validate XCODE memory spinbox + # @parm String string - String to validate + # @return Bool - Validation result + proc validate_xcode {string} { + variable maximum_xcode ;# Int: Maximum external program memory (0x10000 - internal) + + if {![string is digit $string]} { + return 0 + } + if {$string == {}} { + return 1 + } + if {$string < 0 || $string > $maximum_xcode} { + return 0 + } + + return 1 + } + + ## This functionshould be changecmd for vendor comboBox + # @return void + proc change_vendor {} { + variable listbox_widget ;# Widget: List box containing available MCUs + + clear_search_bar + $listbox_widget delete [$listbox_widget items] + fill_gui + } +} + +# >>> File inclusion guard +} +# <<< File inclusion guard diff --git a/lib/dialogues/tips.tcl b/lib/dialogues/tips.tcl new file mode 100644 index 0000000..d2df211 --- /dev/null +++ b/lib/dialogues/tips.tcl @@ -0,0 +1,402 @@ +#!/usr/bin/tclsh +# Part of MCU 8051 IDE ( http://mcu8051ide.sf.net ) + +############################################################################ +# Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 by Martin Ošmera # +# martin.osmera@gmail.com # +# # +# This program is free software; you can redistribute it and#or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation; either version 2 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program; if not, write to the # +# Free Software Foundation, Inc., # +# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # +############################################################################ + +# >>> File inclusion guard +if { ! [ info exists _TIPS_TCL ] } { +set _TIPS_TCL _ +# <<< File inclusion guard + +# -------------------------------------------------------------------------- +# DESCRIPTION +# Provides facility to show tips on start-up +# * Tips are readed from file deindef in NS variable "tips_file" +# * Format of definition file is XML and it supports mutiple languages +# * Usage is simple: execute procedure "::Tips::show_tip_of_the_day_win" +# * It requires NS ConfigDialogues (see ${::GLOBAL_CONFIG(tips)}) +# -------------------------------------------------------------------------- + +namespace eval Tips { + variable tip_of_the_day_win ;# ID of window "Tip of the day" + variable tip_of_the_day_text ;# ID of text widget in "Tip of the day" + variable tip_of_the_day_show_again ;# Bool: Show "Tip of the day" + + variable tips_data ;# List containing tips data + variable number_of_tips ;# Number of tips available + variable current_tip ;# Number of the currently displayed tip + variable expected ;# Expeceted element + variable take_data ;# Bool: Append data section to $tips_data + # File containing tips data + variable tips_file "${::INSTALLATION_DIR}/data/tips.xml" + + ## Invoke dialog "Tip on start-up" + # @return void + proc show_tip_of_the_day_win {} { + variable tip_of_the_day_win ;# ID of window "Tip of the day" + variable tip_of_the_day_text ;# ID of text widget in "Tip of the day" + variable tip_of_the_day_show_again ;# Bool: Show "Tip of the day" + variable number_of_tips ;# Number of tips available + variable tip_of_the_day_show_again ;# Bool: Show "Tip of the day" + + # Set value of checkbox "Show again" + set tip_of_the_day_show_again ${::GLOBAL_CONFIG(tips)} + # Load tips definition file + load_tips_file + + # Create toplevel window + set win [toplevel .tip_of_the_day -class {Tip of the day} -bg ${::COMMON_BG_COLOR}] + set tip_of_the_day_win $win + + # Create window header + pack [label $win.header \ + -text [mc "Did you know ... "] \ + -font [font create \ + -family {times} \ + -size [expr {int(-25 * $::font_size_factor)}] \ + -weight bold \ + ] \ + -compound right \ + -image ::ICONS::32::help \ + ] -pady 5 + + # Create middle frame (text windget and scrollbar) + set middle_frame [frame $win.middle_frame] + set text [text $middle_frame.text \ + -width 0 -height 0 -bg white \ + -wrap word \ + -yscrollcommand "$middle_frame.scrollbar set" \ + -font [font create \ + -family {helvetica} \ + -size [expr {int(-14 * $::font_size_factor)}] \ + -weight normal \ + ] \ + ] + pack $text -side left -fill both -expand 1 + pack [ttk::scrollbar $middle_frame.scrollbar \ + -orient vertical \ + -command [list $text yview] \ + ] -side left -fill y -after $text + set tip_of_the_day_text $text + + ## Create bottom frame + set bottom_frame [frame $win.bottom_frame] + # - CheckButton "Show tips on start-up" + pack [checkbutton $bottom_frame.chbutton \ + -variable ::Tips::tip_of_the_day_show_again \ + -command {::Tips::tip_otd_show_again} \ + -text [mc "Show tips on start-up"] \ + ] -side left -anchor e + # - Button "Close" + pack [ttk::button $bottom_frame.close_but \ + -compound left \ + -text [mc "Close"] \ + -image ::ICONS::16::button_cancel \ + -command {::Tips::tip_otd_CLOSE} \ + -width 8 \ + ] -side right -anchor w -padx 2 + # - Button "Next" + pack [ttk::button $bottom_frame.next_but \ + -compound left \ + -text [mc "Next"] \ + -image ::ICONS::16::right \ + -command {::Tips::tip_otd_NEXT} \ + -width 8 \ + ] -side right -anchor w -padx 2 + # - Button "Previous" + pack [ttk::button $bottom_frame.prev_but \ + -compound left \ + -text [mc "Previous"] \ + -image ::ICONS::16::left \ + -command {::Tips::tip_otd_PREV} \ + -width 8 \ + ] -side right -anchor w -padx 2 + + # Pack window frames + pack $middle_frame -side top -fill both -expand 1 -padx 10 -pady 5 + pack $bottom_frame -side bottom -fill x -after $middle_frame -padx 10 -pady 5 + + # Configure text tags + $text tag configure tag_bold -font [font create \ + -family {helvetica} \ + -size [expr {int(-14 * $::font_size_factor)}] \ + -weight bold \ + ] + # Configure text tags + $text tag configure tag_code -font [font create \ + -family $::DEFAULT_FIXED_FONT \ + -size [expr {int(-14 * $::font_size_factor)}] \ + -weight normal \ + ] -foreground {#DD8800} + + # Create tag for external hyperlinks + create_link_tag_in_text_widget $text + + # Determinate random number of tip to show + expr {srand([clock seconds])} + display_tip [expr {int(rand() * $number_of_tips)}] + + # Configure dialog window + wm iconphoto $win ::ICONS::16::info + wm title $win [mc "Tip of the day - MCU 8051 IDE"] + wm minsize $win 520 250 + wm protocol $win WM_DELETE_WINDOW { + ::Tips::tip_otd_CLOSE + } + wm transient $win . + raise $win + catch { + grab $win + } + } + + ## Load definition of tips + # @return void + proc load_tips_file {} { + variable tips_data ;# List containing tips data + variable number_of_tips ;# Number of tips available + variable tips_file ;# File containing tips data + variable expected ;# Expeceted element + variable take_data ;# Bool: Append data section to $tips_data + + # Initialize NS variables + set take_data 0 + set number_of_tips 0 + set expected {tips} + set tips_data {} + + # Open file + if {[catch { + set file [open $tips_file {r}] + }]} then { + tk_messageBox \ + -parent . \ + -type ok \ + -icon warning \ + -title "tips.xml" \ + -message [mc "Unable to open file containing tips,\nplease check your installation"] + return + } + + # Create XML parser + set parser [::xml::parser -final 1 -ignorewhitespace 1 \ + -elementstartcommand ::Tips::xml_data_parser_element \ + -characterdatacommand ::Tips::xml_data_parser_data \ + ] + + # Start XML parser + if {[catch { + $parser parse [read $file] + } result]} then { + set number_of_tips 0 + set tips_data {} + tk_messageBox \ + -parent . \ + -type ok \ + -icon warning \ + -title [mc "Unable to parse tips.xml"] \ + -message [mc "File tips.xml is corrupted,\nplease check your installation"] + puts stderr $result + return + } + + # Close file and free parser + close $file + $parser free + } + + ## Universal parser handler - handles XML tags and data + # @parm String arg1 - content of the element + # @return void + proc xml_data_parser_data {arg1} { + variable tips_data ;# List containing tips data + variable number_of_tips ;# Number of tips available + variable current_tip ;# Number of the currently displayed tip + variable expected ;# Expeceted element + variable take_data ;# Bool: Append data section to $tips_data + + if {!$take_data} { + return + } + + set take_data 0 + incr number_of_tips + + regsub -all {^\s+} $arg1 {} arg1 + regsub -all {\s+$} $arg1 {} arg1 + lappend tips_data [regsub -all -line {^\t+} $arg1 {}] + } + + ## XML parser handler - handles XML tags + # @parm String arg1 - name of the element + # @parm List attrs - list of attributes '{attr0 val0 attr1 val1 ...}' + # @return void + proc xml_data_parser_element {arg1 attrs} { + variable tips_data ;# List containing tips data + variable number_of_tips ;# Number of tips available + variable current_tip ;# Number of the currently displayed tip + variable expected ;# Expeceted element + variable take_data ;# Bool: Append data section to $tips_data + + if {$arg1 != $expected} { + error "Bad element `$arg1'" + } + if {$arg1 == {tips}} { + set expected {tip} + } + + # Iterate over element attributes + for {set i 0} {$i < [llength $attrs]} {incr i} { + if {[lindex $attrs $i] != {lang}} { + incr i + continue + } + incr i + + # Take data only if some translation has been loaded and it conforms with the text + if {[string tolower [lindex $attrs $i]] == [string tolower ${::GLOBAL_CONFIG(language)}]} { + set take_data 1 + } else { + set take_data 0 + } + } + } + + ## Close dialog + # @return void + proc tip_otd_CLOSE {} { + variable tips_data ;# List containing tips data + variable number_of_tips ;# Number of tips available + variable current_tip ;# Number of the currently displayed tip + variable tip_of_the_day_win ;# ID of window "Tip of the day" + + # Remove dialog + grab release $tip_of_the_day_win + destroy $tip_of_the_day_win + + # Free dialog resources + set tips_data {} + set number_of_tips {} + set current_tip {} + } + + ## Display tip with the given number in the window + # @parm Int tip_number - number of the tip to show (can overlap allowed range) + # @return void + proc display_tip {tip_number} { + variable tips_data ;# List containing tips data + variable number_of_tips ;# Number of tips available + variable current_tip ;# Number of the currently displayed tip + variable tip_of_the_day_text ;# ID of text widget in "Tip of the day" + + set current_tip $tip_number + + # Clear text widget + $tip_of_the_day_text configure -state normal + $tip_of_the_day_text delete 1.0 end + + # Validate tip number + if {!$number_of_tips} { + $tip_of_the_day_text configure -state disabled + return + } + if {$tip_number >= $number_of_tips} { + set current_tip $number_of_tips + incr current_tip -1 + } + + # Create map of bold and code font tags + set bold_tag_map [list] + set code_tag_map [list] + set content [lindex $tips_data $current_tip] + foreach map {bold_tag_map code_tag_map} \ + tag {b c} \ + { + while {1} { + set tag_pair {} + + set idx [string first "<$tag>" $content] + if {$idx == -1} {break} + regsub "<$tag>" $content {} content + lappend tag_pair $idx + + set idx [string first "</$tag>" $content] + if {$idx == -1} {break} + regsub "</$tag>" $content {} content + lappend tag_pair $idx + + lappend $map $tag_pair + } + } + + # Fill text widget + set start [$tip_of_the_day_text index insert] + $tip_of_the_day_text insert end $content + foreach pair $bold_tag_map { + $tip_of_the_day_text tag add tag_bold $start+[lindex $pair 0]c $start+[lindex $pair 1]c + } + foreach pair $code_tag_map { + $tip_of_the_day_text tag add tag_code $start+[lindex $pair 0]c $start+[lindex $pair 1]c + } + $tip_of_the_day_text configure -state disabled + + # Detect external hyperlinks and make the functional + convert_all_https_to_links $tip_of_the_day_text + } + + ## Show next tip + # @return void + proc tip_otd_NEXT {} { + variable number_of_tips ;# Number of tips available + variable current_tip ;# Number of the currently displayed tip + + incr current_tip + if {$current_tip >= $number_of_tips} { + set current_tip 0 + } + display_tip $current_tip + } + + ## Show previous tip + # @return void + proc tip_otd_PREV {} { + variable number_of_tips ;# Number of tips available + variable current_tip ;# Number of the currently displayed tip + + incr current_tip -1 + if {$current_tip < 0} { + set current_tip [expr {$number_of_tips - 1}] + } + display_tip $current_tip + } + + ## Adjust base configuration file to variable "tip_of_the_day_show_again" + # @return void + proc tip_otd_show_again {} { + variable tip_of_the_day_show_again ;# Bool: Show "Tip of the day" + + ::configDialogues::global::set_variable tips $tip_of_the_day_show_again + } +} + +# >>> File inclusion guard +} +# <<< File inclusion guard |