diff options
author | Andrej Shadura <andrewsh@debian.org> | 2018-05-08 15:59:29 +0200 |
---|---|---|
committer | Andrej Shadura <andrewsh@debian.org> | 2018-05-08 15:59:29 +0200 |
commit | 5b8466f7fae0e071c0f4eda13051c93313910028 (patch) | |
tree | 7061957f770e5e245ba00666dad912a2d44e7fdc /lib/bottompanel/calculator.tcl |
Import Upstream version 1.3.7
Diffstat (limited to 'lib/bottompanel/calculator.tcl')
-rwxr-xr-x | lib/bottompanel/calculator.tcl | 2071 |
1 files changed, 2071 insertions, 0 deletions
diff --git a/lib/bottompanel/calculator.tcl b/lib/bottompanel/calculator.tcl new file mode 100755 index 0000000..5196b40 --- /dev/null +++ b/lib/bottompanel/calculator.tcl @@ -0,0 +1,2071 @@ +#!/usr/bin/tclsh +# Part of MCU 8051 IDE ( http://mcu8051ide.sf.net ) + +############################################################################ +# Copyright (C) 2007-2009 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. # +############################################################################ + +# -------------------------------------------------------------------------- +# DESCRIPTION +# +# -------------------------------------------------------------------------- + +class Calculator { + + common count 0 ;# counter of instances + # Font for numerical keypad + common large_font [font create \ + -family {helveticat} \ + -size -12 \ + -weight {bold} \ + ] + + common oper_fg_color {#0000FF} ;# Foreground color for operator display + common error_bg_color {#FF6666} ;# Background color for display containing too many characters + common display_bg_color {#DDFFDD} ;# Background color for main display + common buffer_bg_color {#DDDDFF} ;# Background color for buffer display + + # Variables related to object initialization + private variable parent ;# Teportary variable -- GUI parent + private variable calculatorList ;# Teportary variable -- COnfiguration list + private variable gui_initialized 0 ;# Bool: GUI created + + # GUI variables + private variable calc_num_keypad ;# Container of left side of calc. (keypad) + private variable calc_num_display ;# Container for right side (displays etc.) + private variable calc_timers_calc ;# ID of label frame of timer preset calculator + private variable calc_display_widget ;# ID of main display widget + private variable calc_oper_widget ;# ID of operator display widget + private variable calc_buffer_widget ;# ID of buffer display widget + private variable timerscalc_THxDec_label ;# ID of THx (dec) label + private variable timerscalc_THxHex_label ;# ID of THx (hex) label + private variable timerscalc_THxOct_label ;# ID of THx (oct) label + private variable timerscalc_TLxDec_label ;# ID of TLx (dec) label + private variable timerscalc_TLxHex_label ;# ID of TLx (hex) label + private variable timerscalc_TLxOct_label ;# ID of TLx (oct) label + private variable timerscalc_RepeatDec_label ;# ID of Repeat (dec) label + private variable timerscalc_RepeatHex_label ;# ID of Repeat (hex) label + private variable timerscalc_RepeatOct_label ;# ID of Repeat (oct) label + private variable timerscalc_CorrectionDec_label ;# ID of Correction (dec) label + private variable timerscalc_CorrectionHex_label ;# ID of Correction (hex) label + private variable timerscalc_CorrectionOct_label ;# ID of Correction (oct) label + private variable timerscalc_freq_entry ;# ID of frequency entry widget + private variable timerscalc_mode_spinbox ;# ID of mode spinbox widget + private variable timerscalc_time_entry ;# ID of tim entry widget + private variable mem_entry_0 ;# ID of memory 0 entry widget + private variable mem_entry_1 ;# ID of memory 1 entry widget + private variable mem_entry_2 ;# ID of memory 2 entry widget + + # Core variables + private variable base ;# Numeric base (Hex, Dec. Oct, Bin) + private variable last_base ;# Last numeric base + private variable angle ;# Angle unit (rad, deg, grad) + private variable last_angle ;# Last angle unit + private variable calc_oper {} ;# Chosen mathematical operation + private variable calc_oper_h ;# Human readible $calc_oper + private variable calc_last_oper ;# Last $calc_oper + private variable calc_display ;# Actual display text variable + private variable calc_buffer ;# Last display text variable + private variable calc_last_display ;# Var. for UNDO/REDO (takes back $calc_display) + private variable calc_last_buffer ;# Var. for UNDO/REDO (takes back $calc_buffer) + private variable ena_undo 0 ;# Undo enabled + private variable ena_redo 0 ;# Redo enabled + private variable after_eval 0 ;# Clear display if actual display val. is result of last operation + private variable scrollable_frame ;# Widget: Scrollable area (parent for all other widgets) + private variable horizontal_scrollbar ;# Widget: Horizontal scrollbar for scrollable area + + # other public variables + private variable calc_idx ;# number of current instance + private variable timerscalc_validation_dis 1 ;# Disabled validation of timers calc + + # definition of calculator keyboard + # { + # # row + # { # button + # {text path_part command_postfix + # columnspan rowspan + # helptext width + # height bgColor + # activeBackground bool_large_font + # } + # {separator} + # } + # } + common calculator_keyboard { + { + {{AND} {and} {calc_opr and 1} {} {} + {Bit-wise AND} + {5} {} {} {Calculator_GREEN} {#CCFFCC} 0 + {Bit-wise AND. Valid for integer operands only.}} + {{Sin} {S} {calc_opr Sin 1} {} {} + {Sine} + {5} {} {} {Calculator_RED} {#FFDDDD} 0 + {Sine}} + {{Cos} {Cs} {calc_opr Cos 1} {} {} + {Cosine} + {5} {} {} {Calculator_RED} {#FFDDDD} 0 + {Cosine}} + {{Tan} {T} {calc_opr Tan 1} {} {} + {Tangent} + {5} {} {} {Calculator_RED} {#FFDDDD} 0 + {Tangent}} + {{A} {A} {calc_val A} {} {} {} {} {} {5} {Calculator_PURPLE} {#DDDDFF} 1} + {{F} {F} {calc_val F} {} {} {} {} {} {} {Calculator_PURPLE} {#DDDDFF} 1} + {{/} {div} {calc_opr div 1} {} {} {} {} {} {} {Calculator_YELLOW} {#FFFFDD} 1} + {{*} {mul} {calc_opr mul 1} {} {} {} {} {} {} {Calculator_YELLOW} {#FFFFDD} 1} + {{-} {min} {calc_opr min 1} {} {} {} {} {} {} {Calculator_YELLOW} {#FFFFDD} 1} + } { + {{OR} {or} {calc_opr or 1} {} {} + {Bit-wise OR} + {5} {} {} {Calculator_GREEN} {#CCFFCC} 0 + {Bit-wise OR. Valid for integer operands only.}} + {{ASin} {AS} {calc_opr ASin 1} {} {} + {Arc sine} + {5} {} {} {Calculator_RED} {#FFDDDD} 0 + {Arc sine. Argument should be in the range \[-1,1\].}} + {{ACos} {AC} {calc_opr ACos 1} {} {} + {Arc cosine} + {5} {} {} {Calculator_RED} {#FFDDDD} 0 + {Arc cosine. Argument should be in the range \[-1,1\].}} + {{ATan} {AT} {calc_opr ATan 1} {} {} + {Arc tangent} + {5} {} {} {Calculator_RED} {#FFDDDD} 0 + {Arc tangent}} + + {{B} {B} {calc_val B} {} {} {} {} {} {} {Calculator_PURPLE} {#DDDDFF} 1} + {{7} {7} {calc_val 7} {} {} {} {} {} {} {Calculator_PURPLE} {#DDDDFF} 1} + {{8} {8} {calc_val 8} {} {} {} {} {} {} {Calculator_PURPLE} {#DDDDFF} 1} + {{9} {9} {calc_val 9} {} {} {} {} {} {} {Calculator_PURPLE} {#DDDDFF} 1} + {{+} {add} {calc_opr add 1} {} {2} {} {} {2} {} {Calculator_YELLOW} {#FFFFDD} 1} + } { + {{NOT} {not} {calc_opr not 1} {} {} + {Bit-wise NOT} + {5} {} {} {Calculator_GREEN} {#CCFFCC} 0 + {Bit-wise NOT. Valid for integer operands only.}} + {{e**} {exp} {calc_opr Exp 1} {} {} + {Exponential of argument (e**arg)} + {5} {} {} {Calculator_CYAN} {#AAFFFF} 0 + {Exponential of argument (e**arg)}} + {{sqrt} {sqr} {calc_opr Sqr 1} {} {} + {Square root} + {5} {} {} {Calculator_CYAN} {#AAFFFF} 0 + {Square root. Argument must be non-negative.}} + {{pow} {power} {calc_opr pow 1} {} {} + {Power} + {5} {} {} {Calculator_CYAN} {#AAFFFF} 0 + {Computes the value of x raised to the power y. If x is negative, y must be an integer value.}} + + {{C} {C} {calc_val C} {} {} {} {} {} {} {Calculator_PURPLE} {#DDDDFF} 1} + {{4} {4} {calc_val 4} {} {} {} {} {} {} {Calculator_PURPLE} {#DDDDFF} 1} + {{5} {5} {calc_val 5} {} {} {} {} {} {} {Calculator_PURPLE} {#DDDDFF} 1} + {{6} {6} {calc_val 6} {} {} {} {} {} {} {Calculator_PURPLE} {#DDDDFF} 1} + } { + {{XOR} {xor} {calc_opr xor 1} {} {} + {Bit-wise exclusive OR} + {5} {} {} {Calculator_GREEN} {#CCFFCC} 0 + {Bit-wise exclusive OR. Valid for integer operands only.}} + {{Log} {L} {calc_opr Log 1} {} {} + {Base 10 logarithm} + {5} {} {} {Calculator_CYAN} {#AAFFFF} 0 + {Returns the base 10 logarithm of argument. Argument must be a positive value.}} + {{Ln} {Ln} {calc_opr Ln 1} {} {} + {Natural logarithm} + {5} {} {} {Calculator_CYAN} {#AAFFFF} 0 + {Returns the natural logarithm of argument. Argument must be a positive value.}} + {{PI} {P} {calc_val PI} {} {} + {Constant Pi} + {5} {} {} {Calculator_CYAN} {#AAFFFF} 0 + {Constant Pi}} + + {{D} {D} {calc_val D} {} {} {} {} {} {} {Calculator_PURPLE} {#DDDDFF} 1} + {{1} {1} {calc_val 1} {} {} {} {} {} {} {Calculator_PURPLE} {#DDDDFF} 1} + {{2} {2} {calc_val 2} {} {} {} {} {} {} {Calculator_PURPLE} {#DDDDFF} 1} + {{3} {3} {calc_val 3} {} {} {} {} {} {} {Calculator_PURPLE} {#DDDDFF} 1} + {{=} {=} {calc_Evaluate} {} {2} {} {} {2} {} {Calculator_YELLOW} {#FFFFDD} 1} + } { + {{>>} {right} {calc_opr right 1} {} {} + {Right shift} + {5} {} {} {Calculator_GREEN} {#CCFFCC} 0 + {Right shift. Valid for integer operands only. A right shift always propagates the sign bit.}} + {{Mod} {M} {calc_opr mod 1} {} {} + {Modulo} + {5} {} {} {Calculator_CYAN} {#AAFFFF} 0 + {Computes remainder of integer division}} + {{UNDO} {U} {calc_UNDO} {} {} + {Undo last operation} + {5} {} {} {Calculator_GRAY} {#F8F8F8} 0 + {Undo last operation. Not all operations are supported.}} + {{REDO} {RE} {calc_REDO} {} {} + {Take back last undo operation} + {5} {} {} {Calculator_GRAY} {#F8F8F8} 0 + {Take back last undo operation. Not all operations are supported.}} + + {{E} {E} {calc_val E} {} {} {} {} {} {} {Calculator_PURPLE} {#DDDDFF} 1} + {{0} {0} {calc_val 0} {2} {} {} {5} {} {} {Calculator_PURPLE} {#DDDDFF} 1} + + {{.} {dot} {calc_val .} {} {} {} {} {} {} {Calculator_PURPLE} {#DDDDFF} 1} + } + } + + ## object constructor + constructor {} { + # Initialize some variables + incr count ;# Instance counter + set calc_idx $count ;# Index of this object + set base Dec ;# Default numeric base + set angle rad ;# Default angle unit + set last_base $base ;# Last numeric base + set last_angle $angle ;# Last angle unit + + # Configure ttk styles + ttk::style configure Calculator_Buffer.TEntry \ + -fieldbackground $buffer_bg_color + ttk::style configure Calculator_Oper.TEntry \ + -fieldbackground {#FFDDDD} \ + -fieldforeground $oper_fg_color \ + -justify center + ttk::style configure Calculator_OperError.TEntry\ + -fieldbackground {#FFDDDD} \ + -foreground {#FF0000} \ + -justify center + ttk::style configure Calculator_Display.TEntry \ + -fieldbackground $display_bg_color + ttk::style configure Calculator_Error.TEntry \ + -fieldbackground $error_bg_color + + ttk::style configure Calculator_GREEN.TButton -padding 2 + ttk::style map Calculator_GREEN.TButton \ + -background [list active {#AAFFAA} {!active !disabled} {#CCFFCC} disabled {#DDEEDD}] + + ttk::style configure Calculator_RED.TButton -padding 2 + ttk::style map Calculator_RED.TButton \ + -background [list active {#FFAAAA} {!active !disabled} {#FFDDDD} disabled {#EEDDDD}] + + ttk::style configure Calculator_CYAN.TButton -padding 2 + ttk::style map Calculator_CYAN.TButton \ + -background [list active {#88EEEE} {!active !disabled} {#AAFFFF} disabled {#DDEEEE}] + + ttk::style configure Calculator_GRAY.TButton -padding 2 + ttk::style map Calculator_GRAY.TButton \ + -background [list active {#DDDDDD} {!active !disabled} {#F8F8F8} disabled {#EEEEEE}] + + ttk::style configure Calculator_PURPLE.TButton -padding 2 -font $large_font + ttk::style map Calculator_PURPLE.TButton \ + -background [list active {#AAAAFF} {!active !disabled} {#DDDDFF} disabled {#DDDDEE}] + + ttk::style configure Calculator_YELLOW.TButton -padding 2 -font $large_font + ttk::style map Calculator_YELLOW.TButton \ + -background [list active {#FFFFAA} {!active !disabled} {#FFFFDD} disabled {#EEEEDD}] + } + + ## object destructor + destructor { + # Unallocate GUI related variables + catch { + unset ::Calculator::calc_base$calc_idx + unset ::Calculator::calc_angle$calc_idx + unset ::Calculator::calc_buffer$calc_idx + unset ::Calculator::calc_oper$calc_idx + unset ::Calculator::calc_displ$calc_idx + unset ::Calculator::calc_mem0_$calc_idx + unset ::Calculator::calc_mem1_$calc_idx + unset ::Calculator::calc_mem2_$calc_idx + } + } + + ## Append given value to the end of the display + # Use carefully, it does not check value validity ! + # @parm String value - value to append + # @return String - new display content + public method calc_val {value} { + # Read raw content of the main display + reread_display 1 + + # Insert PI + if {$value == {PI}} { + # Conver PI to selected numeric base + switch -- $base { + {Hex} {set value [NumSystem::dec2hex ${Angle::PI}]} + {Dec} {set value ${Angle::PI}} + {Oct} {set value [NumSystem::dec2oct ${Angle::PI}]} + {Bin} {set value [NumSystem::dec2bin ${Angle::PI}]} + } + # Save current values + set calc_last_display [reread_display] ;# Main display + set calc_last_buffer [reread_buffer] ;# Buffer + # Clear main display + set calc_display {} + # Save current opetaror + set calc_last_oper $calc_oper + enable_undo ;# enable UNDO operation + + # Clear main display if current value is result of the last operation + } elseif {$after_eval} { + set calc_last_display $calc_display ;# Save current content of display + set calc_last_buffer {} + set calc_last_oper {} + set calc_display {} + set after_eval 0 + enable_undo ;# enable UNDO operation + } + + # Append given value to the end of main display + set tmp "$calc_display$value" + if {[calc_validate $calc_display_widget $tmp]} { + set ::Calculator::calc_displ$calc_idx $tmp + + catch { + $calc_display_widget delete sel.first sel.last + } + } + + $calc_display_widget icursor end + $calc_buffer_widget icursor end + return $calc_display + } + + ## Choose mathematical operation + # @parm String operation - Selected operation + # @parm Bool external - Evaluate result + # @return void + public method calc_opr {operation external} { + + # Save current operator and set the new one + set calc_last_oper $calc_oper + set calc_oper $operation + # Clear displays if external + if {$external} { + set calc_last_display [reread_display] + set calc_last_buffer [reread_buffer] + set calc_buffer $calc_display + set calc_display {} + enable_undo + rewrite_buffer + rewrite_display + } + + # Evaluate specified operation + switch -- $operation { + {div} { ;# Division + set calc_oper_h {/} + } + {mul} { ;# Multiplication + set calc_oper_h {*} + } + {min} { ;# Subtraction + set calc_oper_h {-} + } + {add} { ;# Addition + set calc_oper_h {+} + } + {pow} { ;# Power + set calc_oper_h {**} + } + {mod} { ;# Modulo + set calc_oper_h {mod} + } + {and} { ;# Bit-wise and + set calc_oper_h {&} + } + {or} { ;# Bit-wise inclusive or + set calc_oper_h {|} + } + {xor} { ;# Bit-wise exclusive or + set calc_oper_h {^} + } + {right} { ;# Right shift + set calc_oper_h {>>} + } + + {not} { ;# Bit-wise inversion + set calc_oper_h {~} + if {$external} {calc_Evaluate} + } + {Exp} { ;# Exponential of argument + set calc_oper_h {e**} + if {$external} {calc_Evaluate} + } + {Sqr} { ;# Square root + set calc_oper_h {sqrt} + if {$external} {calc_Evaluate} + } + {Log} { ;# Decimal logarithm + set calc_oper_h {lg} + if {$external} {calc_Evaluate} + } + {Ln} { ;# Natural logarithm + set calc_oper_h {ln} + if {$external} {calc_Evaluate} + } + {Sin} { ;# Sine + set calc_oper_h {sin} + if {$external} {calc_Evaluate} + } + {Cos} { ;# Cosine + set calc_oper_h {cos} + if {$external} {calc_Evaluate} + } + {Tan} { ;# Tangent + set calc_oper_h {tan} + if {$external} {calc_Evaluate} + } + {ASin} { ;# Arc sine + set calc_oper_h {asin} + if {$external} {calc_Evaluate} + } + {ACos} { ;# Arc cosine + set calc_oper_h {acos} + if {$external} {calc_Evaluate} + } + {ATan} { ;# Acr cotangent + set calc_oper_h {atan} + if {$external} {calc_Evaluate} + } + default { ;# No operand + set calc_oper_h {} + } + } + + # Display selected operand + set ::Calculator::calc_oper$calc_idx $calc_oper_h + } + + ## Perform operation with calulator memory + # @parm String action - "Save" (to main display) or "Load" (from main display) + # @parm Int cell - Index of memory cell + # @return void + public method mem {action cell} { + if {$action == {Save}} { + # Show message on status bar + Sbar [mc "Calculator: M%s saved" $cell] + # Save content of main display + set calc_mem [reread_display] + if {[regexp {\.0$} $calc_mem]} { + set calc_mem [string range $calc_mem 0 {end-2}] + } + set ::Calculator::calc_mem${cell}_$calc_idx $calc_mem + } { + # Load memory content into main display + set calc_display [subst "\$::Calculator::calc_mem${cell}_$calc_idx"] + rewrite_display + } + } + + ## Perform evaluation of given mathematical expression + # @return void + public method calc_Evaluate {} { + + ## Check for presence of nessesary values + # * For unary operations + set display [reread_display] + set buffer [reread_buffer] + if {$buffer == {} || $buffer == {-}} { + Sbar [mc "Calculator: Unable to evaluate, missing argument"] + return 0 + } + if {$calc_oper == {}} { + Sbar [mc "Calculator: Unable to evaluate, missing operator"] + return 0 + } + # * For binary operations + if { + $calc_oper == {div} || $calc_oper == {mul} || + $calc_oper == {min} || $calc_oper == {add} || + $calc_oper == {pow} || $calc_oper == {mod} || + $calc_oper == {and} || $calc_oper == {or} || + $calc_oper == {xor} || $calc_oper == {nand} + } { + # Check display value length + if {$display == {}} { + Sbar [mc "Calculator: Unable to evaluate, missing argument"] + return 0 + } + } + + # Make backup for display, buffer and operator + enable_undo ;# enable UNDO operation + set calc_last_display $display + set calc_last_buffer $buffer + + # Load up content of buffer and display + read_buffer_inDec + read_display_inDec + + # Perform evaluation in safe environment + if {[catch { + switch -- $calc_oper { + {and} { ;# Bit-wise and + set calc_display [expr {wide($calc_buffer) & wide($calc_display)}] + } + {or} { ;# Bit-wise or + set calc_display [expr {wide($calc_buffer) | wide($calc_display)}] + } + {xor} { ;# Bit-wise xor + set calc_display [expr {wide($calc_buffer) ^ wide($calc_display)}] + } + {right} { ;# Right shift + set tmp [expr {wide($calc_display)}] + if {$tmp > 0} { + set calc_display [expr {wide($calc_buffer) >> $tmp}] + } elseif {$tmp < 0} { + set calc_display [expr {wide($calc_buffer) << abs($tmp)}] + } else { + set calc_display [expr {wide($calc_buffer)}] + } + } + {mul} { ;# Multiplication + set calc_display [expr {$calc_buffer * $calc_display}] + } + {min} { ;# Subtraction + set calc_display [expr {$calc_buffer - $calc_display}] + } + {add} { ;# Addtion + set calc_display [expr {$calc_buffer + $calc_display}] + } + {mod} { ;# Modulo + set calc_display [expr {int(fmod($calc_buffer,$calc_display))}] + } + {pow} { ;# Power + set calc_display [expr {pow($calc_buffer, $calc_display)}] + } + {div} { ;# Division + if {$calc_display == 0} { + Sbar [mc "Calculator: WARNING result is +/- infinity => operation terminated !"] + return + } + if {![regexp {\.} $calc_buffer]} { + set calc_buffer "$calc_buffer.0" + } + set calc_display [expr {$calc_buffer / $calc_display}] + } + {not} { ;# Bit-wise inversion + set len [string length [format {%X} [expr {int($calc_buffer)}]]] + if {$len > 8} { + Sbar [mc "Calculator: This value is too high to invert (max. 0xFFFFFFFF)"] + return + } + incr len -1 + set calc_display [expr {0x7FFFFFFF ^ int($calc_buffer)}] + set calc_display [format {%X} $calc_display] + set calc_display [string range $calc_display end-$len end] + set calc_display [expr "0x$calc_display"] + } + {Exp} { ;# Exponential of argument + set calc_display [expr {exp($calc_buffer)}] + } + {Sqr} { ;# Square root + set calc_display [expr {sqrt($calc_buffer)}] + } + {Log} { ;# Decimal logarithm + set calc_display [expr {log10($calc_buffer)}] + } + {Ln} { ;# Natiral logarithm + set calc_display [expr {log($calc_buffer)}] + } + {ASin} { ;# Arc sine + set calc_display [expr {asin($calc_buffer)}] + set calc_display [rad_to_Xangle $calc_display] + } + {ACos} { ;# Arc cosine + set calc_display [expr {acos($calc_buffer)}] + set calc_display [rad_to_Xangle $calc_display] + } + {ATan} { ;# Arc Tangent + set calc_display [expr {atan($calc_buffer)}] + set calc_display [rad_to_Xangle $calc_display] + } + {Sin} { ;# Sine + set calc_buffer [Xangle_to_rad $calc_buffer] + set calc_display [expr {sin($calc_buffer)}] + } + {Cos} { ;# Cosine + set calc_buffer [Xangle_to_rad $calc_buffer] + set calc_display [expr {cos($calc_buffer)}] + } + {Tan} { ;# Arc tangent + set calc_buffer [Xangle_to_rad $calc_buffer] + set calc_display [expr {tan($calc_buffer)}] + } + } + }]} { + # If error occured -> show error message + Sbar [mc "Calculator: ERROR (result value is out of allowed range)"] + return + } + + # If result value contain exponent -> show error message + if {[regexp {e} $calc_display]} { + Sbar[mc "Calculator: Unable to evaluate, result value is too high"] + return + } + + # Display result + set calc_buffer {} + set after_eval 1 + rewrite_buffer + calc_opr {} 0 + write_display_inXbase $calc_display + } + + ## Safely clear display + # @return void + public method calc_ClearActual {} { + # enable UNDO operation + enable_undo + # save actual values + set calc_last_display [reread_display] + set calc_display {} + set calc_last_buffer [reread_buffer] + # show new values + rewrite_display + } + + ## Safely clear display and buffer + # @return void + public method calc_Clear {} { + # enable UNDO operation + enable_undo + # save actual values + set calc_last_display [reread_display] + set calc_display {} + set calc_last_buffer [reread_buffer] + set calc_buffer {} + calc_opr {} 0 + # show new values + rewrite_display + rewrite_buffer + } + + ## Enable execution of UNDO operation and disable REDO + # @return void + private method enable_undo {} { + # set status + set ena_undo 1 + set ena_redo 0 + # enable/disable UNDO and REDO buttons + enable_buttons {U} + disable_buttons {RE} + } + + ## Enable execution of REDO operation and disable UNDO + # @return void + private method enable_redo {} { + # set status + set ena_undo 0 + set ena_redo 1 + # enable/disable UNDO and REDO buttons + enable_buttons {RE} + disable_buttons {U} + } + + ## Take back the last operation + # @return void + public method calc_UNDO {} { + # enable REDO operation + enable_redo + # .... + set after_eval 0 + # save actual status and restore previous + calc_opr $calc_last_oper 0 + set tmp [reread_display] + set calc_display $calc_last_display + set calc_last_display $tmp + set tmp [reread_buffer] + set calc_buffer $calc_last_buffer + set calc_last_buffer $tmp + # show new values + rewrite_display + rewrite_buffer + # inform user by starts bar + Sbar [mc "Calculator: UNDO: previous state was: %s %s %s" $calc_last_buffer $calc_last_oper $calc_last_display] + } + + ## Take back the UNDO operation + # @return void + public method calc_REDO {} { + # enable UNDO operation + enable_undo + # save actual status and restore previous + calc_opr $calc_last_oper 0 + set tmp [reread_display] + set calc_display $calc_last_display + set calc_last_display $tmp + set tmp [reread_buffer] + set calc_buffer $calc_last_buffer + set calc_last_buffer $tmp + # show new values + rewrite_display + rewrite_buffer + # inform user by starts bar + Sbar [mc "Calculator: REDO: previous state was: %s %s %s" $calc_last_buffer $calc_last_oper $calc_last_display] + } + + ## Convert content of both displays and all merory cells using given command + # @parm String command - command to use for converion + # @return void + private method convert_displays {command} { + + # Determinate what displays aren't empty + if {[reread_display] == {}} {set dis 0} {set dis 1} + if {[reread_buffer] == {}} {set buf 0} {set buf 1} + + # Determinate what memory cells aren't empty + for {set i 0} {$i < 3} {incr i} { + set mem [[subst "\$mem_entry_$i"] get] + if {[string index $mem end] == {.}} { + append mem 0 + } + set memory$i $mem + if {$mem == {} || $mem == 0} { + set mem$i 0 + } { + set mem$i 1 + } + } + + # Convert all non empty displays + foreach cnd "$dis $buf $mem0 $mem1 $mem2" \ + var {calc_display calc_buffer memory0 memory1 memory2} { + if {$cnd} { + if {[catch { + set $var [$command [subst "\$$var"]] + }]} { + Sbar [mc "Calculator: Value is too high to convert, value deleted !"] + set $var 0 + } + } + } + + # Display new content of memory cells + for {set i 0} {$i < 3} {incr i} { + [subst "\$mem_entry_$i"] delete 0 end + [subst "\$mem_entry_$i"] insert end [subst "\$memory$i"] + } + } + + ## Switch numeric base + # @return void + public method cal_switchBase {} { + + # Get chosen value + set base [subst "\$::Calculator::calc_base$calc_idx"] + + # Convert display content to setected numeric system + if {$base == $last_base} { + set last_base $base + return + } + + # Adjust value in display and buffer + if {[regexp {\.0$} $calc_display]} { + set calc_display [string range $calc_display 0 {end-2}] + } + if {[regexp {\.0$} $calc_buffer]} { + set calc_buffer [string range $calc_buffer 0 {end-2}] + } + + # Covert content of all displays to new numeric base + switch -- $base { + {Hex} { ;# to Hexadecimal + enable_buttons {0 1 2 3 4 5 6 7 8 9 A B C D E F} + switch -- $last_base { + {Dec} {convert_displays NumSystem::dec2hex} + {Oct} {convert_displays NumSystem::oct2hex} + {Bin} {convert_displays NumSystem::bin2hex} + } + } + {Dec} { ;# to Decimal + disable_buttons {A B C D E F} + enable_buttons {0 1 2 3 4 5 6 7 8 9} + switch -- $last_base { + {Hex} {convert_displays NumSystem::hex2dec} + {Oct} {convert_displays NumSystem::oct2dec} + {Bin} {convert_displays NumSystem::bin2dec} + } + } + {Oct} { ;# to Octal + disable_buttons {8 9 A B C D E F} + enable_buttons {0 1 2 3 4 5 6 7} + switch -- $last_base { + {Hex} {convert_displays NumSystem::hex2oct} + {Dec} {convert_displays NumSystem::dec2oct} + {Bin} {convert_displays NumSystem::bin2oct} + } + } + {Bin} { ;# to Binary + disable_buttons {2 3 4 5 6 7 8 9 A B C D E F} + enable_buttons {0 1} + switch -- $last_base { + {Hex} {convert_displays NumSystem::hex2bin} + {Dec} {convert_displays NumSystem::dec2bin} + {Oct} {convert_displays NumSystem::oct2bin} + } + } + } + + # Display new values + rewrite_display + rewrite_buffer + + # set last value + set last_base $base + } + + ## Disable buttons specified in the given list + # example: disable_buttons {1 2} ;# disable .calc_1_0 and .calc_2_0 + # @return void + private method disable_buttons {buttons_list} { + foreach path $buttons_list { + $calc_num_keypad.calc_${path} \ + configure -state disabled + } + } + + ## Enable buttons specified in the given list + # example: enable_buttons {1 2} ;# enable .calc_1_0 and .calc_2_0 + # @return void + private method enable_buttons {buttons_list} { + foreach path $buttons_list { + $calc_num_keypad.calc_${path} \ + configure -state normal + } + } + + ## Switch angle unit + # @return void + public method cal_switchAngle {} { + + # Get chosen unit + set angle [subst "\$::Calculator::calc_angle$calc_idx"] + + # Convert all displays + if {$angle != $last_angle} { + # Convert display if is not empty + if {[read_display_inDec] != {}} { + write_display_inXbase [Angle::${last_angle}2${angle} $calc_display] + } + # Convert buffer if is not empty + if {[read_buffer_inDec] != {}} { + write_buffer_inXbase [Angle::${last_angle}2${angle} $calc_buffer] + } + # Conver memory cells + for {set i 0} {$i <3} {incr i} { + # Get memory cell value + set mem [[subst "\$mem_entry_$i"] get] + # Adjust that value + if {[string index $mem end] == {.}} { + append mem 0 + } + # Display new value + if {$mem != {}} { + set mem [Angle::${last_angle}2${angle} $mem] + [subst "\$mem_entry_$i"] delete 0 end + [subst "\$mem_entry_$i"] insert end $mem + } + } + } + + # Set last unit + set last_angle $angle + } + + ## Read content of main display in decimal system + # @return Float result + private method read_display_inDec {} { + # get display content + if {[reread_display] != {}} { + # convert to decimal value + if {$base != {Dec}} { + switch -- $base { + {Hex} { ;# from Hexadecimal + set calc_display [NumSystem::hex2dec $calc_display] + } + {Oct} { ;# from Octal + set calc_display [NumSystem::oct2dec $calc_display] + } + {Bin} { ;# from Binary + set calc_display [NumSystem::bin2dec $calc_display] + } + } + } + } + # done + return $calc_display + } + + ## Write the given number (in dec) to main display (in selected base) + # @parm Float dec_content - number to display + # @return void + private method write_display_inXbase {dec_content} { + + # If selected numeric base isn't Dec -> perform conversion + if {$base != {Dec}} { + switch -- $base { + {Hex} { ;# to Hexadecimal + if {[catch { + set calc_display [NumSystem::dec2hex $dec_content] + }]} { + Sbar [mc "Calculator: ERROR, result is too high (cannot be displayed)"] + set calc_display 0 + } + } + {Oct} { ;# to Octal + if {[catch { + set calc_display [NumSystem::dec2oct $dec_content] + }]} { + Sbar [mc "Calculator: ERROR, result is too high (cannot be displayed)"] + set calc_display 0 + } + } + {Bin} { ;# to Binary + if {[catch { + set calc_display [NumSystem::dec2bin $dec_content] + }]} { + Sbar [mc "Calculator: ERROR, result is too high (cannot be displayed)"] + set calc_display 0 + } + } + } + # If selected numeric base is Dec -> do nothing + } { + set calc_display $dec_content + } + + # display (new) value + rewrite_display + } + + ## Read content of buffer in decimal system + # @return Float result + private method read_buffer_inDec {} { + # Get content buffer display + if {[reread_buffer] != {}} { + # Convert to decimal value + if {$base != {Dec}} { + switch -- $base { + {Hex} { ;# from Hexadecimal + set calc_buffer [NumSystem::hex2dec $calc_buffer] + } + {Oct} { ;# from Octal + set calc_buffer [NumSystem::oct2dec $calc_buffer] + } + {Bin} { ;# from BInary + set calc_buffer [NumSystem::bin2dec $calc_buffer] + } + } + } + } + # done + return $calc_buffer + } + + ## Write the given number (in dec) to buffer display (in selected base) + # @parm Float dec_content - number to display + # @return void + private method write_buffer_inXbase {dec_content} { + + # If selected numeric base isn't Dec -> perform conversion + if {$base != {Dec}} { + switch -- $base { + {Hex} { ;# to Hexadecimal + if {[catch { + set calc_buffer [NumSystem::dec2hex $dec_content] + }]} { + Sbar [mc "Calculator: ERROR, value is too high"] + set calc_buffer 0 + } + } + {Oct} { ;# to Octal + if {[catch { + set calc_buffer [NumSystem::dec2oct $dec_content] + }]} { + Sbar [mc "Calculator: ERROR, value is too high"] + set calc_buffer 0 + } + } + {Bin} { ;# to Binary + if {[catch { + set calc_buffer [NumSystem::dec2bin $dec_content] + }]} { + Sbar [mc "Calculator: ERROR, value is too high"] + set calc_buffer 0 + } + } + } + # If selected numeric base is Dec -> do nothing + } { + set calc_buffer $dec_content + } + + # display (new) value + rewrite_buffer + } + + ## Write adjusted content of variable calc_display to main display widget + # @return void + private method rewrite_display {} { + # Adust content of source variable + if {[regexp {\.0$} $calc_display]} { + set calc_display [string range $calc_display 0 {end-2}] + } + # Show its content + set ::Calculator::calc_displ$calc_idx $calc_display + } + + ## Write adjusted content of variable calc_buffer to buffer display widget + # @return void + private method rewrite_buffer {} { + # Adust content of source variable + if {[regexp {\.0$} $calc_buffer]} { + set calc_buffer [string range $calc_buffer 0 {end-2}] + } + # Show its content + set ::Calculator::calc_buffer$calc_idx $calc_buffer + } + + ## Read true content of main display widget converted + # @parm args = False - adjust to float + # @return Float - content of the main display + private method reread_display args { + + # Get content of the widget + set calc_display [$calc_display_widget get] + regsub {\,} $calc_display {.} calc_display + + # Adhust to float (if requested) + if {$args != 1} { + if {[regexp {^\.} $calc_display]} { + set calc_display "0$calc_display" + } elseif {[regexp {\.$} $calc_display]} { + append calc_display 0 + } + if {[string first {.} $calc_display] == -1} { + append calc_display {.0} + } + } + + # Remove trailing '.0' + if {[regexp {^\.0$} $calc_display]} { + set calc_display {} + } + + # Return result + return $calc_display + } + + ## Read true content of buffer display widget converted + # @return Float - content of the buffer + private method reread_buffer {} { + + # Get content of the widget + set calc_buffer [$calc_buffer_widget get] + regsub {\,} $calc_buffer {.} calc_buffer + + # Adhust to float + if {[regexp {^\.} $calc_buffer]} { + set calc_buffer "0$calc_buffer" + } elseif {[regexp {\.$} $calc_buffer]} { + append calc_buffer 0 + } + if {[string first {.} $calc_buffer] == -1} { + append calc_buffer {.0} + } + + # Remove trailing '.0' + if {[regexp {^\.0$} $calc_buffer]} { + set calc_buffer {} + } + + # Return result + return $calc_buffer + } + + ## Covert given angle to current angle unit + # @parm Float dec_angle - angle to convert in decimal + # @return Float - angle in radians + private method Xangle_to_rad {dec_angle} { + # If current angle unit isn't radians -> perform converison + if {$angle != {rad}} { + # From grad + if {$angle == {grad}} { + set dec_angle [Angle::grad2rad $dec_angle] + # From degrees + } { + set dec_angle [Angle::deg2rad $dec_angle] + } + } + # return result + return $dec_angle + } + + ## Convert given angle in radians to current angle unit + # @parm Float dec_angle - angle to conver in radians (decimal) + # @return Float - converted angle + private method rad_to_Xangle {dec_angle} { + # If current angle unit isn't radians -> perform converison + if {$angle != {rad}} { + # To grad + if {$angle == {grad}} { + set dec_angle [Angle::rad2grad $dec_angle] + # To degrees + } { + set dec_angle [Angle::rad2deg $dec_angle] + } + } + # return result + return $dec_angle + } + + ## Validate display content + # @parm Widget widget - entry widget + # @parm String content - content to validate + # @return bool - result + public method calc_validate {widget content} { + + # Set default background color for that widget + if {$widget == $calc_display_widget} { + $widget configure -style Calculator_Display.TEntry + } elseif {$widget == $calc_buffer_widget} { + $widget configure -style Calculator_Buffer.TEntry + } else { + $widget configure -style TEntry + } + + # Valid if content is empty + set len [string length $content] + if {$len == 0 || $content == {-}} { + return 1 + } + + # Invalid if content is too wide + if {$len > 40} { + Sbar [mc "Calculator: ERROR, value is too high"] + if {[string length [$widget get]] > 13} { + $widget configure -style Calculator_Error.TEntry + } + return 0 + } + + # Adjust content + regsub {\,} $content {.} content + if {[regexp {\.$} $content]} { + append content 0 + } + + # Check for valid numeric base + switch -- $base { + {Hex} {set content [NumSystem::ishex $content]} + {Dec} {set content [NumSystem::isdec $content]} + {Oct} {set content [NumSystem::isoct $content]} + {Bin} {set content [NumSystem::isbin $content]} + default {set content 0} + } + + # Evaluate filan result + if {$content} { + if {$len > 13} { + $widget configure -style Calculator_Error.TEntry + } + return 1 + } { + Sbar [mc "Calculator: Trying to insert invalid value"] + return 0 + } + } + + ## Validate content of operator diaplay + # @parm String content - string to validate + # @return Bool - result of validation + public method calc_oper_validate {content} { + + # Check for length + if {[string length $content] > 4} { + return 0 + } + + # Check for allowed content + switch -- $content { + {/} {set calc_oper {div}} + {*} {set calc_oper {mul}} + {-} {set calc_oper {min}} + {+} {set calc_oper {add}} + {**} {set calc_oper {pow}} + {mod} {set calc_oper {mod}} + {&} {set calc_oper {and}} + {|} {set calc_oper {or}} + {^} {set calc_oper {xor}} + {>>} {set calc_oper {right}} + {~} {set calc_oper {not}} + {e**} {set calc_oper {Exp}} + {sqrt} {set calc_oper {Sqr}} + {lg} {set calc_oper {Log}} + {ln} {set calc_oper {Ln}} + {sin} {set calc_oper {Sin}} + {cos} {set calc_oper {Cos}} + {tan} {set calc_oper {Tan}} + {asin} {set calc_oper {ASin}} + {acos} {set calc_oper {ACos}} + {atan} {set calc_oper {ATan}} + default { + # Set foteground color to #FF0000 if content is invalid + set calc_oper {} + $calc_oper_widget configure -style Calculator_OperError.TEntry + return 1 + } + } + + # Set foreground color to default and return result (True) + $calc_oper_widget configure -style Calculator_Oper.TEntry + return 1 + } + + ## Negate content of the main display + # @return void + public method calc_NegateDis {} { + # Empty display -> abort + if {[reread_display] == {}} { + return + + # Negate value + } { + if {[regexp {^\-} $calc_display]} { + set calc_display [string range $calc_display 1 end] + } { + set calc_display "-$calc_display" + } + } + + # Write result + rewrite_display + } + + ## Prepare object for creating its GUI + # @parm Widget _parent - parent widget (some frame) + # @parm List _calculatorList - List of initial values (displays,, memory, radix, angle unit) + # @return void + public method PrepareCalculator {_parent _calculatorList} { + set parent $_parent + set calculatorList $_calculatorList + set gui_initialized 0 + } + + ## Inform this tab than it has became active + # @return void + public method CalculatorTabRaised {} { + $calc_display_widget selection range 0 end + $calc_display_widget icursor end + focus $calc_display_widget + } + + ## Initialize calculator GUI + # @return void + public method CreateCalculatorGUI {} { + if {$gui_initialized} {return} + set gui_initialized 1 + + # Create scrollable area + set scrollable_frame [ScrollableFrame $parent.scrollable_frame \ + -xscrollcommand "$this calc_gui_scroll_set" \ + ] + set horizontal_scrollbar [ttk::scrollbar $parent.horizontal_scrollbar \ + -orient horizontal -command "$scrollable_frame xview" \ + ] + pack $scrollable_frame -fill both -side bottom -expand 1 + set parent [$scrollable_frame getframe] + + # LEFT HALF + + # create numeric keypad + set calc_num_keypad [frame $parent.calc_num_keypad] + makeKeypad $calc_num_keypad $calculator_keyboard + + + # RIGHT HALF + + # create display + set calc_num_display [frame $parent.calc_num_display] + set frame0 [frame $calc_num_display.calc_num_display0] + set frame1 [frame $calc_num_display.calc_num_display1] + + # Buffer display + set calc_buffer_widget [ttk::entry $frame0.calc_buffer \ + -textvariable ::Calculator::calc_buffer$calc_idx \ + -validate key \ + -validatecommand "$this calc_validate %W %P" \ + -width 13 \ + -style Calculator_Buffer.TEntry \ + ] + DynamicHelp::add $frame0.calc_buffer -text [mc "Buffer display"] + setStatusTip -widget $calc_buffer_widget \ + -text [mc "Calculator buffer"] + # Operator display + set calc_oper_widget [ttk::entry $frame0.calc_oper \ + -textvariable ::Calculator::calc_oper$calc_idx \ + -validate all \ + -width 3 \ + -validatecommand "$this calc_oper_validate %P" \ + -style Calculator_Oper.TEntry \ + ] + DynamicHelp::add $frame0.calc_oper -text [mc "Selected operation"] + setStatusTip -widget $calc_oper_widget \ + -text [mc "Selected operation"] + # Main display + set calc_display_widget [ttk::entry $frame0.calc_displ \ + -textvariable ::Calculator::calc_displ$calc_idx \ + -validate key \ + -validatecommand "$this calc_validate %W %P" \ + -width 13 \ + -style Calculator_Display.TEntry \ + ] + DynamicHelp::add $frame0.calc_displ -text [mc "Main display"] + setStatusTip -widget $calc_display_widget \ + -text [mc "Main display"] + # Pack displays + pack $calc_buffer_widget -side left + pack $calc_oper_widget -side left + pack $calc_display_widget -side left + # Create binding for displays + bind $calc_buffer_widget <KP_Enter> "$this calc_Evaluate" + bind $calc_oper_widget <KP_Enter> "$this calc_Evaluate" + bind $calc_display_widget <KP_Enter> "$this calc_Evaluate" + bind $calc_buffer_widget <Return> "$this calc_Evaluate" + bind $calc_oper_widget <Return> "$this calc_Evaluate" + bind $calc_display_widget <Return> "$this calc_Evaluate" + + + ## Create: numeric base and angle unit switch + CA + C + frame $frame1.lf + # Numeric base switch + pack [ttk::combobox $frame1.lf.calc_base_CB \ + -state readonly \ + -values {Hex Dec Oct Bin} \ + -textvariable ::Calculator::calc_base$calc_idx \ + -width 4 \ + ] -side left + bind $frame1.lf.calc_base_CB <<ComboboxSelected>> "$this cal_switchBase" + DynamicHelp::add $frame1.lf.calc_base_CB -text [mc "Numeric base"] + setStatusTip -widget $frame1.lf.calc_base_CB \ + -text [mc "Numeric base"] + # Angle unit switch + pack [ttk::combobox $frame1.lf.calc_angle_CB \ + -state readonly \ + -values {rad deg grad} \ + -textvariable ::Calculator::calc_angle$calc_idx \ + -width 4 \ + ] -side left + bind $frame1.lf.calc_angle_CB <<ComboboxSelected>> "$this cal_switchAngle" + DynamicHelp::add $frame1.lf.calc_angle_CB -text [mc "Angle unit"] + setStatusTip -widget $frame1.lf.calc_angle_CB \ + -text [mc "Angle unit"] + pack $frame1.lf -side left -padx 5 + + frame $frame1.rf + # Button "Clear" + pack [ttk::button $frame1.rf.calc_Clear \ + -text {C} \ + -command "$this calc_Clear" \ + -width 3 \ + ] -side left + DynamicHelp::add $frame1.rf.calc_Clear \ + -text [mc "Clear both displays"] + setStatusTip -widget $frame1.rf.calc_Clear \ + -text [mc "Clear both displays"] + # Button "Clear actual" + pack [ttk::button $frame1.rf.calc_Clear_act \ + -text {CA} \ + -command "$this calc_ClearActual" \ + -width 3 \ + ] -side left + DynamicHelp::add $frame1.rf.calc_Clear_act \ + -text [mc "Clear main display"] + setStatusTip -widget $frame1.rf.calc_Clear_act \ + -text [mc "Clear main display"] + # Button "Negate" + pack [ttk::button $frame1.rf.calc_Negate_dis \ + -text {+/-} \ + -command "$this calc_NegateDis" \ + -width 3 \ + ] -side left + DynamicHelp::add $frame1.rf.calc_Negate_dis \ + -text [mc "Negate value in main display"] + setStatusTip -widget $frame1.rf.calc_Negate_dis \ + -text [mc "Negate value in main display"] + pack $frame1.rf -side right -padx 5 + + # Create calculator memory cells + for {set i 0} {$i < 3} {incr i} { + # Determinate ID of target frame + set frame_id [frame $calc_num_display.calc_num_display[expr $i + 2]] + # Label "Mx:" + pack [Label $frame_id.calc_mem_label_${i} \ + -text "M$i: " -helptext [mc "Memory bank %s" $i]\ + ] -side left + setStatusTip -widget $frame_id.calc_mem_label_${i} \ + -text [mc "Memory bank %s" $i] + # Entry widget + set entry [ttk::entry $frame_id.calc_mem_entry_${i} \ + -textvariable ::Calculator::calc_mem${i}_${calc_idx} \ + -validate all \ + -validatecommand "$this calc_validate %W %P" \ + ] + DynamicHelp::add $frame_id.calc_mem_entry_${i} -text [mc "Memory bank %s" $i] + pack $entry -side left + set mem_entry_$i $entry + setStatusTip -widget $entry -text [mc "Memory bank %s" $i] + # Button "Save" + pack [ttk::button $frame_id.calc_mem_save_button_${i} \ + -text [mc "Save"] \ + -command "$this mem Save $i" \ + -width 5 \ + ] -side left + DynamicHelp::add $frame_id.calc_mem_save_button_${i} \ + -text [mc "Save content of main display to this memory bank %s" $i] + setStatusTip -widget $frame_id.calc_mem_save_button_${i} \ + -text [mc "Save content of main display to this memory bank %s" $i] + # Button "Load" + pack [ttk::button $frame_id.calc_mem_load_button_${i} \ + -text [mc "Load"] \ + -command "$this mem Load $i" \ + -width 5 \ + ] -side left + DynamicHelp::add $frame_id.calc_mem_load_button_${i} \ + -text [mc "Load content of this bank into main display"] + setStatusTip -widget $frame_id.calc_mem_load_button_${i} \ + -text [mc "Load content of memory bank %s into calculator main display" $i] + } + + bind $mem_entry_0 <Up> "focus $mem_entry_2" + bind $mem_entry_0 <Down> "focus $mem_entry_1" + + bind $mem_entry_1 <Up> "focus $mem_entry_0" + bind $mem_entry_1 <Down> "focus $mem_entry_2" + + bind $mem_entry_2 <Up> "focus $mem_entry_1" + bind $mem_entry_2 <Down> "focus $mem_entry_0" + + + # TIMERS CALC + + set calc_timers_calc [ttk::labelframe $parent.calc_timers_calc -text [mc "Timers preset"]] + makeTimersCalc $calc_timers_calc + + + # INNER INITIALIZATION + + # pack "left side" of calculator + pack $calc_num_keypad -side left + + # pack "right side" of calculator + for {set i 0} {$i < 5} {incr i} { + if {$i == 1} { + pack $calc_num_display.calc_num_display${i} -pady 10 + } { + pack $calc_num_display.calc_num_display${i} + } + } + pack $calc_num_display -side left -padx 10 + + # pack timres calc + pack $calc_timers_calc -side left -expand 0 -anchor nw + + ## save data given by $calculatorList + # "$base $angle $calc_display $calc_oper $calc_buffer $calc_mem0 $calc_mem1 $calc_mem2" + set base [lindex $calculatorList 0] + set angle [lindex $calculatorList 1] + if { + $base != {Hex} && $base != {Dec} && + $base != {Oct} && $base != {Bin} + } { + set base [lindex ${X::project_edit_defaults} {3 1}] + puts stderr [mc "Invalid numerical base: '%s'" $base] + } + if {$angle != {rad} && $angle != {deg} && $angle != {grad}} { + puts stderr [mc "Invalid angle unit: '%s'" $angle] + set angle [lindex ${X::project_edit_defaults} {4 1}] + } + set ::Calculator::calc_base$calc_idx $base + set ::Calculator::calc_angle$calc_idx $angle + + set last_base $base + set last_angle $angle + + # Enable/Disable buttons on numeric keypad + switch -- $base { + {Hex} { + enable_buttons {0 1 2 3 4 5 6 7 8 9 A B C D E F} + disable_buttons {U RE}} + {Dec} { + enable_buttons {0 1 2 3 4 5 6 7 8 9} + disable_buttons {A B C D E F U RE}} + {Oct} { + enable_buttons {0 1 2 3 4 5 6 7} + disable_buttons {8 9 A B C D E F U RE}} + {Bin} { + enable_buttons {0 1} + disable_buttons {2 3 4 5 6 7 8 9 A B C D E F U RE}} + } + + # Fill displays + set calc_display [lindex $calculatorList 2] + rewrite_display + calc_opr [lindex $calculatorList 3] 0 + set calc_buffer [lindex $calculatorList 4] + rewrite_buffer + set ::Calculator::calc_mem0_$calc_idx [lindex $calculatorList 5] + set ::Calculator::calc_mem1_$calc_idx [lindex $calculatorList 6] + set ::Calculator::calc_mem2_$calc_idx [lindex $calculatorList 7] + + # Set frequenci and mode in timers calculator + set freq [lindex $calculatorList 8] + set mode [lindex $calculatorList 10] + if {$freq == {} || [regexp {^\d\+$} $freq] || $freq < 0 || $freq > 99999} { + set freq 12000 + } + if {$mode != 0 && $mode != 1 && $mode != 2} { + set mode 0 + } + $timerscalc_freq_entry insert 0 $freq + $timerscalc_time_entry insert 0 [lindex $calculatorList 9] + $timerscalc_mode_spinbox delete 0 end + $timerscalc_mode_spinbox insert 0 $mode + + # Unset teportary variables + unset parent + unset calculatorList + } + + ## Get calculator list for later initialization + # @return List - resulting list of values + public method get_calculator_list {} { + if {!$gui_initialized} {CreateCalculatorGUI} + return [list $base $angle \ + [$calc_display_widget get] \ + $calc_oper \ + [$calc_buffer_widget get] \ + [subst "\$::Calculator::calc_mem0_$calc_idx"] \ + [subst "\$::Calculator::calc_mem1_$calc_idx"] \ + [subst "\$::Calculator::calc_mem2_$calc_idx"] \ + [$timerscalc_freq_entry get] \ + [$timerscalc_time_entry get] \ + [$timerscalc_mode_spinbox get]] + } + + ## Validate and evaluate content of Frequency entry (timers calculator) + # @parm String content - String to validate (and evaluate) + # @return Bool - result of validation + public method calc_timerscalc_freq_validate {content} { + # If validation disabled -> abort + if {$timerscalc_validation_dis} { + return 1 + } + # If content is decimal number (max 5. digits) -> evaluate and return True + if {[regexp {^\d*$} $content] && ([string length $content] < 6)} { + calc_timerscalc_evaluate \ + $content \ + [$timerscalc_time_entry get] \ + [$timerscalc_mode_spinbox get] \ + + return 1 + } + # Otherwise -> return False + Sbar [mc "Calculator - timers preset: you are trying to insert an invalid value"] + return 0 + } + + ## Validate and evaluate content of Mode entry (timers calculator) + # @parm String content - String to validate (and evaluate) + # @return Bool - result of validation + public method calc_timerscalc_mode_validate {content} { + # If validation disabled -> abort + if {$timerscalc_validation_dis} { + return 1 + } + # If the given value is one of {0 1 2} the evaluate and return True + if {[regexp {^\d?$} $content]} { + if {$content > 2} { + return 0 + } + calc_timerscalc_evaluate \ + [$timerscalc_freq_entry get] \ + [$timerscalc_time_entry get] \ + $content + return 1 + } + # Otherwise -> return False + Sbar [mc "Calculator - timers preset: you are trying to insert an invalid value"] + return 0 + } + + ## Validate and evaluate content of Time entry (timers calculator) + # @parm String content - String to validate (and evaluate) + # @return Bool - result of validation + public method calc_timerscalc_time_validate {content} { + # If validation disabled -> abort + if {$timerscalc_validation_dis} { + return 1 + } + # If content is decimal number (max 9. digits) -> evaluate and return True + if {[regexp {^\d*$} $content] && ([string length $content] < 10)} { + calc_timerscalc_evaluate \ + [$timerscalc_freq_entry get] \ + $content \ + [$timerscalc_mode_spinbox get] + return 1 + } + # Otherwise -> return False + Sbar [mc "Calculator - timers preset: you are trying to insert an invalid value"] + return 0 + } + + ## Highlight result of timer preset calculator + # @parm Bool valid - highlight for valid results + # @return void + private method calc_timerscalc_highlight {valid} { + + # List of widgets to highlight + set widgets " + $timerscalc_THxDec_label + $timerscalc_THxHex_label + $timerscalc_THxOct_label + $timerscalc_TLxDec_label + $timerscalc_TLxHex_label + $timerscalc_TLxOct_label + $timerscalc_RepeatDec_label + $timerscalc_RepeatHex_label + $timerscalc_RepeatOct_label + $timerscalc_CorrectionDec_label + $timerscalc_CorrectionHex_label + $timerscalc_CorrectionOct_label + " + + # Perform highlighting + if {$valid} { + foreach widget $widgets { + $widget configure -state normal + } + } { + foreach widget $widgets { + $widget configure -state disabled + } + } + } + + ## Evaluate tmers preset (timers preset calculator) + # @parm Int freq - Frequency + # @parm Int time - Time in miliseconds + # @parm Int mode - Mode {0 1 2} + # @return Bool - Resulting status + private method calc_timerscalc_evaluate {freq time mode} { + + # Set default results + set TLx 0 + set THx 0 + set repeat 0 + set correction 0 + + # Check for validity of given values + if {$freq == {} || $freq == 0 || $time == {} || $mode == {} } { + set mode {invalid} + } { + # Compute time in machine cycles + set time [expr {int($time * (12000.0 / $freq))}] + } + + # Perform computation for the given mode + switch -- $mode { + 0 { + # Determinate apparent number of repeats + set repeat [expr {($time >> 13) + 1}] + # Compute tempotary results + if {[expr {!($time & 0x1FFF)}]} { + incr repeat -1 + set stepsPerIter 0x1FFF + } { + set stepsPerIter [expr {$time / $repeat}] + set tmp [expr {0x2000 - $stepsPerIter}] + set TLx [expr {$tmp & 0x1F}] + set THx [expr {$tmp >> 5}] + set correction [expr {$time - ((0x1FFF - $tmp) * $repeat)}] + } + } + 1 { + # Determinate apparent number of repeats + set repeat [expr {($time >> 16) + 1}] + # Compute tempotary results + if {[expr {!($time & 0xFFFF)}]} { + incr repeat -1 + set stepsPerIter 0xFFFF + } { + set stepsPerIter [expr {$time / $repeat}] + set tmp [expr {0x10000 - $stepsPerIter}] + set TLx [expr {$tmp & 0xFF}] + set THx [expr {$tmp >> 8}] + set correction [expr {$time - ((0x10000 - $tmp) * $repeat)}] + } + } + 2 { + # Determinate apparent number of repeats + set repeat [expr {($time >> 8) + 1}] + # Compute tempotary results + if {[expr {!($time & 0xFF)}]} { + incr repeat -1 + set stepsPerIter 0xFF + } { + set stepsPerIter [expr {$time / $repeat}] + set TLx [expr {0x100 - $stepsPerIter}] + set THx $TLx + set correction [expr {$time - ((0xFF - $THx) * $repeat)}] + } + } + {invalid} { ;# Invalid input data + calc_timerscalc_highlight 0 + } + default { ;# Something went wrong + error "Calculator error: Invalid timer mode $mode" + return 0 + } + } + + # If pre-computation was performed succesfully -- finish the results + if {$mode != {invalid}} { + # Highlight results as valid + calc_timerscalc_highlight 1 + + # Perform correction + if {$correction >= $stepsPerIter} { + incr repeat [expr {$correction / $stepsPerIter}] + set correction [expr {$correction % $stepsPerIter}] + } + } + + # Check for allowed length of results (string representation) + if { + [string length [format "%o" $repeat]] > 6 + || + [string length [format "%o" $correction]] > 6 + } { + set TLx 0 + set THx 0 + set repeat 0 + set correction 0 + calc_timerscalc_highlight 0 + Sbar [mc "Calculator: Unable to evaluate, result value is too high"] + } + + ## Write results + # THx values + $timerscalc_THxDec_label configure -text $THx + $timerscalc_THxHex_label configure -text [format "%X" $THx] + $timerscalc_THxOct_label configure -text [format "%o" $THx] + # TLx values + $timerscalc_TLxDec_label configure -text $TLx + $timerscalc_TLxHex_label configure -text [format "%X" $TLx] + $timerscalc_TLxOct_label configure -text [format "%o" $TLx] + # Repeat values + $timerscalc_RepeatDec_label configure -text $repeat + $timerscalc_RepeatHex_label configure -text [format "%X" $repeat] + $timerscalc_RepeatOct_label configure -text [format "%o" $repeat] + # Correction values + $timerscalc_CorrectionDec_label configure -text $correction + $timerscalc_CorrectionHex_label configure -text [format "%X" $correction] + $timerscalc_CorrectionOct_label configure -text [format "%o" $correction] + + return 1 + } + + ## Create widgets of timers preset calculator + # @parm widget parent - parent contaner (some frame) + # @return void + private method makeTimersCalc {parent} { + # TOP HALF + set top_frame [frame $parent.calc_timerscalc_top_frame] + # frequency + grid [label $top_frame.calc_timerscalc_freq_label \ + -text [mc "Frequency \[kHz\]"] \ + ] -row 0 -column 0 -sticky w + set timerscalc_freq_entry [ttk::entry \ + $top_frame.calc_timerscalc_freq_entry \ + -width 5 \ + -validate all \ + -validatecommand "$this calc_timerscalc_freq_validate %P" \ + ] + grid $timerscalc_freq_entry -row 0 -column 1 -sticky we + # mode + grid [label $top_frame.calc_timerscalc_mode_label \ + -text [mc "Mode"] \ + ] -row 0 -column 2 -sticky w + set timerscalc_mode_spinbox [spinbox \ + $top_frame.calc_timerscalc_mode_spinbox \ + -bg {#FFFFFF} -highlightthickness 0 \ + -from 0 -to 2 -width 1 -validate key \ + -vcmd "$this calc_timerscalc_mode_validate %P" \ + ] + grid $timerscalc_mode_spinbox -row 0 -column 3 -sticky we + # time + grid [label $top_frame.calc_timerscalc_time_label \ + -text [mc "Time \[us\]"] \ + ] -row 1 -column 0 -sticky w + set timerscalc_time_entry [ttk::entry \ + $top_frame.calc_timerscalc_time_entry \ + -width 8 \ + -validate all \ + -validatecommand "$this calc_timerscalc_time_validate %P" \ + ] + grid $timerscalc_time_entry -row 1 -column 1 -sticky we -columnspan 3 + + # BOTTOM HALF + set bottom_frame [frame $parent.calc_timerscalc_bottom_frame] + + # "dec" "hex" "oct" + grid [label $bottom_frame.calc_timerscalc_dec_label \ + -text [mc "DEC"] -font $::smallfont -anchor e \ + -highlightthickness 0 \ + ] -row 0 -column 1 -ipadx 12 + grid [label $bottom_frame.calc_timerscalc_hex_label \ + -text [mc "HEX"] -font $::smallfont -anchor e \ + -highlightthickness 0 \ + ] -row 0 -column 2 -ipadx 12 + grid [label $bottom_frame.calc_timerscalc_oct_label \ + -text [mc "OCT"] -font $::smallfont -anchor e \ + -highlightthickness 0 \ + ] -row 0 -column 3 -ipadx 12 + + # "THx" "TLx" "Repeat" "Correction" + grid [label $bottom_frame.calc_timerscalc_thx_label \ + -text "THx" \ + ] -row 1 -column 0 -sticky w + grid [label $bottom_frame.calc_timerscalc_tlx_label \ + -text "TLx" \ + ] -row 2 -column 0 -sticky w + grid [label $bottom_frame.calc_timerscalc_repeat_label \ + -text [mc "Repeats"] \ + ] -row 3 -column 0 -sticky w + grid [label $bottom_frame.calc_timerscalc_correction_label \ + -text [mc "Correction"] \ + ] -row 4 -column 0 -sticky w + + # THx values + set timerscalc_THxDec_label [label \ + $bottom_frame.calc_timerscalc_THxDec_label \ + -text "0" -disabledforeground {#AAAAAA} -fg {#000033} \ + ] + set timerscalc_THxHex_label [label \ + $bottom_frame.calc_timerscalc_THxHex_label \ + -text "0" -disabledforeground {#AAAAAA} -fg {#000033} \ + ] + set timerscalc_THxOct_label [label \ + $bottom_frame.calc_timerscalc_THxOct_label \ + -text "0" -disabledforeground {#AAAAAA} -fg {#000033} \ + ] + + # TLx values + set timerscalc_TLxDec_label [label \ + $bottom_frame.calc_timerscalc_TLxDec_label \ + -text "0" -disabledforeground {#AAAAAA} -fg {#000033} \ + ] + set timerscalc_TLxHex_label [label \ + $bottom_frame.calc_timerscalc_TLxHex_label \ + -text "0" -disabledforeground {#AAAAAA} -fg {#000033} \ + ] + set timerscalc_TLxOct_label [label \ + $bottom_frame.calc_timerscalc_TLxOct_label \ + -text "0" -disabledforeground {#AAAAAA} -fg {#000033} \ + ] + + # Repeat values + set timerscalc_RepeatDec_label [label \ + $bottom_frame.calc_timerscalc_RepeatDec_label \ + -text "0" -disabledforeground {#AAAAAA} -fg {#000033} \ + ] + set timerscalc_RepeatHex_label [label \ + $bottom_frame.calc_timerscalc_RepeatHex_label \ + -text "0" -disabledforeground {#AAAAAA} -fg {#000033} \ + ] + set timerscalc_RepeatOct_label [label \ + $bottom_frame.calc_timerscalc_RepeatOct_label \ + -text "0" -disabledforeground {#AAAAAA} -fg {#000033} \ + ] + + # Correction values + set timerscalc_CorrectionDec_label [label \ + $bottom_frame.calc_timerscalc_CorrectionDec_label \ + -text "0" -disabledforeground {#AAAAAA} -fg {#000033} \ + ] + set timerscalc_CorrectionHex_label [label \ + $bottom_frame.calc_timerscalc_CorrectionHex_label \ + -text "0" -disabledforeground {#AAAAAA} -fg {#000033} \ + ] + set timerscalc_CorrectionOct_label [label \ + $bottom_frame.calc_timerscalc_CorrectionOct_label \ + -text "0" -disabledforeground {#AAAAAA} -fg {#000033} \ + ] + + # Show widgets + grid $timerscalc_THxDec_label -row 1 -column 1 -sticky e + grid $timerscalc_THxHex_label -row 1 -column 2 -sticky e + grid $timerscalc_THxOct_label -row 1 -column 3 -sticky e + grid $timerscalc_TLxDec_label -row 2 -column 1 -sticky e + grid $timerscalc_TLxHex_label -row 2 -column 2 -sticky e + grid $timerscalc_TLxOct_label -row 2 -column 3 -sticky e + grid $timerscalc_RepeatDec_label -row 3 -column 1 -sticky e + grid $timerscalc_RepeatHex_label -row 3 -column 2 -sticky e + grid $timerscalc_RepeatOct_label -row 3 -column 3 -sticky e + grid $timerscalc_CorrectionDec_label -row 4 -column 1 -sticky e + grid $timerscalc_CorrectionHex_label -row 4 -column 2 -sticky e + grid $timerscalc_CorrectionOct_label -row 4 -column 3 -sticky e + + # Make widgets in table as small as possible + foreach widget " + $bottom_frame.calc_timerscalc_dec_label + $bottom_frame.calc_timerscalc_hex_label + $bottom_frame.calc_timerscalc_oct_label + $bottom_frame.calc_timerscalc_thx_label + $bottom_frame.calc_timerscalc_tlx_label + $bottom_frame.calc_timerscalc_repeat_label + $bottom_frame.calc_timerscalc_correction_label + $timerscalc_THxDec_label + $timerscalc_THxHex_label + $timerscalc_THxOct_label + $timerscalc_TLxDec_label + $timerscalc_TLxHex_label + $timerscalc_TLxOct_label + $timerscalc_RepeatDec_label + $timerscalc_RepeatHex_label + $timerscalc_RepeatOct_label + $timerscalc_CorrectionDec_label + $timerscalc_CorrectionHex_label + $timerscalc_CorrectionOct_label + " { + $widget configure -bd 0 -relief raised -pady 0 -highlightthickness 0 + } + + # Pack frames + pack $top_frame -padx 5 -pady 2 + pack $bottom_frame -padx 5 -pady 2 + + # Highlight calculator results as invalid + calc_timerscalc_highlight 0 + set timerscalc_validation_dis 0 + } + + ## Create calculator keypad + # @parm widget parent - target contaner (some frame) + # @parm List definition - keypad definition (see class header) + # @return void + private method makeKeypad {parent definition} { + # Local variables + set row 0 ;# Current row in the grid + + # Oterate over row definitions in the given keypad definition + foreach line $definition { + # Local variables + set col 0 ;# current column in the grid + + # Iterate over button definitions in the row + foreach item $line { + if {$item == "separator"} {continue} + + # Inicalize array of button features + for {set i 0} {$i < 13} {incr i} { + set parm($i) [lindex $item $i] + } + + if {[lsearch -ascii -exact {A B C D E} $parm(0)] != -1} { + incr col + } + + # Initialize default values for some items + foreach i {3 4 7} { + if {$parm($i) == {}} {set parm($i) 1} + } + if {$parm(6) == {}} {set parm(6) 2} + if {$parm(8) == {}} {set parm(8) 0} + if {$parm(9) == {}} {set parm(9) {#FFFFFF}} + if {$parm(10) == {}} {set parm(10) {#FFFFFF}} + + if {[string index $parm(9) 0] == {#}} { + set parm(9) {Calculator} + } + + # Set button ID + set path "$parent.calc_$parm(1)" + # Create button + ttk::button $path \ + -text $parm(0) \ + -command "$this $parm(2)" \ + -width $parm(6) \ + -style $parm(9).TButton +# -activebackground $parm(10) \ +# -height $parm(7) \ + DynamicHelp::add $path -text [mc $parm(5)] + # Confugure button +# if {$parm(11) == 1} {$path configure -font $large_font -pady 2} + if {$parm(12) != {}} { + setStatusTip -widget $path -text [mc $parm(12)] + } + + if {$parm(3) > 1} { + set sticky {we} + } elseif {$parm(4) > 1} { + set sticky {ns} + } else { + set sticky {} + } + + # Show button + grid $path \ + -columnspan $parm(3) \ + -rowspan $parm(4) \ + -sticky $sticky \ + -padx 2 \ + -pady 2 \ + -column $col \ + -row $row + + # Incremet number of current column + incr col $parm(3) + } + # Incremet number of current row + incr row + } + + grid columnconfigure $parent 4 -minsize 10 + } + + ## Adjust scrollbar for scrollable area + # @parm Float frac0 - 1st fraction + # @parm Float frac0 - 2nd fraction + # @return void + public method calc_gui_scroll_set {frac0 frac1} { + # Hide scrollbar + if {$frac0 == 0 && $frac1 == 1} { + if {[winfo ismapped $horizontal_scrollbar]} { + pack forget $horizontal_scrollbar + update + } + # Show scrollbar + } { + if {![winfo ismapped $horizontal_scrollbar]} { + pack $horizontal_scrollbar -fill x -side top -before $scrollable_frame + } + $horizontal_scrollbar set $frac0 $frac1 + update + } + } +} |