1#!/usr/local/bin/bash
2
3# ncid - Network Caller-ID client
4
5# Copyright (c) 2001-2020
6#  John L. Chmielewski <jlc@users.sourceforge.net>
7#  Steve Limkemann
8#  Todd Andrews <tandrews@users.sourceforge.net>
9
10# This program is free software: you can redistribute it and/or modify
11# it under the terms of the GNU General Public License as published by
12# the Free Software Foundation, either version 3 of the License, or
13# (at your option) any later version.
14
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18# GNU General Public License for more details.
19
20# You should have received a copy of the GNU General Public License
21# along with this program.  If not, see <http://www.gnu.org/licenses/>.
22
23# Lines beginning with # and ending in backslash are a trick to allow sh
24# to execute the lines and tclsh/wish to ignore them.
25
26# START OF LOCAL MODIFICATION SECTION
27# set PATH to include /usr/local/bin \
28  PATH=$PATH:/usr/local/bin; export PATH
29# END OF LOCAL MODIFICATION SECTION
30
31# eliminate duplicate PATHS https://unix.stackexchange.com/questions/40749/remove-duplicate-path-entries-with-awk-command \
32  PATH=$(printf %s "$PATH" | awk -v RS=: '!a[$0]++' | paste -s -d: -)
33
34# determine full path to tclsh and wish binaries
35# set TCLSH variable, FreeBSD will call it something like tclsh8.4 \
36  TCLSH=`type tclsh | sed 's,.* \/,/,'`
37# set WISH variable, FreeBSD will call it something like wish8.4 \
38  WISH=`type wish | sed 's,.* \/,/,'`
39
40# configuration file \
41  CF=/etc/ncid/ncid.conf
42
43# check for config file and set GUI if not found \
44  [ -f $CF ] || GUI=1
45
46# if $GUI not set, set GUI based on configuration file \
47  [ "$GUI" == "" ] && GUI=`awk 'BEGIN {GUI = 1} /^ *#/ {next} \
48      /^ *set  *NoGUI/ {if ($3 == 0) {GUI = 0}} \
49      END {print GUI}' $CF`
50
51# if GUI == 1, look for the --no-gui option, if found set GUI="0" \
52  [ "$GUI" == "1" ] && for i in $*; do if [ "$i" = "--no-gui" ]; then  GUI="0"; fi; done
53
54# if DISPLAY is not in the environment, set GUI="0" \
55  [ -z "$DISPLAY" ] && GUI="0"
56
57# if wish found and $GUI == 1, look for wish and exec it \
58  [ -n "$WISH" ] && [ "$GUI"  == "1" ] && exec $WISH -f "$0" -- "$@"
59
60# if tclsh found and $GUI == 0, look for tclsh and exec it \
61  [ -n "$TCLSH" ] && [ "$GUI" == "0" ] && exec $TCLSH "$0" "$@"
62
63# if tclsh found and wish not found, exec tclsh with the --no-gui option \
64  [ -n "$TCLSH" ] && exec $TCLSH "$0" --no-gui "$@"
65
66# tcl or tk not found \
67  echo "wish or tclsh not found in your \$PATH"; exit -1
68
69set DefaultHost         127.0.0.1
70set DefaultPort         3333
71set ConfigFile         /usr/local/etc/ncid/ncid.conf
72set ImageDir            /usr/share/ncid/images
73set Logo                $ImageDir/ncid.gif
74set ThemeDir            /usr/share/ncid/themes
75
76### global variables that can be changed by
77### command line options, the configuration file, the rcfile
78set AltDate             0
79
80### global variables that can be changed by
81### command line options, the configuration file
82set Hosts               [list]
83set Delay               15
84set PIDfile             ""
85set PopupTime           1
86set Verbose             0
87set NoGUI               0
88set CallOnRing          0
89set HostnameFlag        0
90set Ring                999
91set NoExit              0
92set WakeUp              0
93set ExitOn              exit
94set CallLog             0
95set Country             "US"
96set LogEnable           0
97set UnixLogDir          $::env(HOME)/NCID/client
98set WinLogDir           "logs"
99set UnixRCfile          "$::env(HOME)/.ncid"
100set WinRCfile           ".ncid"
101set UnixASfile          "$::env(HOME)/.config/autostart/net.sourceforge.ncid.desktop"
102
103### global variables that can be changed by
104### command line options, the rcfile
105set Host                ""
106set Port                ""
107
108### global variables that can be changed by
109### the configuration file, the rcfile
110set DateSepar           "/"
111
112###  global variables that can only be changed by
113#### command line options
114set Module              ""
115
116###  global variables that can only be changed by
117#### the configuration file
118set ModDir              /usr/share/ncid/modules
119set ModName             ""
120set NoOne               0
121set YearDot             0
122set WrapLines           "word"
123set DialPrefix          ""
124set preClient_1_0       0
125set nameWidth           30
126set ClipboardPopup      1
127set ClipboardPopupTime  3
128
129### global variables that can only be changed by
130### the rcfile
131set clock               24
132set autoSave            "off"
133set autoStart           "off"
134set fontList            ""
135set wmGeometry          ""
136
137### global variables that are used as static variables
138set NightMode           0
139set ThemeName           "day"
140set fgColor             ""
141set fg2Color            ""
142set bckColor            ""
143set LogFile             ""
144set LogChan             ""
145set LogStatus           "Log File:      disabled"
146set LogDirLocation      ""
147set oldHost             $Host
148set oldPort             $Port
149set oldDateSepar        $DateSepar
150set oldAltDate          $AltDate
151set oldAutoSave         $autoSave
152set oldAutoStart        $autoStart
153set oldClock            $clock
154set Leading1            "Leave"
155set oldLeading1         $Leading1
156set DialLineID          ""
157set display_line_num    0
158set awakened            0
159set Begin               0
160set End                 0
161set DoingCallLog        0
162set waitMsg             0
163set mod_menu            0
164set multi               0
165set menuDisabled        0
166set labelWidth          4
167set dateWidth           10
168set timeWidth           5
169set lineIDWidth         16
170set nmbrWidth           20
171set mtypeWidth          5
172set fieldseparators     6
173# the alias width must be in the same position as the alias type
174set aliasTypes          "NAMEDEP NAMEONLY NMBRDEP NMBRONLY NMBRNAME LINEONLY"
175set aliasWidths         "$nameWidth $nameWidth $nmbrWidth $nmbrWidth $nmbrWidth $lineIDWidth"
176set aliasList           ""
177set dtfile              "/usr/share/applications/net.sourceforge.ncid.desktop"
178set countryCodes        "DE FR HR SE UK US NONE"
179array set hup           {}
180set HostIndex           -1
181set SelAliasType        ""
182set ChangeHostFlag      0
183set hupColor            ""
184set ltColor             ""
185set dateColor           ""
186set timeColor           ""
187set lineColor           ""
188set nmbrColor           ""
189set nameColor           ""
190set mtColor             ""
191set tvFgColor           ""
192set tvBckColor          ""
193set vhFgColor           ""
194set vhBckColor          ""
195set vhInsColor          ""
196
197set LEVEL1              1
198set LEVEL2              2
199set LEVEL3              3
200set LEVEL4              4
201set LEVEL5              5
202set LEVEL6              6
203set LEVEL7              7
204set LEVEL8              8
205set LEVEL9              9
206
207set TypeGroups          [list]
208set oldTypeGroups       [list]
209
210set SelectedTypes       {BLK 0 CID 0 HUP 0 MWI 0 OUT 0 PID 0\
211                         PUT 0 RID 0 WID 0 MSG 0 NOT 0}
212set oldSelectedTypes    [list]
213set SelectedAllTypes    {BLK 1 CID 1 HUP 1 MWI 1 OUT 1 PID 1\
214                         PUT 1 RID 1 WID 1 MSG 1 NOT 1}
215set SelectedCalls       {BLK 1 CID 1 HUP 1 MWI 1 OUT 1 PID 1\
216                         PUT 1 RID 1 WID 1 MSG 0 NOT 0}
217set SelectedMessages    {BLK 0 CID 0 HUP 0 MWI 0 OUT 0 PID 0\
218                         PUT 0 RID 0 WID 0 MSG 1 NOT 1}
219set SelectedSmartPhone  {BLK 0 CID 0 HUP 0 MWI 0 OUT 0 PID 1\
220                         PUT 1 RID 0 WID 0 MSG 0 NOT 1}
221
222set LineIDGroups         0
223set oldLineIDGroups      0
224set DiscoveredLineIDs    [list]
225set SelectedLineIDs      [list]
226set oldSelectedLineIDs   [list]
227
228# Global Variables
229set Interpreter         [info nameofexecutable]
230set ServerOptions       ""
231set Dialed              0
232set ServerOptLineIDS    ""
233set svrLID              ""
234set OptPmsg             ""
235set delayedMsgs         ""
236
237set ConfigFileHost      ""
238set ConfigFilePort      ""
239set ConfigFileoldHost   ""
240set ConfigFileoldPort   ""
241set ConfigFileHosts     ""
242set ConfigFileHostIndex ""
243
244set ArgHost             ""
245set ArgPort             ""
246set ArgoldHost          ""
247set ArgoldPort          ""
248set ArgHosts            ""
249set ArgHostIndex        ""
250
251set RCfileHost          ""
252set RCfilePort          ""
253set RCfileoldHost       ""
254set RCfileoldPort       ""
255set RCfileHosts         ""
256set RCfileHostIndex     ""
257set RCfileThemeName     ""
258
259set PortableDir         ""
260
261set ScriptDir [file normalize [file dirname [info script]]]
262if {[file exists [file join $ScriptDir [file tail $ConfigFile]]]} {
263  set PortableDir $ScriptDir
264  set ConfigFile [file join $PortableDir [file tail $ConfigFile]]
265}
266
267if {$::tcl_platform(platform) == "unix"} {set LogDir $UnixLogDir
268} elseif {$::tcl_platform(platform) == "windows"} {set LogDir $WinLogDir}
269
270if {[file exists $ConfigFile]} {
271  # note: debug log has not been opened yet so logMsg is unavailable
272  source $ConfigFile
273  set delayedMsgs "Processed config file: $ConfigFile"
274  #deprecated warning will be given once the log file has been opened
275  if {$Hosts == ""} {
276    if {$Host != ""} {
277      set delayedMsgs \
278      "$delayedMsgs\n***** Host: $Host\n***** WARNING: config file option 'Host' is deprecated, use 'Hosts'"
279    }
280    if {$Port != ""} {
281      set delayedMsgs \
282      "$delayedMsgs\n***** Port: $Port\n***** WARNING: config file option 'Port' is deprecated, use 'Hosts'"
283    }
284    if {$Host != "" || $Port != ""} {
285      if {$Host == ""} {
286        set newhost $DefaultHost
287      } else {set newhost $Host}
288      if {$Port == ""} {
289        set newport $DefaultPort
290      } else {set newport $Port}
291      set Hosts "$newhost:$newport"
292      set delayedMsgs \
293        "$delayedMsgs\n***** Hosts: $newhost:$newport"
294    }
295  } else {
296    if {$Host != ""} {
297      set delayedMsgs \
298      "$delayedMsgs\n***** Host: $Host\n***** WARNING: using config file 'Hosts option, remove deprecated 'Host' option."
299      set Host ""
300    }
301    if {$Port != ""} {
302      set delayedMsgs \
303      "$delayedMsgs\n***** Port: $Port\n***** WARNING: using config file 'Hosts' option, remove deprecated, 'Port' option."
304      set Port ""
305    }
306  }
307} else {set delayedMsgs "*** Config file Missing: $ConfigFile"}
308
309# if we're running in tclsh, force non-graphical
310if {[regexp {tclsh} $Interpreter]} {set NoGUI 1}
311
312set ConfigFileHost $Host
313set ConfigFilePort $Port
314set ConfigFileoldHost $ConfigFileHost
315set ConfigFileoldPort $ConfigFilePort
316set ConfigFileHosts $Hosts
317set ConfigFileHostIndex $HostIndex
318
319### Constants
320
321set CygwinBat     /cygwin.bat
322
323set viewTextWidth 80
324
325# historyTextWidth must be increased by 1 character
326# $timeWidth will be included after config file sets $clock which can
327# cause it to change its value from 5 to 8
328set historyTextWidth [ expr $labelWidth + $dateWidth + $lineIDWidth + \
329                            $nmbrWidth + $nameWidth +  $mtypeWidth + \
330                            $fieldseparators + 1 ]
331
332set linelabel [format "%-${lineIDWidth}.${lineIDWidth}s" "LINE ID"]
333set nmbrlabel [format "%-${nmbrWidth}.${nmbrWidth}s" "NUMBER"]
334set namelabel [format "%-${nameWidth}.${nameWidth}s" "NAME"]
335set mtypelabel [format "%-${mtypeWidth}.${mtypeWidth}s" "MTYPE"]
336
337# DATE field width is either 10 or 11 characters
338# TIME field width is either 5 or 8 characters
339# clock 24 & DateSepar .
340set lbl1 "TYPE |DATE      |TIME |$linelabel|$nmbrlabel|$namelabel|$mtypelabel|"
341# clock 24 & DateSepar -
342set lbl2 "TYPE |DATE      |TIME |$linelabel|$nmbrlabel|$namelabel|$mtypelabel|"
343# clock 24 & DateSepar /
344set lbl3 "TYPE |DATE      |TIME |$linelabel|$nmbrlabel|$namelabel|$mtypelabel|"
345# clock 12 & DateSepar .
346set lbl4 "TYPE |DATE      |TIME    |$linelabel|$nmbrlabel|$namelabel|$mtypelabel|"
347# clock 12 & DateSepar -
348set lbl5 "TYPE |DATE      |TIME    |$linelabel|$nmbrlabel|$namelabel|$mtypelabel|"
349# clock 12 & DateSepar /
350set lbl6 "TYPE |DATE      |TIME    |$linelabel|$nmbrlabel|$namelabel|$mtypelabel|"
351# clock 24 & DateSepar . & YearDot 1
352set lbl7 "TYPE |DATE       |TIME |$linelabel|$nmbrlabel|$namelabel|$mtypelabel|"
353# clock 12 & DateSepar . & YearDot 1
354set lbl8 "TYPE |DATE       |TIME    |$linelabel|$nmbrlabel|$namelabel|$mtypelabel|"
355
356if {$ModName != ""} {
357    if {[regexp {^.*/} $ModName]} { set Module "$ModName"
358    } else {set Module "$ModDir/$ModName"}
359} else {set Module ""}
360
361### global variables that are fixed
362set Count       0
363set ExecSh      0
364set Socket      0
365set Try         0
366set Version     "XxXxX"
367#during development the version# is embedded in the file name
368#'tolower' and 'repeat' thwarts sed in Makefile
369if {[string tolower $Version] == [string repeat "x" 5]} {set Version [file tail [info script]]}
370
371set VersionIDENT "client ncid (NCID) $Version"
372set Usage       {Usage:   ncid  [OPTS] [ARGS]
373         OPTS: [--no-gui]
374               [--alt-date                  | -A]
375               [--call-log                  | -c]
376               [--country-code <code>       | -C <country code>]
377               [--delay <seconds>           | -D <seconds>]
378               [--help                      | -h]
379               [--hostname-flag             | -H]
380               [--log-enable <0-2>          | -l <0-2>]
381               [--log-dir <dirname>         | -L <dirname>]
382               [--module <module name>      | -m <module name>]
383               [--noexit                    | -X]
384               [--pidfile <file>            | -p <file>]
385               [--PopupTime <0-5>           | -t <0-5>]
386               [--ring <0-9|-1|-2|-9>       | -r <0-9|-1|-2|-9>]
387               [--verbose <1-9>             | -v <1-9>]
388               [--version                   | -V]
389               [--wakeup                    | -W]
390
391         ARGS: [<IP_ADDRESS>                | <HOSTNAME>]
392               [<PORT_NUMBER>]}
393
394set hostname [info hostname]
395regsub {([^.]*).*} $hostname {\1/ncid} LineName
396set VersionInfo "Client: ncid \[$hostname\] NCID $Version"
397
398set Author \
399"
400Copyright © 2001-2020
401John L. Chmielewski
402"
403
404set Website "http://ncid.sourceforge.net"
405
406set labBLK "BLK:  Blocked           - blacklisted call blocked"
407set labCID "CID:  Caller ID         - incoming call"
408set labHUP "HUP:  Hangup            - blacklisted call hangup"
409set labMSG "MSG:  Message           - text message from a user or the server"
410set labMWI "MWI:  Voicemail         - one or more voicemail messages"
411set labNOT "NOT:  Notice            - a smartphone message notice"
412set labOUT "OUT:  Out               - outgoing call"
413set labPID "PID:  Phone ID          - Caller ID from a smartphone"
414set labPUT "PUT:  Phone out call    - outgoing Caller ID from a smartphone"
415set labRID "RID:  Ring Back         - rings back when called number is available"
416set labWID "WID:  Call Waiting ID   - Caller ID from call waiting"
417
418set labList \
419"
420$labBLK
421$labCID
422$labHUP
423$labMSG
424$labMWI
425$labNOT
426$labOUT
427$labPID
428$labPUT
429$labRID
430$labWID
431"
432
433set fieldList \
434"
435TYPE       - call or message label
436DATE       - date of call or message
437TIME       - time of call or message
438LINE ID    - telephone line label
439NUMBER     - caller telephone number
440NAME       - caller name
441MTYPE      - message type
442"
443
444set viewHelp \
445"
446Types:   Shows all NCID line types.  Those
447         not crossed out are those that may
448         or may not show up in the history
449         window.
450
451LineIDs: Shows all line identifications.
452         Those that are not crossed out
453         are those that show up in the
454         history window.
455"
456
457set serverHelp \
458"
459\"Reload alias, blacklist and whitelist files\" menu entry:
460    Server reloads its Alias, Blacklist and Whitelist files.
461
462\"Update current call log\" menu entry:
463    Server replaces items in its cidcall.log file with
464    aliases in its ncidd.alias file.
465
466\"Update all call logs\" menu entry:
467    Server replaces items in its current cidcall.log file
468    and previous ones with aliases in its ncidd.alias file.
469
470\"Reread call log\" menu entry:
471    Server resends the cidcall.log file.
472
473Selecting a line in the history window will enable the alias,
474blacklist, whitelist and clipboard menu entries.  If a modem
475is active, \"Dial Number Manually \" is enabled.  If the line
476selected is all digits, \"Dial Number From History\" will also
477be enabled.
478
479You can remove a history window line selection by clicking
480on \"Send Message:\" or on a text line below it
481
482Once you modify the alias file you must:
483    * Reload alias, blacklist and whitelist files
484    * Update the current call log or all call logs
485    * Reread call log
486
487Once you modify the blacklist or whitelist file, you must:
488    * Reload alias, blacklist and whitelist files
489"
490
491########################################################################
492#                       PROCEDURE DEFINITIONS                          #
493########################################################################
494
495# display error message and exit
496proc exitMsg {code msg} {
497    global NoGUI LogChan
498
499    if {$LogChan != ""} {
500        set systemTime [clock seconds]
501        puts $LogChan "\[[clock format $systemTime -format "%m/%d %H:%M"]\] $msg"
502    }
503
504    if $NoGUI {
505        puts stderr $msg
506    } else {
507        wm withdraw .
508        option add *Dialog.msg.wrapLength 9i
509        option add *Dialog.msg.font "courier 12"
510        tk_messageBox -message $msg -icon error -type ok
511    }
512    exit $code
513}
514
515# platform, OS, etc.
516proc machine {which} {
517  # Observed values      platform       os             osgui
518  # Windows 10:          windows        Windows NT     win32
519  # Mac (native GUI):    unix           Darwin         aqua
520  # Mac (XQuartz):       unix           Darwin         x11
521  # AndroWish:           unix           Linux          x11
522  # Fedora 25 cinnamon:  unix           Linux          x11
523  # FreeBSD              unix           FreeBSD        x11
524
525  switch $which {
526    platform {
527      if {$::sdltk_present && [expr [sdltk android]]} {return "android"}
528      if {$::sdltk_present && [expr [sdltk ischromebook]]} {return "chromebook"}
529      return $::tcl_platform(platform)
530    }
531    os {
532      if {$::sdltk_present && [expr [sdltk ischromebook]]} {return "Chrome OS"}
533      if {$::sdltk_present && [expr [sdltk android]]} {return "Linux"}
534      return $::tcl_platform(os)
535    }
536    osgui {
537      return [tk windowingsystem]
538    }
539    osname {
540      set osname [machine os]
541      if {$osname eq "Darwin"} {return "OS X"}
542      if {$osname eq "Chrome OS"} {return "Chromebook"}
543      if {$::sdltk_present && [expr [sdltk android]]} {return "Android"}
544      return $osname
545    }
546    model {
547      if {$::borg_present} {
548        array set temparray [borg osbuildinfo]
549        return $temparray(model)
550      } else {return "not available"}
551    }
552  }
553}
554
555# display the $Try attempt number to connect to ncidd
556proc tryCount {msg} {
557    global Count
558    global Delay
559    global Try
560    global Txt
561    global NoGUI
562
563    # If $Delay == 0, do not try to reconnect
564    if (!$Delay) {exit -1}
565
566    if $NoGUI {
567        set Once 0
568        puts -nonewline stderr $msg
569        after [expr $Delay*1000] set Once 1
570        vwait Once
571    } else {
572        set Count $Delay
573        while {$Count > 0} {
574            if {$Count == 1} {
575                set Txt "$msg Try $Try in $Count second."
576            } else {
577                set Txt "$msg Try $Try in $Count seconds."
578            }
579            set Once 0
580            set Count [expr $Count - 1]
581            after [expr 1000] set Once 1
582            vwait Once
583        }
584    }
585}
586
587# close connection to NCID server if open, then reconnect
588proc Reconnect {} {
589    global Socket
590    global Count
591
592    if $Count {
593        # already waiting to reconnect, force a retry
594        set Count 0
595        return
596    }
597
598    if {$Socket > 0} {
599        # close connection to server
600        flush $Socket
601        fileevent $Socket readable ""
602        close $Socket
603        set Socket 0
604    }
605
606    connectCID
607}
608
609# This catches a lot of errors!
610proc bgerror {mess} {
611    global errorInfo
612    global errorCode
613
614    exitMsg 1 "BGError: $mess\n$errorInfo\n$errorCode\n"
615}
616
617proc ncidInfo {} {
618
619set sysInfo1 \
620"
621Windowing System: [string totitle [machine osgui]]
622Operating System: [machine osname]
623Platform        : [string totitle [machine platform]]
624Theme           : [string totitle $::ThemeName]"
625
626if {[file isdirectory $::ThemeDir]} {
627    set sysInfo2 "\nAddon Themes Dir: $::ThemeDir/"
628} else {set sysInfo2 ""}
629
630set sysInfo3 \
631"
632Config File     : $::ConfigFile
633Preference File : $::rcfile
634Wish Executable : $::Interpreter
635                  Version [info patchlevel]
636[regsub {:} $::LogDirLocation {   :}]
637[regsub {:     } [regsub {          } $::LogStatus {             }] {        :}]
638"
639    if {$sysInfo2 == ""} {
640        set sysinfo "$sysInfo1$sysInfo3"
641    } else {
642        set sysinfo "$sysInfo1$sysInfo2$sysInfo3"
643    }
644}
645
646proc getWidgetProps {verboseLevel verboseMsg widgetPath whichOptions} {
647
648        #if whichOptions is "all" then all properties are dumped
649        #otherwise, only options related to color, relief and text are dumped
650
651        logMsg $verboseLevel "$verboseMsg Current theme reported by ttk::style is: [ttk::style theme use]"
652
653        lmap c [$widgetPath configure ] {
654             if {[llength $c] == 2} continue;
655             set testvarname [lindex $c 0]
656             set doDump 1
657             if {$whichOptions != "all"} {
658                 set doDump 0
659                 if {[string match -nocase "*ground*" $testvarname] || [string match -nocase "*color*" $testvarname] |\
660                      [string match -nocase "*relief*" $testvarname] | [string match -nocase "*text*" $testvarname]} {
661                      set doDump 1
662                 }
663            }
664            if {$doDump} {
665                 set val [$widgetPath cget $testvarname]
666                 logMsg $verboseLevel "$verboseMsg $widgetPath [format "%-25.25s %s" "$testvarname" "$val"]"
667            }
668        }
669
670}
671
672# performs crude checks on host and port
673# https://www.appypie.com/faqs/what-characters-are-allowed-in-a-domain-name
674#   The characters allowed in a domain name include letters (abc), numbers
675#   (123), and dashes/hyphens (---). No spaces are allowed and the domain
676#   name can't begin or end with dash/hyphen.
677proc checkHosts {} {
678    global Hosts
679
680    # Hosts = host:port [host:port] [...]
681    foreach hostport $Hosts {
682        lassign [split $hostport ":"] host port
683        if {[string length $host] < 4} {
684            exitMsg 8 "Network address too short: $host"
685        } elseif {![regexp {^[\w][\w.-]+$} $host]} {
686            exitMsg 8 "Network address has characters not allowed: $host"
687        }
688        if {[regexp {^.*-$} $host]} {
689            exitMsg 8 "Network address must not end in a dash: $host"
690        }
691        if {![regexp {^\d{4,5}$} $port]} {
692            exitMsg 9 "Network port must be 4 or 5 digits: $port"
693        }
694    }
695}
696
697proc getWindowProps {verboseLevel verboseMsg windowPath} {
698
699    foreach i {geometry manager name parent rootx rooty width height screenwidth screenheight vrootwidth vrootheight  \
700               vrootx vrooty x y} {
701        set val [winfo $i $windowPath]
702        logMsg $verboseLevel "$verboseMsg $windowPath [format "%-25.25s %s" "$i" "$val"] "
703    }
704
705}
706
707# Get data from CID server
708proc getCID {} {
709    global Module Host Port Socket NoGUI Try Verbose VersionInfo
710    global Ring CallOnRing
711    global cid label display_line_num DoingCallLog
712    global call Dialed lineID ServerOptLineIDS
713    global WakeUp wakened targetTime Begin End ClientJobResult waitMsg
714    global mod_menu argument menuDisabled
715    global CIDaliasType LineAliasType
716    global ServerOptions nmbrREQ
717    global hup bckColor
718    global TypeGroups SelectedTypes ChangeHostFlag
719    global LineIDGroups SelectedLineIDs DiscoveredLineIDs
720
721    # convert list to array
722    array set t_array $SelectedTypes
723
724    set msg {server connection closed}
725    set cnt 0
726    while {$cnt != -1} {
727        if {[eof $Socket] || [catch {set cnt [gets $Socket dataBlock]} msg]} {
728            # remove event handler
729            fileevent $Socket readable ""
730            close $Socket
731            set ServerOptions ""
732            if !$NoGUI {
733                if {$menuDisabled == 0} {
734                  set menu .menubar.server
735                  $menu entryconfigure Reload* -state disabled
736                  $menu entryconfigure Update*current* -state disabled
737                  $menu entryconfigure Update*all*call* -state disabled
738                  $menu entryconfigure Reread* -state disabled
739                  $menu entryconfigure Add/Modify* -state disabled
740                  $menu entryconfigure Add*to*Blacklist* -state disabled
741                  $menu entryconfigure Remove*from*Blacklist* -state disabled
742                  $menu entryconfigure Add*to*Whitelist* -state disabled
743                  $menu entryconfigure Remove*from*Whitelist* -state disabled
744                  $menu entryconfigure Dial*Number* -state disabled
745                  $menu entryconfigure Copy*to*Clipboard* -state disabled
746                  set menuDisabled 1
747                }
748            }
749            set Try [expr $Try + 1]
750            tryCount "$Host:$Port - $msg\n"
751            connectCID
752            return
753        }
754        set Try 0
755
756        # get rid of non-printable characters at start/end of string
757        set dataBlock [string trim $dataBlock]
758
759        if {[string match 200* $dataBlock]} {
760            # output NCID server connect message
761            logMsg $::LEVEL1 $dataBlock
762            regsub {200 (.*)} $dataBlock {\1} dataBlock
763            if $NoGUI {
764               logMsg $::LEVEL1 "$VersionInfo\n$dataBlock"
765               set targetTime 0
766            } else {
767                set targetTime [expr [clock clicks -milliseconds] + 500]
768                displayCID "$VersionInfo\n$dataBlock" 1
769                }
770        } elseif {[string match 254* $dataBlock]} {
771            # NCID server sent start of call log message
772            if {!$NoGUI} {
773                set DoingCallLog 1
774                .vh configure -state normal
775                .vh insert 1.0 "\n\n\t\tReading the call log\n\n"
776                set Begin [clock clicks -milliseconds]
777            }
778        } elseif {[string match {25[0-3]*} $dataBlock]} {
779            # NCID server sent call log message
780            if !$NoGUI {
781                .vh delete 1.0 6.0
782                .vh yview moveto 1.0
783                .vh configure -state disabled
784                if {[lindex [.vh yview] 0] + [lindex [.vh yview] 1] == 1.0} {
785                    grid remove .ys
786                } else {
787                    grid .ys
788                }
789            }
790            set DoingCallLog 0
791            if {[regexp {250} $dataBlock]} {
792                # NCID server sent end of call log message
793                if {!$NoGUI} {
794                    set DiscoveredLineIDs [lsort -dictionary $DiscoveredLineIDs]
795                    if {$ChangeHostFlag == 1} {
796                        logMsg $::LEVEL1 "$dataBlock - $display_line_num lines"
797                        set SelectedLineIDs $DiscoveredLineIDs
798                        write_rc_file "set SelectedLineIDs" "set SelectedLineIDs \"$SelectedLineIDs\""
799                        set ChangeHostFlag 0
800                    }
801                }
802            } else {logMsg $::LEVEL1 "$dataBlock"}
803            set End [clock clicks -milliseconds]
804            set elapsed [expr $End - $Begin]
805            logMsg $::LEVEL2 "$display_line_num call history entries in $elapsed milliseconds"
806        } elseif {[string match 300* $dataBlock]} {
807            # NCID server sent end of startup message
808            if {$ServerOptions == ""} {set ServerOptions "\nnone"}
809            logMsg $::LEVEL1 $dataBlock
810            logMsg $::LEVEL2 "ServerOptions: [split [regsub -all {^\n|  +} $ServerOptions {}] "\n"]"
811            if {!$NoGUI} {logMsg $::LEVEL3 "DiscoveredLineIDs: $DiscoveredLineIDs"}
812            continue
813        } elseif {[string match 400* $dataBlock]} {
814            # NCID server has sent text to be displayed
815            logMsg $::LEVEL1 $dataBlock
816            toplevel .reply -background $bckColor
817            wm title .reply "Server's Response"
818            grid [text .reply.text -yscrollcommand ".reply.ys set" -setgrid 1 \
819                     -font FixedFontP -height 8 -width 70] \
820                     -pady 1 -padx 1 -sticky nesw
821            grid [ttk::scrollbar .reply.ys -command ".reply.text yview"] \
822                    -column 1 -row 0 -sticky ns -pady 1 -padx 1
823            grid [ttk::button .reply.btn -text "OK" -command {destroy .reply}] \
824                    -pady 10 -columnspan 2
825            grid columnconfigure .reply 0 -weight 1
826            grid rowconfigure .reply 0 -weight 1
827            wm minsize .reply 25 4
828            bind .reply <Configure> {
829                if {[lindex [.reply.text yview] 0] + [lindex [.reply.text yview] 1] == 1.0} {
830                    grid remove .reply.ys
831                } else {
832                    grid .reply.ys
833                }
834            }
835            modal {.reply}
836            continue;
837        } elseif {[string match 401* $dataBlock]} {
838            # NCID server has sent text to be displayed, must ACCEPT or REJECT
839            logMsg $::LEVEL1 $dataBlock
840            toplevel .reply -background $bckColor
841            wm title .reply "Server's Response"
842            grid [text .reply.text -yscrollcommand ".reply.ys set" -setgrid 1 \
843                    -font FixedFontP -height 8 -width 70] \
844                    -pady 10 -padx 10 -sticky nesw
845            .reply.text insert 1.0 "\n\n\tUpdating call logs"
846            .reply.text configure -state disabled
847            grid [ttk::scrollbar .reply.ys -command ".reply.text yview"] \
848                    -column 1 -row 0 -sticky ns -pady 10 -padx 5
849            grid [ttk::frame .reply.fr]  -pady 10 -padx 10 -columnspan 2 -row 1
850            ttk::button .reply.accept_btn -text "Accept" -state disabled -command {
851                    global multi
852
853                    if {$multi} {
854                        set temp "S"
855                    } else {
856                        set temp ""
857                    }
858                    puts $Socket "WRK: ACCEPT LOG$temp"
859                    flush $Socket
860                    destroy .reply
861                    }
862            ttk::button .reply.reject_btn -text "Reject" -state disabled -command {
863                    global multi
864
865                    if {$multi} {
866                        set temp "S"
867                    } else {
868                        set temp ""
869                    }
870                    puts $Socket "WRK: REJECT LOG$temp"
871                    flush $Socket
872                    destroy .reply
873                    }
874            grid .reply.accept_btn .reply.reject_btn -in .reply.fr -padx 25
875            grid columnconfigure .reply 0 -weight 1
876            grid rowconfigure .reply 0 -weight 1
877            wm minsize .reply 40 5
878            bind .reply <Configure> {
879                if {[lindex [.reply.text yview] 0] + [lindex [.reply.text yview] 1] == 1.0} {
880                    grid remove .reply.ys
881                } else {
882                    grid .reply.ys
883                }
884            }
885            showBusy "." .reply.text
886            modal {.reply}
887            continue;
888        } elseif {[string match 402* $dataBlock]} {
889            logMsg $::LEVEL1 $dataBlock
890             set ClientJobResult ""
891        } elseif {[string match 403* $dataBlock]} {
892            logMsg $::LEVEL1 $dataBlock
893            set mod_menu 1
894        } elseif {[string match 410* $dataBlock]} {
895            logMsg $::LEVEL1 $dataBlock
896            .reply.text configure -state normal
897            .reply.text delete end-1chars
898            .reply.text configure -state disabled
899            .reply.text see end
900            catch {
901                if {[lindex [.reply.text yview] 0] + [lindex [.reply.text yview] 1] == 1.0} {
902                    grid remove .reply.ys
903                } else {
904                    grid .reply.ys
905                }
906            }
907            catch {
908                .reply.accept_btn configure -state normal
909                .reply.reject_btn configure -state normal
910            }
911            continue
912        } elseif {[string match 411* $dataBlock]} {
913            logMsg $::LEVEL1 $dataBlock
914            if {$mod_menu} {
915                set mod_menu 0
916                continue
917            }
918            if {[string length $ClientJobResult] < 4} {
919                set ClientJobResult "Done."
920            }
921            if {$Dialed} {
922                # $Dialed == 2 when dial aborted
923                if {$Dialed == 1} {
924                    .dial.close configure -state active
925                    .dial.abort configure -state active
926                }
927                set Dialed 0
928            } else {.confirm.close configure -state active}
929        } elseif {[string match INFO:* $dataBlock]} {
930            logMsg $::LEVEL1 $dataBlock
931            if {$mod_menu} {
932                set menu .menubar.server
933                $menu entryconfigure Copy*to*Clipboard* -state normal
934                set temp [split $dataBlock " "]
935                set fileType [lindex $temp 1]
936                if {$fileType == "dial"} {
937                    set dialarg [lindex $temp 2]
938                } else {set argument [lindex $temp 2]}
939                switch $fileType {
940                    alias {
941                        set CIDaliasType [lindex $temp 2]
942                        set LineAliasType [lindex $temp 3]
943                        $menu entryconfigure Add*Alias* -state normal
944                    }
945                    black {
946                        $menu entryconfigure Add*Black* -state disabled
947                        $menu entryconfigure Add*White* -state normal
948                        $menu entryconfigure Remove*Black* -state normal
949                        $menu entryconfigure Remove*White* -state disabled
950                    }
951                    white {
952                        $menu entryconfigure Add*Black* -state disabled
953                        $menu entryconfigure Add*White* -state disabled
954                        $menu entryconfigure Remove*Black* -state disabled
955                        $menu entryconfigure Remove*White* -state normal
956                    }
957                    neither {
958                        $menu entryconfigure Add*Black* -state normal
959                        $menu entryconfigure Add*White* -state normal
960                        $menu entryconfigure Remove*Black* -state disabled
961                        $menu entryconfigure Remove*White* -state disabled
962                    }
963                    dial {
964                        if {$dialarg == "NODIAL"} {
965                            $menu.dial entryconfigure From*History* -state disabled
966                        } else {
967                            $menu.dial entryconfigure From*History* -state normal
968                        }
969                    }
970                }
971                continue;
972            }
973            .reply.text configure -state normal
974            if {$waitMsg} {
975                set waitMsg 0
976                .reply.text delete 1.0 end
977            }
978            .reply.text insert end [string range [append dataBlock " \n"] 6 end]
979            .reply.text configure -state disabled
980            continue
981        } elseif {[string match RESP:* $dataBlock]} {
982            logMsg $::LEVEL1 $dataBlock
983            if ($Dialed) {
984                if {[string first "Pickup phone" $dataBlock 0] != -1} {
985                    .dial.abort configure -state active
986                    .dial.close configure -state active
987                } else {
988                    regsub {.*Server modem ([\w\d\s]+) dialed.*$} $dataBlock {\1} svrLID
989                }
990            }
991            append ClientJobResult [string range $dataBlock 6 end]
992            append ClientJobResult "\n"
993            continue
994        } elseif {[string match RPLY:* $dataBlock]} {
995            logMsg $::LEVEL1 $dataBlock
996            set ClientJobResult [string range $dataBlock 6 end]
997            append ClientJobResult "\n"
998            destroy .dial
999            doRPLY
1000        } elseif {[string match OPT:* $dataBlock]} {
1001            set ServerOpt [string trim [string range $dataBlock 5 end]]
1002            if {[string first "LineIDS:" $ServerOpt 0] != -1} {
1003                regsub {^.*LineIDS: (.*)$} $ServerOpt {\1} ServerOptLineIDS
1004                if {!$NoGUI} {
1005                    .menubar.server entryconfigure Dial*Number* -state normal
1006                }
1007            }
1008            logMsg $::LEVEL3 "Received Server Option: $ServerOpt"
1009            set ServerOptions "$ServerOptions\n$ServerOpt"
1010        }
1011        if {[set label [checkType $dataBlock]]} {
1012            if {$label == 3} {
1013                # CIDINFO (ring) line
1014                set ringinfo [getField RING $dataBlock]
1015                # must use $call($lineinfo) instead of $cid
1016                set lineinfo [getField LINE $dataBlock]
1017                if {!$NoGUI} {
1018                    set status [processLineID "$lineinfo"]
1019                    if {$LineIDGroups == 1 && $status != 1} {continue}
1020                }
1021                if {[array get call $lineinfo] != {}} {
1022                  set CIDtype [lindex $call($lineinfo) 5]
1023                  if {$ringinfo == -4} {
1024                    if {!$NoGUI } {
1025                      # get line from hup array and restore HUP color in GUI
1026                      if {[catch {set displine $hup($lineinfo)} huperr]} {
1027                        logMsg $::LEVEL1 "huperr"
1028                      } else {
1029                        # restore theme color to $CIDtype
1030                        .vh configure -state normal
1031                        .vh replace $displine.0 $displine.0+4c "$CIDtype:" lttag
1032                        .vh configure -state disabled
1033                      }
1034                    }
1035                  } elseif {$CallOnRing && $CIDtype == "CID"} {
1036                    if {$Module != "" && ($Ring == $ringinfo ||
1037                        ($Ring == -9 && $ringinfo > 1))} {
1038                      sendCID $call($lineinfo)
1039                      logMsg $::LEVEL1 "$dataBlock"
1040                    } else { logMsg $::LEVEL6 "$dataBlock" }
1041                  }
1042                } else {
1043                    logMsg $::LEVEL1 "Phone line label \"$lineinfo\" not found"
1044                }
1045                if {$WakeUp && $ringinfo == 1} {
1046                    doWakeup
1047                    set wakened 1
1048                }
1049            } elseif {$label == 4 || $label == 5} {
1050                # MSG (4), NOT (4)
1051                # MSGLOG (5), NOTLOG (5)
1052                set msg [formatMSG $dataBlock]
1053                if {!$NoGUI} {
1054                    set status [processLineID "[lindex $msg 4]"]
1055                    set thisTYPE [lindex $msg 5]
1056                    if {$LineIDGroups == 1 && $status != 1} {continue}
1057                    if {$TypeGroups == 1} {continue}
1058                    if {$TypeGroups == 3 && !$t_array($thisTYPE)} {continue}
1059                    if {$TypeGroups == 4 && !$t_array($thisTYPE)} {continue}
1060                }
1061                displayLog $msg 1
1062                if {$label == 4} {
1063                    if {!$NoGUI} {
1064                        displayCID "[lindex $msg 7]\n" 1
1065                        doPopup
1066                    }
1067                    if {$Module != ""} {
1068                        sendMSG $msg
1069                    }
1070                }
1071            } elseif {$label == 1 || $label == 2} {
1072                # CID (1), HUP (1) OUT (1), RID (1)
1073                # BLK (2), MWI (2), PID (2), PUT(2), WID (2)
1074                if {$WakeUp} {
1075                    if {!$wakened} {
1076                        doWakeup
1077                    } else {set wakened 0}
1078                }
1079                set cid [formatCID $dataBlock]
1080                if {!$NoGUI} {
1081                    set status [processLineID "[lindex $cid 4]"]
1082                    set thisTYPE [lindex $cid 5]
1083                    if {$LineIDGroups == 1 && $status != 1} {continue}
1084                    if {$TypeGroups == 2} {continue}
1085                    if {$TypeGroups == 3 && !$t_array($thisTYPE)} {continue}
1086                    if {$TypeGroups == 4 && !$t_array($thisTYPE)} {continue}
1087                }
1088                if {$label == 1} {array set call "{$lineID} [list $cid]"}
1089                # display log
1090                displayLog $cid 0
1091                # display CID
1092                if {!$NoGUI} {
1093                    displayCID $cid 0
1094                    doPopup
1095                }
1096                set CIDtype [lindex $cid 5]
1097                if {(!$CallOnRing  || $CIDtype == "CID" || $Ring == -9) && $Module != ""} {
1098                    sendCID $cid
1099                }
1100            } elseif {$label == 6} {
1101                # BLKLOG, CIDLOG, HUPLOG, MWILOG, OUTLOG, PIDLOG, PUTLOG, RIDLOG, WIDLOG
1102                set cid [formatCID $dataBlock]
1103                if {!$NoGUI} {
1104                    set status [processLineID "[lindex $cid 4]"]
1105                    set thisTYPE [lindex $cid 5]
1106                    if {$LineIDGroups == 1 && $status != 1} {continue}
1107                    if {$TypeGroups == 2} {continue}
1108                    if {$TypeGroups == 3 && !$t_array($thisTYPE)} {continue}
1109                    if {$TypeGroups == 4 && !$t_array($thisTYPE)} {continue}
1110                }
1111                # display log
1112                displayLog $cid 0
1113                if {!$NoGUI && $targetTime && [clock clicks -milliseconds] >= $targetTime} {
1114                    set targetTime [expr [clock clicks -milliseconds] + 500]
1115                    .vh insert 3.end "."
1116                    update idletasks
1117                }
1118            }
1119        }
1120    }
1121    if {!$NoGUI && $DiscoveredLineIDs != ""} {updateViewDisplay}
1122}
1123
1124proc showBusy {text widget} {
1125    global waitMsg
1126
1127    $widget configure -state normal
1128    $widget insert end $text
1129    $widget configure -state disabled
1130    set waitMsg 1
1131}
1132
1133proc doWakeup {} {
1134    global ExecSh
1135    global ModDir
1136
1137    if $ExecSh {
1138        catch {exec sh -c $ModDir/ncid-wakeup} oops
1139    } else {
1140        catch {exec $ModDir/ncid-wakeup} oops
1141    }
1142}
1143
1144#   PopupTime = 0:   doPopup is disabled
1145#   PopupTime = 1-5: Time in seconds window is forced to remain
1146#                    on top before user is allowed to remove it.
1147proc doPopup {} {
1148    global PopupTime
1149
1150    if {! $PopupTime} { return }
1151
1152    wm deiconify .
1153    raise .
1154    wm attributes . -topmost true
1155    after [expr $PopupTime*1000] wm attributes . -topmost false
1156}
1157
1158proc checkType {dataBlock} {
1159
1160    set rtn 0
1161    # Determine label type
1162    # General classifications:
1163    #  1 = real time: calls that can trigger WakeUp - CID, HUP, OUT, RID
1164    #  2 = real time: other calls - BLK, MWI, PID, PUT, WID
1165    #  3 = real time: ring detected - CIDINFO
1166    #  4 = real time: messages (non-calls)
1167    #  5 = log file : messages (non-calls)
1168    #  6 = log file : calls classified as 1 and 2 with suffix LOG
1169    #  7 = log file : unrecognized line type
1170    #  8 = real time: relay job - RLY
1171    #  9 = log file : relay job - RLYLOG
1172    # 10 = real time: call accounting - END
1173    # 11 = log file : call accounting - ENDLOG
1174          if [string match CID:* $dataBlock] {set rtn 1
1175    } elseif [string match HUP:* $dataBlock] {set rtn 1
1176    } elseif [string match OUT:* $dataBlock] {set rtn 1
1177    } elseif [string match RID:* $dataBlock] {set rtn 1
1178
1179    } elseif [string match BLK:* $dataBlock] {set rtn 2
1180    } elseif [string match MWI:* $dataBlock] {set rtn 2
1181    } elseif [string match PID:* $dataBlock] {set rtn 2
1182    } elseif [string match PUT:* $dataBlock] {set rtn 2
1183    } elseif [string match WID:* $dataBlock] {set rtn 2
1184
1185    } elseif [string match CIDINFO:* $dataBlock] {set rtn 3
1186
1187    } elseif [string match MSG:* $dataBlock] {set rtn 4
1188    } elseif [string match NOT:* $dataBlock] {set rtn 4
1189
1190    } elseif [string match MSGLOG:* $dataBlock] {set rtn 5
1191    } elseif [string match NOTLOG:* $dataBlock] {set rtn 5
1192
1193    } elseif [string match BLKLOG:* $dataBlock] {set rtn 6
1194    } elseif [string match CIDLOG:* $dataBlock] {set rtn 6
1195    } elseif [string match HUPLOG:* $dataBlock] {set rtn 6
1196    } elseif [string match MWILOG:* $dataBlock] {set rtn 6
1197    } elseif [string match OUTLOG:* $dataBlock] {set rtn 6
1198    } elseif [string match PIDLOG:* $dataBlock] {set rtn 6
1199    } elseif [string match PUTLOG:* $dataBlock] {set rtn 6
1200    } elseif [string match RIDLOG:* $dataBlock] {set rtn 6
1201    } elseif [string match WIDLOG:* $dataBlock] {set rtn 6
1202
1203    } elseif [string match LOG:* $dataBlock] {set rtn 7
1204
1205    } elseif [string match RLY:* $dataBlock] {set rtn 8
1206
1207    } elseif [string match RLYLOG:* $dataBlock] {set rtn 9
1208
1209    } elseif [string match END:* $dataBlock] {set rtn 10
1210
1211    } elseif [string match ENDLOG:* $dataBlock] {set rtn 11}
1212    logMsg $::LEVEL6 "Assigned $rtn for $dataBlock"
1213    return $rtn
1214}
1215
1216# must be sure the line passed checkType
1217# returns: $ciddate $cidtime $cidnumber $cidname $cidline $linetype "" ""
1218proc formatCID {dataBlock} {
1219    global lineID lineIDWidth
1220
1221    set cidname [formatNAME $dataBlock]
1222    set cidnumber [formatNMBR $dataBlock]
1223    set ciddate [formatDATE $dataBlock]
1224    set cidtime [formatTIME $dataBlock]
1225    set cidline ""
1226    if [string match {*\*LINE\**} $dataBlock] {
1227        set cidline [formatLINE $dataBlock]
1228    }
1229    # set default line indicator, should not be needed anymore
1230    if {$cidline == ""} {
1231        set cidline "-"
1232        for {set x 0} {$x < $lineIDWidth} {incr x} {
1233            set cidline "$cidline "
1234        }
1235    }
1236    # create call line label
1237    regsub { *$} $cidline {} lineID
1238    # set type of call
1239    if {![regsub {(\w+)LOG:.*} $dataBlock {\1} linetype]} {
1240        regsub {(\w+):.*} $dataBlock {\1} linetype
1241    }
1242
1243    return [list $ciddate $cidtime $cidnumber $cidname $cidline $linetype "" ""]
1244}
1245
1246# returns: $msgdate $msgtime $msgnumber $msgname $msgline $linetype $mesgtype $message
1247proc formatMSG {dataBlock} {
1248
1249    if {![regsub {(\w+)LOG:.*} $dataBlock {\1} linetype]} {
1250        regsub {(\w+):.*} $dataBlock {\1} linetype
1251    }
1252
1253    if {[regexp {\*\*\*DATE} $dataBlock]} {
1254        set msgdate [formatDATE $dataBlock]
1255        set msgtime [formatTIME $dataBlock]
1256        set msgname [formatNAME $dataBlock]
1257        set msgnmbr [formatNMBR $dataBlock]
1258        set msgline [formatLINE $dataBlock]
1259        set msgtype [formatMTYPE $dataBlock]
1260        regsub {\w+:\s+(.*) \*\*\*DATE.*} $dataBlock {\1\2} mesg
1261        set message [list $msgdate $msgtime $msgnmbr $msgname $msgline $linetype $msgtype $mesg]
1262    } else {
1263        regsub {\w+:\s+(.*)} $dataBlock {\1} mesg
1264        set message [list {} {} {} {} {} $linetype {} $mesg]
1265    }
1266
1267    return $message
1268}
1269
1270proc formatMTYPE {dataBlock} {
1271    if {[regexp {\*\*\*DATE.*MTYPE} $dataBlock]} {
1272        set msgtype [getField MTYPE $dataBlock]
1273    } else {
1274        set msgtype "-"
1275    }
1276    return $msgtype
1277}
1278
1279proc formatLINE {dataBlock} {
1280    set cidline [getField LINE $dataBlock]
1281    return $cidline
1282}
1283
1284proc formatDATE {dataBlock} {
1285    global AltDate DateSepar YearDot
1286
1287    set ciddate [getField DATE $dataBlock]
1288    # slash (/) is the default date separator
1289    if {$AltDate} {
1290        # Date format: DDMMYY or DDMM
1291        if {![regsub {([0-9][0-9])([0-9][0-9])([0-9][0-9][0-9][0-9])} \
1292            $ciddate {\2/\1/\3} ciddate]} {
1293            regsub {([0-9][0-9])([0-9][0-9].*)} $ciddate {\2/\1} ciddate
1294        }
1295    } else {
1296        # Date format: MMDDYY or MMDD
1297        if {![regsub {([0-9][0-9])([0-9][0-9])([0-9][0-9][0-9][0-9])} \
1298            $ciddate {\1/\2/\3} ciddate]} {
1299            regsub {([0-9][0-9])([0-9][0-9].*)} $ciddate {\1/\2} ciddate
1300        }
1301    }
1302    if {$DateSepar == "-"} {
1303        # set hyphen (-) as date separator
1304        regsub -all {/} $ciddate - ciddate
1305    } elseif {$DateSepar == "."} {
1306        if $YearDot {
1307          # set period (.) as date separator and append it to year (ordinal numbers)
1308          regsub -all {/} $ciddate. . ciddate
1309        } else {
1310          # set period (.) as date separator
1311          regsub -all {/} $ciddate . ciddate
1312        }
1313    }
1314    return $ciddate
1315}
1316
1317proc formatTIME {dataBlock} {
1318    global clock
1319
1320    set cidtime [getField TIME $dataBlock]
1321    if ([regexp {(\d{2})(\d{2})} $cidtime time hours minutes]) {
1322        if {$clock == 24} {
1323            set cidtime "$hours:$minutes"
1324        } else {
1325        set cidtime [convertTo12 $hours $minutes]
1326        }
1327    }
1328    return $cidtime
1329}
1330
1331proc formatNAME {dataBlock} {
1332    set cidname [getField NAME $dataBlock]
1333    if {$cidname == "-"} {set cidname "NO NAME"}
1334    return $cidname
1335}
1336
1337proc checkCountry {code} {
1338    global countryCodes NoGUI
1339
1340    if {![regexp "$code" $countryCodes]} {
1341      if {!$NoGUI} {
1342        logMsg $::LEVEL1 "Country Code \"$code\" is not supported.\nShould be one of \"$countryCodes\"."
1343     }
1344     exitMsg 7 "Country Code \"$code\" is not supported.\nShould be one of \"$countryCodes\"."
1345    }
1346}
1347
1348proc formatNMBR {dataBlock} {
1349    global Country NoOne
1350    # https://en.wikipedia.org/wiki/National_conventions_for_writing_telephone_numbers
1351
1352    set cidnumber [getField NMBR $dataBlock]
1353    if {$cidnumber == "-"} {set cidnumber "NO-NUMBER"}
1354    switch $Country {
1355      DE {set cidnumber [formatDE $cidnumber]}
1356      FR {set cidnumber [formatFR $cidnumber]}
1357      HR {set cidnumber [formatHR $cidnumber]}
1358      SE {set cidnumber [formatSE $cidnumber]}
1359      UK {set cidnumber [formatUK $cidnumber]}
1360      US {set cidnumber [formatUS $cidnumber]}
1361      NONE {}
1362      DEFAULT {
1363        exitMsg 7 "Country Code \"$Country\" is not supported. Please change it."
1364      }
1365    }
1366    return $cidnumber
1367}
1368
1369proc formatUS {cidnumber} {
1370    global NoOne
1371    # https://en.wikipedia.org/wiki/North_American_Numbering_Plan
1372    # US-PBX examples: 919715559679 -> 9,1-971-555-9679
1373    #                  99715559679 -> 9,971-555-9679
1374    if {![regsub {(9)(1)([0-9]{3})([0-9]{3})([0-9]{4})} \
1375      $cidnumber {\1,\2-\3-\4-\5} cidnumber]} {
1376      if {![regsub {(9)([0-9]{3})([0-9]{3})([0-9]{4})} \
1377      $cidnumber {\1,\2-\3-\4} cidnumber]} {
1378        if {![regsub {(1)([0-9]{3})([0-9]{3})([0-9]{4})} \
1379          $cidnumber {\1-\2-\3-\4} cidnumber]} {
1380          if {![regsub {(ob)([0-9]{3})([0-9]{3})([0-9]{3})} \
1381            $cidnumber {\1-\2-\3-\4} cidnumber]} {
1382            if {![regsub {^([0-9]{3})([0-9]{3})([0-9]{3})$} \
1383              $cidnumber {\1-\2-\3} cidnumber]} {
1384              if {![regsub {([0-9]{3})([0-9]{3})([0-9]{4})} \
1385                $cidnumber {\1-\2-\3} cidnumber]} {
1386                regsub {([0-9]{3})([0-9]{4})} \
1387                $cidnumber {\1-\2} cidnumber
1388              }
1389            }
1390          }
1391        }
1392      }
1393    } elseif {$NoOne} {
1394      regsub {^1-?(.*)} $cidnumber {\1} cidnumber
1395    }
1396    return $cidnumber
1397}
1398
1399proc formatUK {cidnumber} {
1400    # https://en.wikipedia.org/wiki/United_Kingdom_area_codes
1401    if {![regsub {^(011[0-9])([0-9]{3})([0-9]+)} \
1402      $cidnumber {\1-\2-\3} cidnumber]} {
1403      if {![regsub {^(01[0-9]1)([0-9]{3})([0-9]+)} \
1404        $cidnumber {\1-\2-\3} cidnumber]} {
1405        if {![regsub {^(13873|15242|19467)([0-9]{4,5})} \
1406          $cidnumber {\1-\2} cidnumber]} {
1407          if {![regsub {^(153)(94|95|96)([0-9]{4,5})} \
1408            $cidnumber {\1\2-\3} cidnumber]} {
1409            if {![regsub {^(169)(73|74|77)([0-9]{4,5})} \
1410              $cidnumber {\1\2-\3} cidnumber]} {
1411              if {![regsub {^(176)(83|84|87)([0-9]{4,5})} \
1412                $cidnumber {\1\2-\3} cidnumber]} {
1413                if {![regsub {^(01[0-9]{3})([0-9]+)} \
1414                  $cidnumber {\1-\2} cidnumber]} {
1415                  if {![regsub {^(02[0-9])([0-9]{4})([0-9]+)} \
1416                    $cidnumber {\1-\2-\3} cidnumber]} {
1417                    if {![regsub {^(0[389][0-9]{2})([0-9]{3})([0-9]+)} \
1418                      $cidnumber {\1-\2-\3} cidnumber]} {
1419                      if {![regsub {^(07[0-9]{3})([0-9]+)} \
1420                        $cidnumber {\1-\2} cidnumber]} {
1421                      }
1422                    }
1423                  }
1424                }
1425              }
1426            }
1427          }
1428        }
1429      }
1430    }
1431    return $cidnumber
1432}
1433
1434proc formatSE {cidnumber} {
1435    # https://en.wikipedia.org/wiki/Telephone_numbers_in_Sweden#Area_codes
1436    if {![regsub {^(07[0-9])([0-9]+)} \
1437        $cidnumber {\1-\2} cidnumber]} {
1438     if {![regsub {^(08)([0-9]+)} \
1439         $cidnumber {\1-\2} cidnumber]} {
1440      if {![regsub {^(01[013689])([0-9]+)} \
1441          $cidnumber {\1-\2} cidnumber]} {
1442       if {![regsub {^(0[23][[136])([0-9]+)} \
1443           $cidnumber {\1-\2} cidnumber]} {
1444        if {![regsub {^(04[0246])([0-9]+)} \
1445            $cidnumber {\1-\2} cidnumber]} {
1446         if {![regsub {^(054)([0-9]+)} \
1447             $cidnumber {\1-\2} cidnumber]} {
1448          if {![regsub {^(06[02])([0-9]+)} \
1449              $cidnumber {\1-\2} cidnumber]} {
1450           if {![regsub {^(090)([0-9]+)} \
1451               $cidnumber {\1-\2} cidnumber]} {
1452            regsub {^([0-9]{4})([0-9]+)} \
1453                    $cidnumber {\1-\2} cidnumber
1454           }
1455          }
1456         }
1457        }
1458       }
1459      }
1460     }
1461    }
1462    return $cidnumber
1463}
1464
1465proc formatDE {cidnumber} {
1466  # https://en.wikipedia.org/wiki/Area_codes_in_Germany
1467
1468  # format for numbers was broken, removed until it can be done correctly
1469  return $cidnumber
1470}
1471
1472proc formatHR {cidnumber} {
1473    # https://en.wikipedia.org/wiki/Telephone_numbers_in_Croatia
1474    if {![regsub {^(01)([0-9]+)} \
1475      $cidnumber {\1-\2} cidnumber]} {
1476      if {![regsub {^(02[0123])([0-9]+)} \
1477        $cidnumber {\1-\2} cidnumber]} {
1478        if {![regsub {^(03[12345])([0-9]+)} \
1479          $cidnumber {\1-\2} cidnumber]} {
1480          if {![regsub {^(04[0234789])([0-9]+)} \
1481            $cidnumber {\1-\2} cidnumber]} {
1482            if {![regsub {^(05[123])([0-9]+)} \
1483              $cidnumber {\1-\2} cidnumber]} {
1484              if {![regsub {^(09[125789])([0-9]+)} \
1485                $cidnumber {\1-\2} cidnumber]} {
1486              }
1487            }
1488          }
1489        }
1490      }
1491    }
1492    return $cidnumber
1493}
1494
1495proc formatFR {cidnumber} {
1496    # http://en.wikipedia.org/wiki/Telephone_numbers_in_France
1497    set nmbrWidth 20
1498    #French national calls
1499    if {![regsub {^(0[1-9])([0-9][0-9])([0-9][0-9])([0-9][0-9])([0-9][0-9])} \
1500      $cidnumber {\1 \2 \3 \4 \5} cidnumber]} {
1501	#international calls (prefix 1* ,2 )
1502	#formats prefix but doesn't format local number
1503      if {![regsub {^(00)(1)([1-9]+)} \
1504        $cidnumber {(+1) \3 } cidnumber]} {
1505      }
1506      if {![regsub {^(00)(2[078])([0-9]+)} \
1507        $cidnumber {(+\2) \3 } cidnumber]} {
1508      }
1509      if {![regsub {^(00)(2[1234569][0-9])([0-9]+)} \
1510        $cidnumber {(+\2) \3 } cidnumber]} {
1511      }
1512      if {![regsub {^(00)(3[012469])([0-9]+)} \
1513        $cidnumber {(+\2) \3 } cidnumber]} {
1514      }
1515      if {![regsub {^(00)(3[578][0-9])([0-9]+)} \
1516        $cidnumber {(+\2) \3 } cidnumber]} {
1517      }
1518	#telemarketing calls with France international prefix
1519	#formats prefix and formats local number to french standard
1520      if {![regsub {^(00)(33)([1-9])([0-9][0-9])([0-9][0-9])([0-9][0-9])([0-9][0-9]+)} \
1521        $cidnumber {(+33) \3 \4 \5 \6 \7} cidnumber]} {
1522      }
1523	#other international calls (prefix 3*,4*,5*,6*,7*,8*,9*)
1524	#formats prefix but doesn't format local number
1525      if {![regsub {^(00)(4[013456789])([0-9]+)} \
1526        $cidnumber {(+\2) \3 } cidnumber]} {
1527      }
1528      if {![regsub {^(00)(4[2][0-9])([0-9]+)} \
1529        $cidnumber {(+\2) \3 } cidnumber]} {
1530      }
1531      if {![regsub {^(00)(5[1345678])([0-9]+)} \
1532        $cidnumber {(+\2) \3 } cidnumber]} {
1533      }
1534      if {![regsub {^(00)(5[09][0-9])([0-9]+)} \
1535        $cidnumber {(+\2) \3 } cidnumber]} {
1536      }
1537      if {![regsub {^(00)(6[013456])([0-9]+)} \
1538        $cidnumber {(+\2) \3 } cidnumber]} {
1539      }
1540      if {![regsub {^(00)(6[789][0-9])([0-9]+)} \
1541        $cidnumber {(+\2) \3 } cidnumber]} {
1542      }
1543      if {![regsub {^(00)(7)([1-9]+)} \
1544        $cidnumber {(+7) \3 } cidnumber]} {
1545      }
1546      if {![regsub {^(00)(8[123469])([0-9]+)} \
1547        $cidnumber {(+\2) \3 } cidnumber]} {
1548      }
1549      if {![regsub {^(00)(8[0578][0-9])([0-9]+)} \
1550        $cidnumber {(+\2) \3 } cidnumber]} {
1551      }
1552      if {![regsub {^(00)(9[0123458])([0-9]+)} \
1553        $cidnumber {(+\2) \3 } cidnumber]} {
1554      }
1555      if {![regsub {^(00)(9[679][0-9])([0-9]+)} \
1556        $cidnumber {(+\2) \3 } cidnumber]} {
1557      }
1558    }
1559    return $cidnumber
1560}
1561
1562proc convertTo12 {hours minutes} {
1563    set AmPm "am"
1564    if {$hours > 12} {
1565        set hours [expr $hours - 12]
1566        set AmPm "pm"
1567    } elseif {$hours == 12} {
1568        set AmPm "pm"
1569    } elseif {$hours == 0} {
1570        set hours 12
1571    }
1572    regsub {^(0|\s|)?(\d)$} $hours { \2} hours
1573    return "$hours:$minutes $AmPm"
1574}
1575
1576proc convertTo24 {hours minutes AmPm} {
1577    if {$hours == 12 && $AmPm eq "am"} {
1578        set hours 0
1579    } elseif {$hours != 12 && $AmPm eq "pm"} {
1580        set hours [expr $hours + 12]
1581    }
1582    regsub {^(0|\s|)?(\d)$} $hours {0\2} hours
1583    return "$hours:$minutes"
1584}
1585
1586# extract field pair where 'dataString' is the field label (NAME, NMBR, etc.)
1587# and $result is the field data
1588proc getField {dataString dataBlock} {
1589  regsub ".*\\*$dataString\\*" $dataBlock {} result
1590
1591  switch $dataString {
1592    RING -
1593    DATE -
1594    TIME {
1595      regsub {(\d+).*} $result {\1} result
1596    }
1597    LINE {
1598      regsub {([\w\s@!-]+)\*.*} $result {\1} result
1599    }
1600    NMBR -
1601    NAME {
1602      regsub {\*DATE\*.*|\*TIME\*.*$|\*LINE\*.*|\*NMBR\*.*|\*MESG\*.*|\*NAME\*.*|\*MTYPE\*.*|\*$} $result "" result
1603    }
1604    MTYPE -
1605    default {
1606      regsub {([\w-]+)\*} $result {\1} result
1607    }
1608  }
1609  return $result
1610}
1611
1612# send the CID information to an external program
1613# Input: $ciddate $cidtime $cidnumber $cidname $cidline $cidtype "" ""
1614proc sendCID {cid} {
1615  global Module ExecSh ModDir WakeUp
1616
1617  set modcid "$cid"
1618  # send DATE\nTIME\nNUMBER\nNAME\nLINE\nTYPE\nMESG\nMTYPE\n
1619  set modtype "using a module"
1620  set modin "[lindex $cid 0]\n[lindex $cid 1]\n[lindex $cid 2]\n[lindex $cid 3]\n[lindex $cid 4]\n[lindex $cid 5]\n[lindex $cid 6]\n[lindex $cid 7]"
1621  if $ExecSh {
1622    catch {exec sh -c $Module << "$modin" >@stdout &} oops
1623  } else {
1624    catch {exec $Module << "$modin" >@stdout &} oops
1625  }
1626  logMsg $::LEVEL1 "$modtype\nSent $Module $modcid"
1627}
1628
1629# pass the message to an external program
1630# input: $msgdate $msgtime $msgnumber $msgname $msgline $msgtype $mtype $msg
1631proc sendMSG {msg} {
1632  global Module ExecSh preClient_1_0
1633
1634  set mesg "$msg"
1635  if $preClient_1_0 {
1636    # send "\n\n\nMESG\n\nTYPE\n"
1637    set modtype "using a preClient 1.0 module for a message"
1638    set modin "[lindex $msg 0]\n[lindex $msg 1]\n[lindex $msg 2]\n[lindex $msg 6]\n[lindex $msg 4]\n[lindex $msg 5]\n[lindex $msg 3]\n"
1639    set mesg [lreplace $mesg 3 3 [lindex $msg 7]]
1640    set mesg [lreplace $mesg 7 7 [lindex $msg 3]]
1641    if $ExecSh {
1642      catch {exec sh -c $Module << "$modin" >@stdout &} oops
1643    } else {
1644      catch {exec $Module << "$modin" >@stdout &} oops
1645    }
1646  } else {
1647    # send "DATE\nTIME\nNMBR\nNAME\nLINE\nTYPE\n\MESG\nMTYPE\n"
1648    set modtype "using a Client 1.0 type module for a message"
1649    set modin "[lindex $msg 0]\n[lindex $msg 1]\n[lindex $msg 2]\n[lindex $msg 3]\n[lindex $msg 4]\n[lindex $msg 5]\n[lindex $msg 7]\n[lindex $msg 6]\n"
1650    set mesg "[list [lindex $msg 0]\n[lindex $msg 1]\n[lindex $msg 2]\n[lindex $msg 3]\n[lindex $msg 4]\n[lindex $msg 5]\n[lindex $msg 7]\n[lindex $msg 6]]\n"
1651    if $ExecSh {
1652      catch {exec sh -c $Module << "$modin" >@stdout &} oops
1653    } else {
1654      catch {exec $Module << "$modin" >@stdout &} oops
1655    }
1656  }
1657  logMsg $::LEVEL1 "$modtype\nSent $Module $mesg"
1658}
1659
1660# display CID information or message
1661# Input: $ciddate $cidtime $cidnumber $cidname $cidline $type "" ""
1662#        $msgdate $msgtime $msgnumber $msgname $msgline $type message msgio
1663# ismsg = 0 for CID and 1 for message
1664proc displayCID {input ismsg} {
1665    global Txt historyTextWidth
1666
1667    if {$ismsg} {
1668       set maxwidth [expr $historyTextWidth - 30]
1669       set firstNewline [string first "\n" $input]
1670       if {($firstNewline == [expr [string length $input] - 1]) && ([string length $input] > $maxwidth)} {
1671          logMsg $::LEVEL2 "Truncating long message of length [string length $input] to fit within width $maxwidth"
1672          set Txt "[string range [string trim $input] 0 $maxwidth](truncated)"
1673       } else {
1674        # string is not too long OR string has embedded new lines already
1675        set Txt $input
1676       }
1677    } else {
1678        set Txt "[lindex $input 3]\n[lindex $input 2]"
1679    }
1680}
1681
1682# display Call Log
1683# Input: $ciddate $cidtime $cidnumber $cidname $cidline $linetype "" ""
1684# Input: $msgdate $msgtime $msgnumber $msgname $msgline $linetype $msgtype message
1685proc displayLog {input ismsg} {
1686    global Module NoGUI
1687    global display_line_num DoingCallLog
1688    global nmbrWidth nameWidth lineIDWidth mtypeWidth
1689    global hup label
1690    global hupColor ltColor dateColor timeColor lineColor nmbrColor nameColor
1691
1692    if $NoGUI {
1693        if {$Module == ""} {
1694            if $ismsg {
1695                if {[lindex $input 1] eq {}} {
1696                    logMsg $::LEVEL1 "[lindex $input 6]: [lindex $input 7]"
1697                } else {
1698                    logMsg $::LEVEL1 "[lindex $input 5]: [lindex $input 0]  [lindex $input 1] [lindex $input 4] [lindex $input 2] [lindex $input 3] [lindex $input 6] [lindex $input 7]"
1699                }
1700            } else {
1701                logMsg $::LEVEL1 "[lindex $input 5]: [lindex $input 0]  [lindex $input 1] [lindex $input 4] [lindex $input 2] [lindex $input 3]"
1702            }
1703        }
1704        incr display_line_num
1705    } else {
1706        # GUI
1707        incr display_line_num
1708        if {! $DoingCallLog} {.vh configure -state normal}
1709        if {[lindex $input 1] eq {}} {
1710        .vh insert end "\n[lindex $input 5]: " lttag [lindex $input 7] msgtag
1711        } else {
1712            set ciddate [lindex $input 0]
1713            set cidtime [lindex $input 1]
1714            set cidnmbr [format "%${nmbrWidth}.${nmbrWidth}s" [lindex $input 2]]
1715            set cidname [format "%-${nameWidth}.${nameWidth}s" [lindex $input 3]]
1716            set cidline [format "%-${lineIDWidth}.${lineIDWidth}s" \
1717                         [lindex $input 4]]
1718            set linetype [lindex $input 5]
1719
1720            if {$label != 6 && $linetype == "HUP"} {
1721
1722                # hup array used to restore normal color to $linetype
1723                set lineid [string trimright $cidline]
1724                array set hup "{$lineid} $display_line_num"
1725
1726                # set HUP active color to $linetype
1727                .vh insert end "\n$linetype: " huptag
1728            } else {
1729                # set theme color to $linetype
1730                .vh insert end "\n$linetype: " lttag
1731            }
1732
1733            .vh insert end "$ciddate "   datetag \
1734                           "$cidtime "   timetag \
1735                           "$cidline "   linetag \
1736                           "$cidnmbr "   nmbrtag \
1737                           "$cidname "   nametag
1738
1739            if $ismsg {
1740                set msgtype [format "%-${mtypeWidth}.${mtypeWidth}s" \
1741                             [lindex $input 6]]
1742                set message [lindex $input 7]
1743                .vh insert end "$msgtype " mttag $message msgtag
1744            }
1745        }
1746        if {! $DoingCallLog} {
1747            if {$display_line_num == 1} {
1748                .vh delete 1.0 2.0
1749            }
1750            .vh yview moveto 1.0
1751            .vh configure -state disabled
1752            if {[lindex [.vh yview] 0] + [lindex [.vh yview] 1] == 1.0} {
1753                grid .ys
1754            }
1755        }
1756    }
1757}
1758
1759#https://www.rosettacode.org/wiki/Word_wrap#Tcl
1760#label widgets don't have a -wrap option so use this
1761proc wrapParagraph {width text} {
1762    regsub -all {\s+} [string trim $text] " " text
1763    set RE "^(.{1,$width})(?:\\s+(.*))?$"
1764    for {set result ""} {[regexp $RE $text -> line text]} {} {
1765	append result $line "\n"
1766    }
1767    return [string trimright $result "\n"]
1768}
1769
1770# Open a connection to the NCID server
1771proc connectCID {} {
1772    global Host Port
1773    global Try Delay
1774    global Socket menuDisabled
1775    global NoGUI
1776    global VersionInfo VersionIDENT HostnameFlag hostname
1777    global Module dtfile
1778    global CallLog
1779
1780    set ServerOptions ""
1781    set Socket 0
1782
1783    if {!$NoGUI} {
1784      if {$::tcl_platform(platform) == "unix"} {
1785        set menu .menubar.file
1786
1787        # enable or disable autostart menu
1788        if ![file isfile $dtfile] {
1789          $menu entryconfigure Auto*Start -state disabled
1790        } else {
1791          $menu entryconfigure Auto*Start -state normal
1792        }
1793      }
1794      set menu .menubar.server
1795    }
1796
1797    logServerAddress
1798    logMsg $::LEVEL3 "Attempting to connect"
1799
1800    while (1) {
1801        # open socket to server
1802        if {[catch {set Socket [socket $Host $Port]} msg]} {
1803            if {!$NoGUI} {
1804                if {$menuDisabled == 0} {
1805                    $menu entryconfigure Reload* -state disabled
1806                    $menu entryconfigure Update*current* -state disabled
1807                    $menu entryconfigure Update*all*call* -state disabled
1808                    $menu entryconfigure Reread* -state disabled
1809                    $menu entryconfigure Dial*Number* -state disabled
1810                    set menuDisabled 1
1811
1812                  # a delay of 1 second causes Reconnect to break ncid
1813                  if {$Delay == 1} {
1814                    .menubar.file entryconfigure Reconnect* -state disabled
1815                  }
1816                }
1817            }
1818            set Try [expr $Try + 1]
1819            tryCount "$Host:$Port - $msg\n"
1820        } else {
1821            # set socket to non-blocking
1822            fconfigure $Socket -blocking 0
1823            # get response from server as an event
1824            fileevent $Socket readable getCID
1825
1826            if {!$NoGUI && $menuDisabled} {
1827                $menu entryconfigure Reload* -state normal
1828                $menu entryconfigure Update*current* -state normal
1829                $menu entryconfigure Update*all*call* -state normal
1830                $menu entryconfigure Reread* -state normal
1831                set menuDisabled 0
1832
1833              if {$Delay == 1} {
1834                .menubar.file entryconfigure Reconnect* -state normal
1835              }
1836            }
1837
1838            puts $Socket "HELLO: IDENT: $VersionIDENT"
1839            flush $Socket
1840            logMsg $::LEVEL1 "HELLO: IDENT: $VersionIDENT"
1841            if $NoGUI {
1842                logMsg $::LEVEL1 "Connected to $Host:$Port"
1843                if $CallLog {
1844                    # tell server to send call log
1845                    puts $Socket "HELLO: CMD: log"
1846                    flush $Socket
1847                    logMsg $::LEVEL1 "Sent: HELLO: CMD: log"
1848                } else {
1849                    # tell server to not send call log
1850                    puts $Socket "HELLO: CMD: no_log"
1851                    flush $Socket
1852                    logMsg $::LEVEL1 "Sent: HELLO: CMD: no_log"
1853                }
1854            } else {
1855                clearLog
1856                displayCID "Connected to\n$Host:$Port" 1
1857                logMsg $::LEVEL1 "Connected to $Host:$Port"
1858            }
1859        break
1860        }
1861    }
1862}
1863
1864# valid option with argument examples: -v 1 | -v1 | --verbose 1 | --verbose=1
1865# combined options are not handled, for example: -AcHXW
1866proc handleOptArg {} {
1867    global Usage Opt OptArg OptCnt
1868
1869    set gotarg 0
1870    if {[regexp {^--} $Opt]} {
1871        set len [string length $Opt]
1872        set pos [string first = $Opt]
1873        if {$pos != -1 && $len > $pos} {
1874            set OptArg [string range $Opt [expr $pos + 1] $len]
1875            set Opt [string range $Opt 0 [expr $pos - 1]]
1876        } else {incr OptCnt}
1877    } else {
1878        if {[string length $Opt] > 2} {
1879            # single letter option combined with argument
1880            set OptArg [string range $Opt 2 [string length $Opt]]
1881            set Opt [string range $Opt 0 1]
1882        } else {incr OptCnt}
1883   }
1884
1885   if {$OptArg == ""} {exitMsg 6 "Missing $Opt argument\n$Usage\n"}
1886}
1887
1888proc getArg {} {
1889    # note: debug log has not been opened yet so logMsg is unavailable
1890    global Opt OptArg OptCnt OptPmsg delayedMsgs
1891    global Host Port Hosts HostIndex oldHost oldPort SelectedLineIDs
1892    global Delay
1893    global Usage
1894    global NoGUI
1895    global Verbose
1896    global LogEnable LogDir
1897    global Module ModDir Ring CallOnRing
1898    global HostnameFlag
1899    global PIDfile
1900    global PopupTime
1901    global NoExit
1902    global AltDate
1903    global WakeUp
1904    global Version
1905    global WrapLines
1906    global CallLog
1907    global NightMode Country
1908
1909    set showUsage 0
1910    set hostport 0
1911
1912    for {set OptCnt 0} {$OptCnt < $::argc} {incr OptCnt} {
1913        set OptArg [lindex $::argv [expr $OptCnt + 1]]
1914        switch -regexp -- [set Opt [lindex $::argv $OptCnt]] {
1915            {^-r} -
1916            {^--ring$|^--ring=} {
1917                handleOptArg
1918                if {[regexp {^-[129]$} $OptArg]
1919                    || [regexp {^[0123456789]$} $OptArg]} {
1920                    set Ring $OptArg
1921                    set CallOnRing 1
1922                } else {exitMsg 4 "Invalid $Opt argument: $OptArg\n$Usage\n"}
1923            }
1924            {^--no-gui$} {set NoGUI 1}
1925            {^--night-mode$} {set NightMode 1}
1926            {^-A$} -
1927            {^--alt-date$} {set AltDate 1}
1928            {^-c$} -
1929            {^--call-log$} {set CallLog 1}
1930            {^-C} -
1931            {^--country-code$|^--country-code=} {
1932                handleOptArg
1933                checkCountry $OptArg
1934                set Country $OptArg
1935            }
1936            {^-D} -
1937            {^--delay$|^--delay=} {
1938                handleOptArg
1939                if {[regexp {^[0-9]+$} $OptArg]} {
1940                    set Delay $OptArg
1941                } else {exitMsg 4 "Invalid $Opt argument: $OptArg\n$Usage\n"}
1942            }
1943            {^-h$} -
1944            {^--help$} {set showUsage 1; set NoGUI 1}
1945            {^-H$} -
1946            {^--hostname-flag$} {set HostnameFlag 1}
1947            {^-l} -
1948            {^--log-enable$|^--log-enable=} {
1949                handleOptArg
1950                if {[regexp {^[0-2]+$} $OptArg]} {
1951                    set LogEnable $OptArg
1952                } else {exitMsg 4 "Invalid $Opt argument: $OptArg\n$Usage\n"}
1953            }
1954            {^-L} -
1955            {^--log-dir$|^--log-dir=} {
1956                handleOptArg
1957                if {$OptArg == ""} {
1958                    exitMsg 4 "Invalid $Opt argument: $OptArg\n$Usage\n"}
1959                set LogDir $OptArg
1960            }
1961            {^-m} -
1962            {^--module$|^--module=} -
1963            {^-P$} -
1964            {^--program$} {
1965                if {$Opt == "-P" || $Opt == "--program"} {
1966                    set delayedMsgs \
1967                    "$delayedMsgs\n***** WARNING: option -P|--program is deprecated, use option -m|--module"
1968                }
1969                handleOptArg
1970                if {[regexp {^.*/} $OptArg]} {
1971                    set Module [list $OptArg]
1972                } else {set Module [list $ModDir/$OptArg]}
1973            }
1974            {^-p} -
1975            {^--pidfile$|^--pidfile=} {
1976                handleOptArg
1977                if {[regexp {^[\w./]+} $OptArg]} {
1978                    set PIDfile $OptArg
1979                } else {exitMsg 4 "Invalid $Opt argument: $OptArg\n$Usage\n"}
1980            }
1981            {^-t} -
1982            {^--PopupTime$^--PopupTime=} {
1983                handleOptArg
1984                if {[regexp {^[0-5]$} $OptArg]} {
1985                    set PopupTime $OptArg
1986                } else {exitMsg 4 "Invalid $Opt argument: $OptArg\n$Usage\n"}
1987            }
1988            {^-v} -
1989            {^--verbose$|--verbose=} {
1990                handleOptArg
1991                if {[regexp {^[1-9]+$} $OptArg]} {
1992                    set Verbose $OptArg
1993                } else {exitMsg 4 "Invalid $Opt argument: $OptArg\n$Usage\n"}
1994            }
1995            {^-V$} -
1996            {^--version$} {set NoGUI 1; puts "ncid (NCID) $Version"; exit 0}
1997            {^-X$} -
1998            {^--noexit} {set NoExit 1}
1999            {^-W$} -
2000            {^--wakeup$} {set WakeUp 1}
2001            {^-.*$} {set NoGUI 1; exitMsg 5 "Unknown option: $Opt\n$Usage\n"}
2002            {^\d+$} {set Port $Opt; set hostport 1}
2003            {^.+$} {set Host $Opt; set hostport 1}
2004            default {set NoGUI 1; exitMsg 5 "Unknown option: $Opt\n$Usage\n"}
2005        }
2006    }
2007
2008    if {$showUsage} {exitMsg 1 "$Usage\n"}
2009
2010    if {$hostport} {
2011        set SelectedLineIDs ""
2012        if {[regexp {:} $Host]} {
2013            set Hosts $Host
2014            lassign [split $Hosts ":"] Host Port
2015        } else {
2016            set Hosts "$Host:$Port"
2017        }
2018        set delayedMsgs \
2019            "$delayedMsgs\nncidd address temporarily changed to $Host:$Port"
2020    }
2021    set oldHost ""
2022    set oldPort ""
2023}
2024
2025# ttk Widgets: http://wiki.tcl-lang.org/14796
2026# https://tkdocs.com/tutorial/styles.html
2027# Styles and themes: http://www.tkdocs.com/tutorial/styles.html#using
2028# Changing ttk Widget Colors: https://wiki.tcl.tk/37973
2029#   A collection of all the information on setting the
2030#   colors of modern widgets in one place.
2031#
2032# sets styles for Day, Night, and themes (if needed)
2033proc setStyles {} {
2034  global ThemeName fgColor bckColor mtColor bckSelColor fgSelColor
2035  global hupColor dateColor timeColor lineColor nmbrColor nameColor
2036  global tvFgColor  tvBckColor \
2037  global vhFgColor  vhBckColor vhInsColor
2038
2039  set enabletheme 0
2040  set m .menubar
2041  set items "file file.auto file.startup server server.hosts server.dial \
2042             server.copy view view.types view.lines prefs theme help"
2043
2044  switch $ThemeName {
2045    night {
2046      set fgColor "yellow"
2047      set fg2Color "white"
2048      set fg3Color "cyan"
2049      set fgActColor "white"
2050      set fgSelColor "red"
2051      set bckColor "#262626"
2052      set bckActColor "#215d9c"
2053      set bckSelColor "#262626"
2054      set bck2SelColor "blue"
2055      set bckFldColor "#0f0f00"
2056      set bckFldActColor "#215d9c"
2057      set roColor "#0f0f00"
2058      set arrowColor "white"
2059      set arrowActColor "black"
2060      set indColor "black"
2061      set indSelColor "white"
2062      set indPressColor "white"
2063      set truColor "#3c3c3c"
2064      set selColor "white"
2065      set insColor "white"
2066
2067      # text colors
2068      set tvFgColor  "yellow"
2069      set tvBckColor "#262626"
2070      set vhFgColor  "white"
2071      set vhBckColor "#262626"
2072      set vhInsColor "white"
2073
2074      # history window column colors
2075      .vh tag configure huptag  -foreground white   -selectbackground blue
2076      .vh tag configure lttag   -foreground #7fff7f -selectbackground blue
2077      .vh tag configure datetag -foreground yellow  -selectbackground blue
2078      .vh tag configure timetag -foreground cyan    -selectbackground blue
2079      .vh tag configure linetag -foreground #7fff7f -selectbackground blue
2080      .vh tag configure nmbrtag -foreground yellow -selectbackground blue
2081      .vh tag configure nametag -foreground cyan   -selectbackground blue
2082      .vh tag configure mttag   -foreground yellow -selectbackground blue
2083      .vh tag configure msgtag  -foreground white  -selectbackground blue
2084    }
2085    day {
2086      set fgColor "blue"
2087      set fg2Color "black"
2088      set fg3Color "green"
2089      set fgActColor "white"
2090      set fgSelColor "red"
2091      set bckColor "#d9d9d9"
2092      set bckActColor "#4a90d9"
2093      set bckSelColor "#d9d9d9"
2094      set bck2SelColor "blue"
2095      set bckFldColor "#f0f0ff"
2096      set bckFldActColor "#4a90d9"
2097      set roColor "#f0f0ff"
2098      set arrowColor "black"
2099      set arrowActColor "white"
2100      set indColor "#d9d9d9"
2101      set indSelColor "black"
2102      set indPressColor "black"
2103      set truColor "#c9c9c9"
2104      set selColor "black"
2105      set insColor "black"
2106
2107      #text colors
2108      set tvBckColor "white"
2109      set tvFgColor  "blue"
2110      set vhBckColor "white"
2111      set vhFgColor  "black"
2112      set vhInsColor "white"
2113
2114      # history window column colors
2115      .vh tag configure huptag  -foreground black   -selectbackground lightgreen
2116      .vh tag configure lttag   -foreground purple  -selectbackground lightgreen
2117      .vh tag configure datetag -foreground blue    -selectbackground lightgreen
2118      .vh tag configure timetag -foreground red     -selectbackground lightgreen
2119      .vh tag configure linetag -foreground purple  -selectbackground lightgreen
2120      .vh tag configure nmbrtag -foreground blue    -selectbackground lightgreen
2121      .vh tag configure nametag -foreground red     -selectbackground lightgreen
2122      .vh tag configure mttag   -foreground blue    -selectbackground lightgreen
2123      .vh tag configure msgtag  -foreground black   -selectbackground lightgreen
2124    }
2125    default {
2126      set enabletheme 1
2127
2128      set fgColor "black"
2129      set fg2Color "black"
2130      set fg3Color "black"
2131      set fgActColor "black"
2132      set fgSelColor "black"
2133      set bckColor "#d9d9d9"
2134      set bckActColor "#d9d9d9"
2135      set bckSelColor "#d9d9d9"
2136      set bck2SelColor "blue"
2137      set bckFldColor "#f0f0ff"
2138      set bckFldActColor "#d9d9d9"
2139      set roColor "#f0f0ff"
2140      set arrowColor "black"
2141      set arrowActColor "black"
2142      set indColor "#d9d9d9"
2143      set indSelColor "black"
2144      set indPressColor "black"
2145      set truColor "#c9c9c9"
2146      set selColor "black"
2147      set insColor "black"
2148
2149      #text colors
2150      set tvBckColor "white"
2151      set tvFgColor  "black"
2152      set vhBckColor "white"
2153      set vhFgColor  "black"
2154      set vhInsColor "white"
2155
2156      # history window column colors
2157      .vh tag configure huptag  -foreground black -selectbackground #d9d9d9
2158      .vh tag configure lttag   -foreground black -selectbackground #d9d9d9
2159      .vh tag configure datetag -foreground black -selectbackground #d9d9d9
2160      .vh tag configure timetag -foreground black -selectbackground #d9d9d9
2161      .vh tag configure linetag -foreground black -selectbackground #d9d9d9
2162      .vh tag configure nmbrtag -foreground black -selectbackground #d9d9d9
2163      .vh tag configure nametag -foreground black -selectbackground #d9d9d9
2164      .vh tag configure mttag   -foreground black -selectbackground #d9d9d9
2165      .vh tag configure msgtag  -foreground black -selectbackground #d9d9d9
2166    }
2167  }
2168  .tv configure -background $tvBckColor -foreground $tvFgColor \
2169                -selectbackground $tvBckColor -selectforeground $tvFgColor
2170  .vh configure -background $vhBckColor -foreground $vhFgColor \
2171                -insertbackground $vhInsColor
2172  $m configure -background $bckColor -foreground $fg2Color
2173  foreach item $items {
2174    $m.$item configure -background $bckColor -foreground $fg2Color \
2175      -selectcolor $selColor
2176  }
2177if {$::tcl_platform(platform) == "unix"} {
2178    ttk::style configure TButton \
2179      -background $bckColor \
2180      -foreground $fg2Color
2181    ttk::style map TButton \
2182      -background [list active $bckActColor] \
2183      -foreground [list active $fgActColor]
2184  }
2185  ttk::style configure TEntry \
2186    -foreground $fg3Color \
2187    -fieldbackground $bckFldColor \
2188    -selectbackground $bckSelColor \
2189    -selectforeground $fgSelColor \
2190    -insertcolor $insColor
2191  ttk::style configure TFrame \
2192    -background $bckColor
2193  ttk::style configure TLabel \
2194    -background $bckColor \
2195    -foreground $fgColor
2196  ttk::style configure TLabelframe \
2197    -background $bckColor \
2198    -foreground $fg2Color
2199  ttk::style configure TSpinbox \
2200    -arrowsize 15 \
2201    -arrowcolor $arrowColor \
2202    -background $bckColor \
2203    -foreground $fg2Color \
2204    -selectbackground $bck2SelColor
2205  ttk::style map TSpinbox \
2206    -background [list active $bckActColor] \
2207    -fieldbackground [list active $bckFldColor readonly $roColor] \
2208    -relief [list {pressed !disabled} sunken]
2209  ttk::style configure TRadiobutton \
2210    -indicatorcolor $indColor \
2211    -background $bckColor \
2212    -foreground $fgColor
2213  ttk::style map TRadiobutton \
2214    -indicatorcolor [list selected $indSelColor pressed $indPressColor] \
2215    -background [list active $bckActColor] \
2216    -foreground [list active $fgActColor]
2217  ttk::style configure TCheckbutton \
2218    -indicatorcolor $indColor \
2219    -background $bckColor \
2220    -foreground $fgColor
2221  ttk::style map TCheckbutton \
2222    -indicatorcolor [list selected $indSelColor pressed $indPressColor] \
2223    -background [list active $bckActColor]
2224  ttk::style configure TScrollbar \
2225    -arrowsize 15 \
2226    -arrowcolor $arrowColor \
2227    -background $bckColor \
2228    -troughcolor $truColor
2229  ttk::style map TScrollbar \
2230    -arrowcolor [list active $arrowActColor] \
2231    -background [list active $bckActColor] \
2232    -relief [list {pressed !disabled} sunken]
2233  ttk::style configure TCombobox \
2234    -arrowsize 15 \
2235    -arrowcolor $arrowColor \
2236    -background $bckColor \
2237    -foreground $fgColor \
2238    -fieldbackground $bckColor \
2239    -selectbackground $bckColor \
2240    -selectforeground $fgColor
2241  ttk::style map TCombobox \
2242    -arrowcolor [list active $arrowActColor] \
2243    -background [list active $bckActColor] \
2244    -relief [list {pressed !disabled} sunken]
2245  . configure -background $bckColor
2246
2247  if $enabletheme {
2248    #ttk::style theme use $ThemeName
2249    ttk::setTheme $ThemeName
2250  } else {
2251    # Day and Night styles modify the default theme
2252    ttk::setTheme "default"
2253  }
2254}
2255
2256proc processRCfile {} {
2257    global rcfile ExitOn fontList PortableDir wmGeometry
2258    global autoSave oldAutoSave autoStart oldAutoStart
2259    global clock oldClock AltDate oldAltDate DateSepar oldDateSepar
2260    global ThemeName oldThemeName delayedMsgs
2261    global TypeGroups oldTypeGroups SelectedTypes oldSelectedTypes
2262    global LineIDGroups oldLineIDGroups SelectedLineIDs oldSelectedLineIDs
2263    global Host Port Hosts HostIndex Leading1 oldLeading1
2264    global DefaultHost DefaultPort ConfigFileHost ConfigFilePort
2265
2266    if [expr [file exists $rcfile] && [file isfile $rcfile]] {
2267        set id [open $rcfile]
2268        set data [read $id]
2269        close $id
2270    } else {
2271        set data "no data"
2272    }
2273
2274    set lines [split $data "\n"]
2275
2276    foreach line $lines {
2277        if [regexp {geometry\s+\S+\s+[0-9x]+} $line] {
2278            eval $line
2279            set wmGeometry [regsub {\w+\s+\w+\s+\S+\s+([\dx+]+)} $line {\1}]
2280        } elseif [regexp {font\s+create} $line] {
2281            eval $line
2282        } elseif [regexp {(:?fontList|clock|AltDate|DateSepar|NightMode|\
2283                             autoSave|autoStart|TypeGroups|SelectedTypes|\
2284                             LineIDGroups|SelectedLineIDs|ThemeName|\
2285                             Leading1|Host|Port|\
2286                             )\s+} $line] {
2287            eval $line
2288        }
2289    }
2290
2291    # initial values, not final values
2292    set oldHost $Host
2293    set oldPort $Port
2294
2295    # determine Host, Port and Hosts values
2296    if {$ConfigFileHost == ""} {
2297        if {$Host == ""} {set Host $DefaultHost}
2298    } else {
2299        set Host $ConfigFileHost
2300    }
2301    if {$ConfigFilePort == ""} {
2302        if {$Port == ""} {set Port $DefaultPort}
2303    } else {
2304        set Port $ConfigFilePort
2305    }
2306
2307    # host and port variables may have been replaced by the .ncid RC file
2308    # make sure there is a match against the list of hosts
2309    if {$Hosts == ""} {
2310        set Hosts "$DefaultHost:$DefaultPort"
2311        set delayedMsgs \
2312            "$delayedMsgs\nempty Hosts list set to $DefaultHost:$DefaultPort"
2313        set HostIndex 0
2314        lassign [split [lindex $Hosts $HostIndex] ":"] Host Port
2315        write_rc_file "set Host" "set Host $Host"
2316        write_rc_file "set Port" "set Port $Port"
2317    } else {
2318        set HostIndex [lsearch -regexp $Hosts $Host:$Port]
2319        if {$HostIndex == -1} {
2320            # the config file changed since the rcfile was last updated
2321            set delayedMsgs \
2322                "$delayedMsgs\n$Host:$Port not in list of Hosts: \"$Hosts\""
2323            set HostIndex 0
2324            lassign [split [lindex $Hosts $HostIndex] ":"] Host Port
2325            set delayedMsgs \
2326                "$delayedMsgs\nUsing first entry in list of hosts: $Host:$Port"
2327        }
2328        write_rc_file "set Host" "set Host $Host"
2329        write_rc_file "set Port" "set Port $Port"
2330    }
2331
2332    if {$Host != $oldHost} {
2333        # the RC file does not contain "set Host"
2334        write_rc_file "set Host" "set Host $Host"
2335    }
2336
2337    if {$Port != $oldPort} {
2338        # the RC file does not contain "set Port"
2339        write_rc_file "set Port" "set Port $Port"
2340    }
2341
2342    # remove NightMode from rcfile and add ThemeName to rcfile
2343    if {[info exists NightMode]} {
2344        if {$NightMode == 1} {set ThemeName "night"} else {set ThemeName "day"}
2345        write_rc_file "set ThemeName" "set ThemeName \"$ThemeName\""
2346        write_rc_file "" "set NightMode"
2347        set delayedMsgs \
2348            "$delayedMsgs\nNightMode \"$NightMode\" in rc file converted to ThemeName \"$ThemeName\""
2349    }
2350
2351    set oldClock $clock
2352    set oldAltDate $AltDate
2353    set oldDateSepar $DateSepar
2354    set oldLeading1 $Leading1
2355    set oldThemeName $ThemeName
2356    set oldAutoSave $autoSave
2357    set oldAutoStart $autoStart
2358    set oldTypeGroups $TypeGroups
2359    set oldSelectedTypes $SelectedTypes
2360    set oldLineIDGroups $LineIDGroups
2361    set oldSelectedLineIDs $SelectedLineIDs
2362}
2363
2364proc logRCfileOldNewVarChange {verboseLevel oldVarName newVarName} {
2365
2366     # prints "rcfile and $newVarName..."
2367     upvar $oldVarName oldVarValue
2368     upvar $newVarName newVarValue
2369     if {$oldVarValue != $newVarValue} {
2370        if {[string length $oldVarValue] >30 || [string length $newVarValue] >30} {
2371           logMsg $verboseLevel "rcfile and $newVarName have been changed"
2372           logMsg $verboseLevel "    from: $oldVarValue"
2373           logMsg $verboseLevel "      to: $newVarValue"
2374        } else {
2375           logMsg $verboseLevel "rcfile and $newVarName have been changed from: $oldVarValue to: $newVarValue"
2376        }
2377     }
2378}
2379
2380proc do_nothing {} {
2381}
2382
2383proc do_goodbye {} {
2384    global Socket
2385
2386    if {$Socket > 0} {puts $Socket "GOODBYE"}
2387}
2388
2389proc makeWindow {} {
2390    global ExitOn
2391    global Verbose WrapLines viewTextWidth
2392    global fontList m currentFont
2393    global clock autoSave autoStart
2394    global DateSepar YearDot
2395    global Hosts HostIndex
2396    global nameREQ nmbrREQ lineREQ historyTextWidth timeWidth
2397    global lbl1 lbl2 lbl3 lbl4 lbl5 lbl6 lbl7 lbl8
2398    global wmGeometry ThemeName
2399    global TypeGroups LineIDGroups DiscoveredLineIDs
2400
2401    wm title . "Network Caller ID"
2402    wm protocol . WM_DELETE_WINDOW $ExitOn
2403
2404    set auto [expr \"$autoSave\" eq \"off\" ? \"normal\" : \"disabled\"]
2405
2406    if {$fontList == ""} {scanFonts}
2407
2408    if {[catch {font configure FixedFontH}]} {
2409        font create FixedFontH -family "$currentFont" -size 12
2410        write_rc_file "FixedFontH" \
2411                "font create FixedFontH [font configure FixedFontH]"
2412    }
2413    if {[catch {font configure FixedFontM}]} {
2414        font create FixedFontM -family "$currentFont" -size 12
2415        write_rc_file "FixedFontM" \
2416                "font create FixedFontM [font configure FixedFontM]"
2417    }
2418    if {[catch {font configure FixedFontP}]} {
2419        font create FixedFontP -family "$currentFont" -size 12
2420                write_rc_file "FixedFontP" \
2421                "font create FixedFontP [font configure FixedFontP]"
2422    }
2423
2424    ttk::style configure TButton -font FixedFontP
2425    ttk::style configure TCheckbutton -font FixedFontP
2426    ttk::style configure TRadiobutton -font FixedFontP
2427
2428    # menu options: no tearoff and help menu on far right
2429    option add *tearOff 0
2430    option add *Menu.useMotifHelp 1
2431    option add *Text.relief sunken
2432    option add *Text.borderWidth 2
2433    option add *highlightThickness 1
2434#
2435    # create menubar
2436    menu .menubar
2437    . configure -menu .menubar
2438
2439    # create and place: column labels
2440    if {$clock == 24 && $DateSepar == "."} {set lbl $lbl1}
2441    if {$clock == 24 && $DateSepar == "-"} {set lbl $lbl2}
2442    if {$clock == 24 && $DateSepar == "/"} {set lbl $lbl3}
2443    if {$clock == 12 && $DateSepar == "."} {set lbl $lbl4}
2444    if {$clock == 12 && $DateSepar == "-"} {set lbl $lbl5}
2445    if {$clock == 12 && $DateSepar == "/"} {set lbl $lbl6}
2446    if {$clock == 24 && $DateSepar == "." && $YearDot == 1} {set lbl $lbl7}
2447    if {$clock == 12 && $DateSepar == "." && $YearDot == 1} {set lbl $lbl8}
2448    ttk::label .la -text $lbl -justify left -font {FixedFontH}
2449    grid .la -row 1 -sticky w -columnspan 2
2450
2451    # create File, Server, Preferences, View, Theme and Help menus
2452    set m .menubar
2453    menu $m.file
2454    menu $m.file.auto
2455    menu $m.file.startup
2456    menu $m.server
2457    menu $m.server.hosts
2458    menu $m.server.dial
2459    menu $m.server.copy
2460    menu $m.prefs
2461    menu $m.view
2462    menu $m.view.types
2463    menu $m.view.lines
2464    menu $m.theme
2465    menu $m.help
2466    $m add cascade -menu $m.file -label File -underline 0 -font FixedFontM
2467    $m add cascade -menu $m.server -label Server -underline 0 -font FixedFontM
2468    $m add cascade -menu $m.prefs -label Preferences -underline 0 -font FixedFontM
2469    $m add cascade -menu $m.view -label View -underline 0 -font FixedFontM
2470    $m add cascade -menu $m.theme -label Themes -underline 0 -font FixedFontM
2471    $m add cascade -menu $m.help -label Help -underline 0 -font FixedFontM
2472
2473    # create File menu items
2474    $m.file add command -label "Clear Log" -command clearLog -font FixedFontM
2475    $m.file add command -label "Reconnect" -command Reconnect -font FixedFontM
2476    $m.file add separator
2477    if {$::tcl_platform(platform) == "unix"} {
2478    $m.file add cascade -menu $m.file.startup -label "Auto Start" -font FixedFontM
2479    $m.file add separator
2480  }
2481    $m.file add cascade -menu $m.file.auto -label "Auto Save" -font FixedFontM
2482    $m.file add command -label "Save Size" -state $auto -command {saveSize 0} -font FixedFontM
2483    $m.file add command -label "Save Size and Position" -state $auto -command {saveSize 1} -font FixedFontM
2484    $m.file add separator
2485    $m.file add command -label Quit -command {do_goodbye; exit} -font FixedFontM
2486
2487    $m.file.auto add radiobutton -label "Size" -variable autoSave -value "size" -command {logAuto $m.file} -font FixedFontM
2488    $m.file.auto add radiobutton -label "Size and Position" -variable autoSave -value "both" -command {logAuto $m.file} -font FixedFontM
2489    $m.file.auto add radiobutton -label "Off" -variable autoSave -value "off" -command {logAuto $m.file} -font FixedFontM
2490
2491    $m.file.startup add radiobutton -label "On" -variable autoStart -value "on" -command {logStart} -font FixedFontM
2492    $m.file.startup add radiobutton -label "On with desktop notifications" -variable autoStart -value "on+alert" -command {logStart} -font FixedFontM
2493    $m.file.startup add radiobutton -label "On only desktop notifications" -variable autoStart -value "alert" -command {logStart} -font FixedFontM
2494    $m.file.startup add radiobutton -label "Off" -variable autoStart -value "off" -command {logStart} -font FixedFontM
2495
2496    # create Server menu items
2497    $m.server add cascade -menu $m.server.hosts -label "Select NCID Server" -state normal   -font FixedFontM
2498    set x 0
2499    foreach i $Hosts {
2500        $m.server.hosts add radiobutton -label "$i" -variable HostIndex \
2501            -value $x -command {logHosts} -font FixedFontM
2502        incr x
2503    }
2504    $m.server add separator
2505    $m.server add command -label "Reload alias, blacklist and whitelist files" -command {
2506                Disable $m
2507                puts $Socket "REQ: RELOAD"
2508                flush $Socket
2509            } -font FixedFontM
2510    $m.server add command -label "Update current call log" -command {
2511                global multi
2512
2513                Disable $m
2514                puts $Socket "REQ: UPDATE"
2515                flush $Socket
2516                set multi 0
2517            } -font FixedFontM
2518    $m.server add command -label "Update all call logs" -command {
2519                global multi
2520
2521                Disable $m
2522                puts $Socket "REQ: UPDATES"
2523                flush $Socket
2524                set multi 1
2525            } -font FixedFontM
2526    $m.server add command -label "Reread call log" -command {
2527                global display_line_num
2528
2529                set display_line_num 0
2530                clearLog
2531                puts $Socket "REQ: REREAD"
2532                flush $Socket
2533            } -font FixedFontM
2534    $m.server add separator
2535    $m.server add cascade -menu $m.server.dial -label "Dial Number" -state disabled -font FixedFontM
2536    $m.server.dial add command -label "Manually" -command { doDial "any" } -font FixedFontM
2537    $m.server.dial add command -label "From History" -state disabled -command { doDial "history" } -font FixedFontM
2538    $m.server add separator
2539    $m.server add command -label "Add/Modify/Remove Alias in Alias File" -state disabled -command { doList alias "" "" } -font FixedFontM
2540    $m.server add separator
2541    $m.server add command -label "Add to Blacklist File" -state disabled -command {
2542                doList black add ""
2543            } -font FixedFontM
2544    $m.server add command -label "Remove from Blacklist File" -state disabled -command {
2545                global argument
2546                doList black remove $argument
2547            } -font FixedFontM
2548    $m.server add separator
2549    $m.server add command -label "Add to Whitelist File" -state disabled -command {
2550                doList white add ""
2551            } -font FixedFontM
2552    $m.server add command -label "Remove from Whitelist File" -state disabled -command {
2553                global argument
2554                doList white remove $argument
2555            } -font FixedFontM
2556    $m.server add separator
2557    $m.server add cascade -menu $m.server.copy -label "Copy to Clipboard" -state disabled -font FixedFontM
2558    $m.server.copy add command -label "Name" -command { doClipboard 4 [lindex $dataDump 16] } -font FixedFontM
2559    $m.server.copy add command -label "Number Formatted" -command { doClipboard 1 [lindex $dataDump 13] } -font FixedFontM
2560    $m.server.copy add command -label "Number Digits" -command { doClipboard 2 [lindex $dataDump 13] } -font FixedFontM
2561    $m.server.copy add command -label "Entire Line" -command { doClipboard 3 $dataDump } -font FixedFontM
2562
2563    # create Preferences menu items
2564    $m.prefs add command -label "Font..." -command {changeFont} -font FixedFontM
2565    $m.prefs add separator
2566    $m.prefs add command -label "Date and Time..." -command {formatDT} -font FixedFontM
2567
2568    # create View menu items
2569    $m.view add cascade -menu $m.view.types -label "Line Types" -font FixedFontM
2570    $m.view add cascade -menu $m.view.lines -label "LineIDs" -font FixedFontM
2571
2572    $m.view.types add radiobutton -label "All" \
2573        -variable TypeGroups -value 0 \
2574        -command {logTypeGroups} -font FixedFontM
2575    $m.view.types add separator
2576    $m.view.types add radiobutton -label "Calls" \
2577        -variable TypeGroups -value 1 \
2578        -command {logTypeGroups} -font FixedFontM
2579    $m.view.types add radiobutton -label "Messages" \
2580        -variable TypeGroups -value 2 \
2581        -command {logTypeGroups} -font FixedFontM
2582    $m.view.types add radiobutton -label "Smart Phone" \
2583        -variable TypeGroups -value 3 \
2584        -command {logTypeGroups} -font FixedFontM
2585    $m.view.types add separator
2586    $m.view.types add radiobutton -label "Select..." \
2587        -variable TypeGroups -value 4 \
2588        -command {doSelectedTypes} -font FixedFontM
2589
2590    $m.view.lines add radiobutton -label "All" \
2591        -variable LineIDGroups -value 0 \
2592              -command {logViewLineIDs} -font FixedFontM
2593    $m.view.lines add separator
2594    $m.view.lines add radiobutton -label "Select..." \
2595        -variable LineIDGroups -value 1 \
2596        -command {doLineIDs} -font FixedFontM
2597
2598    # create themes menu items
2599    $m.theme add radiobutton -label "Day" \
2600        -variable ThemeName -value "day" \
2601        -command {logTheme} -font FixedFontP
2602    $m.theme add radiobutton -label "Night" \
2603        -variable ThemeName -value "night" \
2604        -command {logTheme} -font FixedFontP
2605    $m.theme add radiobutton -label "Default" \
2606        -variable ThemeName -value "default" \
2607        -command {logTheme} -font FixedFontP
2608    if {$::DistThemes ne "default"} {$m.theme add separator}
2609
2610    #Observed values
2611    #Windows 10:         alt clam classic default    vista winnative xpnative
2612    #Mac:                alt clam classic default    aqua
2613    #AndroWish:          alt clam classic default    droid
2614    #Fedora 25 cinnamon: alt clam classic default
2615    foreach t $::DistThemes {
2616        if {$t eq "default"} {continue}
2617        $m.theme add radiobutton -label [join [list [string totitle $t]]] \
2618        -variable ThemeName -value $t \
2619        -command {logTheme} -font FixedFontP
2620    }
2621
2622    if {$::AddonThemes != ""} {
2623        $m.theme add separator
2624        foreach t $::AddonThemes {
2625            $m.theme add radiobutton -label [join [list [string totitle $t]]] \
2626            -variable ThemeName -value $t \
2627            -command {logTheme} -font FixedFontP
2628        }
2629    }
2630
2631    # create Help menu item
2632    $m.help add command -label About -command {helpItem "About" $About [ncidInfo]} -font FixedFontM
2633    $m.help add command -label "Online Docs" -command {helpItem "Online Documentation" "" ""} -font FixedFontM
2634    $m.help add command -label "Field Labels" -command {helpItem "Field Labels" $fieldList ""} -font FixedFontM
2635    $m.help add command -label "Line Types" -command {helpItem "Line Types" $labList ""} -font FixedFontM
2636    $m.help add command -label "Views Window" -command {helpItem "Views Window" $viewHelp ""} -font FixedFontM
2637    $m.help add command -label "Server Menu" -command {helpItem "Server Menu Help" $serverHelp ""} -font FixedFontM
2638    $m.help add command -label "Server Options" -command serverOPT -font FixedFontM
2639
2640    # create and place: CID history scroll window
2641    if {$clock == 12} {set timeWidth 8}
2642    set historyTextWidth [expr $historyTextWidth + $timeWidth]
2643    text .vh -width $historyTextWidth -height 4 -yscrollcommand ".ys set" \
2644        -state disabled -font {FixedFontH} -setgrid 1 -wrap $WrapLines
2645    grid [ttk::scrollbar .ys -command ".vh yview"] \
2646          -column 1 -row 0 -sticky ns -pady 10 -padx 5
2647    grid .vh -row 2 -sticky nsew -padx 2 -pady 2
2648    grid .ys -row 2 -column 1 -sticky ns -pady 2
2649
2650    # create and place: window for view, user message
2651    ttk::frame .fr
2652    grid .fr -row 4 -columnspan 2
2653    text .tv -font {FixedFontM} -height 2 -insertwidth 0 -wrap word \
2654        -width $viewTextWidth -yscrollcommand {.vsb set}
2655    .tv tag add strike 1.end
2656    .tv tag add blank 1.end
2657    .tv tag configure strike -overstrike 1
2658    grid .tv -row 3 -columnspan 2
2659    grid [ttk::scrollbar .vsb -command ".tv yview"] \
2660          -column 1 -row 0 -sticky ns -pady 10 -padx 5
2661    grid .vsb -row 3 -column 1
2662    grid remove .vsb
2663    bind .tv <KeyPress> break
2664    if {$DiscoveredLineIDs != ""} {updateViewDisplay}
2665
2666    ttk::label .ml -text "Send Message: " -font {FixedFontM}
2667    ttk::entry .im -width 40 -font {FixedFontM}
2668    grid .ml .im -in .fr
2669
2670    # create and place: call and server message display
2671    ttk::label .md -textvariable Txt -font {FixedFontM}
2672    grid .md -row 5 -columnspan 2
2673
2674    grid columnconfigure . 0 -weight 1
2675    grid rowconfigure . 2 -weight 1
2676
2677    set geometry [wm grid .]
2678    wm minsize . [lindex $geometry 0] [lindex $geometry 1]
2679    update
2680
2681    switch $autoSave {
2682        "size" {
2683            $m.file entryconfigure Quit -command {do_goodbye; saveSize 0; exit}
2684            wm protocol . WM_DELETE_WINDOW {saveSize 0; $ExitOn}
2685        }
2686        "both" {
2687            $m.file entryconfigure Quit -command {do_goodbye; saveSize 1; exit}
2688            wm protocol . WM_DELETE_WINDOW {saveSize 1; $ExitOn}
2689        }
2690    }
2691    setStyles
2692
2693    wm geometry . $wmGeometry
2694
2695    logMsg $::LEVEL4 "History window font: \
2696        [regsub {\s+\-slant.+$} [font configure FixedFontH] {}]"
2697
2698    logMsg $::LEVEL4 "Message window and display font: \
2699        [regsub {\s+\-slant.+$} [font configure FixedFontM] {}]"
2700
2701    logMsg $::LEVEL4 "Help text: \
2702        [regsub {\s+\-slant.+$} [font configure FixedFontP] {}]"
2703
2704    logMsg $::LEVEL4 "Window geometry set to: \
2705        [regsub {(\d+x\d+)\+(\d+)\+(\d+)} [wm geometry .] {\1 at x=\2 y=\3}]"
2706
2707    bind . <Configure> {
2708            if {[lindex [.vh yview] 0] + [lindex [.vh yview] 1] == 1.0} {
2709                grid remove .ys
2710            } else {grid .ys}
2711    }
2712    bind .fr  <Button-1> {
2713        Disable $m
2714    }
2715    bind .ml  <Button-1> {
2716        Disable $m
2717    }
2718    bind .md  <Button-1> {
2719        Disable $m
2720    }
2721    bind .vh <ButtonRelease-1> {
2722        .vh tag remove sel 1.0 end
2723        set first [.vh index @%x,%ylinestart]
2724        set last [.vh index @%x,%ylineend]
2725        .vh tag add sel $first $last
2726        .vh mark unset anchor
2727        .vh mark unset tk::anchor1
2728        .vh mark set insert 1.0
2729        .vh mark set current 1.0
2730        set dataDump [.vh dump -text $first $last]
2731        set select_label [string trimright [lindex $dataDump 1]]
2732        set lineREQ [string trimright [lindex $dataDump 10]]
2733        set nmbrREQ [string trimright [lindex $dataDump 13]]
2734        set nmbrREQ [string trimleft $nmbrREQ]
2735        set nameREQ [string trimright [lindex $dataDump 16]]
2736        set selected [.vh get $first $last]
2737        set nmbrREQ [regsub -all -- {[-,]} $nmbrREQ ""]
2738        if {$nameREQ eq ""} {
2739            logMsg $::LEVEL1 "$select_label nameREQ is null"
2740            set menu .menubar.server
2741            $menu entryconfigure Add*Alias* -state disabled
2742            if {!$NoGUI} {
2743                $menu entryconfigure *Blacklist* -state disabled
2744                $menu entryconfigure *Whitelist* -state disabled
2745            }
2746        } else {
2747            if {!$Try} {
2748                puts $Socket "REQ: INFO $nmbrREQ&&$nameREQ&&$lineREQ"
2749                flush $Socket
2750                logMsg $::LEVEL1 "REQ: INFO $nmbrREQ&&$nameREQ&&$lineREQ"
2751            } else { logMsg "Server not connected for a REQ: INFO" $::LEVEL1}
2752        }
2753        break
2754    }
2755}
2756
2757proc Disable {menu} {
2758    .vh tag remove sel 1.0 end
2759    set last [$menu.server index last]
2760    set found 0
2761    for {set index 0} {$index <= $last} {incr index} {
2762        set type [$menu.server type $index]
2763        # do not disable menu items before the 3rd separator
2764        if {$found == 3} {
2765            if {$type ne "separator"} {
2766                $menu.server entryconfigure $index -state disabled
2767            }
2768        } elseif {$type eq "separator"} { set found [expr $found + 1] }
2769    }
2770    # need to disable this submenu from not disabled menu entry "Dial Number"
2771    .menubar.server.dial entryconfigure From*History* -state disabled
2772}
2773
2774proc remove {menu block} {
2775    set last [$menu index last]
2776    set found [expr $block == 0 ? 1 : 0]
2777    for {set index 0} {$index <= $last} {incr index} {
2778        set type [$menu type $index]
2779        if {$found} {
2780            $menu delete $index
2781            set index [expr $index - 1]
2782            if {$type eq "separator"} {
2783                break
2784            }
2785        } elseif {$type eq "separator"} {
2786            set block [expr $block - 1]
2787            set found [expr $block == 0 ? 1 : 0]
2788            continue
2789        }
2790    }
2791}
2792
2793proc doRPLY {} {
2794    global ClientJobResult bckColor
2795
2796    toplevel .rply -background $bckColor
2797    wm title .rply "Dial Reply"
2798    wm resizable .rply 0 0
2799
2800    grid [ttk::label .rply.mesg -font FixedFontP -justify left -textvariable ClientJobResult ] -columnspan 2 -padx 12
2801
2802    grid [ttk::button .rply.ok -text "Close" -command { destroy .rply}] \
2803          -columnspan 2 -pady 10
2804
2805    modal {.rply}
2806}
2807
2808proc doDial {dialtype} {
2809  global nmbrREQ nameREQ lineREQ DialPrefix DialLineID ClientJobResult
2810  global bckColor fgColor bckSelColor fgSelColor
2811  global Country nmbrWidth wantdial
2812
2813  toplevel .dial -background $bckColor
2814  wm title .dial "Dial"
2815  wm resizable .dial 0 0
2816  if {$DialPrefix == ""} {set dprefix "no"} else {set dprefix $DialPrefix}
2817  if {$dialtype == "history"} {
2818    set wantdial 1
2819    set ht "Request the server dial:\n\nDial: $dprefix prefix\nNmbr: $nmbrREQ\nName: $nameREQ\nLine: $lineREQ"
2820  } else {
2821    set wantdial 2
2822    set nameREQ "No Name"
2823    set ht "Request the server dial:\n\nDial: $dprefix prefix\nName: $nameREQ"
2824  }
2825  set row 1
2826  grid [ttk::label .dial.rd -justify left -font FixedFontP \
2827        -text $ht ] \
2828        -columnspan 2 -padx 12 -pady 10
2829  grid [ttk::label .dial.cl -justify left -font FixedFontP \
2830        -text "Server Dial Line:"] -columnspan 2 -padx 12 -column 0
2831  set lineid_list [list $::ServerOptLineIDS]
2832  set DialLineID [lindex $lineid_list 0]
2833  grid [ttk::combobox .dial.lb -font FixedFontP -values $lineid_list \
2834        -height 2 -textvariable DialLineID] -columnspan 2 -column 0
2835  .dial.lb set $DialLineID
2836  set row [expr $row + 2]
2837  if {$dialtype == "history"} {
2838    ttk::label .dial.mml -font FixedFontM -text "Leading 1 in number"
2839    grid [ttk::labelframe .dial.lf -labelwidget .dial.mml -labelanchor "n"] \
2840          -columnspan 2 -column 0 -pady 5 -padx 40
2841    if {$Country == "US"} {
2842      grid [ttk::radiobutton .dial.lf.one -text "Add" \
2843            -variable Leading1 -value "Add" -command logOne] \
2844            -row 0 -column 0 -padx 8
2845      grid [ttk::radiobutton .dial.lf.noone -text "Remove" \
2846            -variable Leading1 -value "Remove" -command logOne] \
2847            -row 0 -column 1 -padx 8
2848      grid [ttk::radiobutton .dial.lf.leave -text "Leave" \
2849            -variable Leading1 -value "Leave" -command logOne] \
2850            -row 0 -column 2 -padx 8
2851    }
2852  } else {
2853    grid [ttk::label .dial.num -justify left -font FixedFontP \
2854          -text "Dial Number:"] -columnspan 2 -padx 12 -column 0
2855    ttk::entry .dial.number -width $nmbrWidth -font FixedFontP
2856    grid .dial.number -sticky ew -columnspan 2 -padx 8
2857    focus .dial.number
2858  }
2859  set row [expr $row + 1]
2860  grid [ttk::frame .dial.fr] -pady 10 -columnspan 2
2861  incr row
2862  grid [ttk::label .dial.fr.lab1 -font {FixedFontM} -text "Status:"] -padx 3
2863  incr row
2864  grid [ttk::label .dial.fr.lab2 -font {FixedFontM} \
2865        -textvariable ClientJobResult] -column 0 -padx 3
2866  incr row
2867  grid [ttk::button .dial.cancel -text "Cancel" \
2868        -command {destroy .dial}] -row $row -pady 12
2869  grid [ttk::button .dial.call -text "Call" \
2870        -command {DoIt "" "DIAL" "$DialPrefix[logOne]" "$nameREQ" $wantdial}] \
2871        -row $row -column 1
2872  grid [ttk::button .dial.abort -text "ABORT" \
2873      -command {DoIt "" "DIAL_ABORT" "$DialPrefix[logOne]" "$nameREQ" $wantdial} \
2874      -state disabled] \
2875      -pady 10 -row $row -column 1
2876  grid [ttk::button .dial.close -text "Close" -command {
2877        Disable .menubar
2878        destroy .dial} \
2879        -state disabled] -columnspan 2 -row $row -pady 10
2880  grid remove .dial.close .dial.abort
2881  set ClientJobResult "Waiting for user action..."
2882  modal {.dial}
2883}
2884
2885proc doAliasType {} {
2886    global nameREQ nmbrREQ lineREQ AliasText SelAliasType
2887    global action_ replace_ alias_action line_action
2888
2889    switch $SelAliasType {
2890        NAMEDEP {
2891            set action_ $alias_action
2892            set replace_ $nameREQ
2893            set AliasText "Replace NAME: $nameREQ\nif      NMBR: $nmbrREQ\nwith ALIAS entered below"
2894            if {$action_ eq "modify" } {
2895                append AliasText ",\n or clear it to remove it"
2896                incr row
2897            }
2898        }
2899        NMBRDEP {
2900            set action_ $alias_action
2901            set replace_ $nmbrREQ
2902            set AliasText "Replace NMBR: $nmbrREQ\nif      NAME: $nameREQ\nwith ALIAS entered below"
2903            if {$action_ eq "modify" } {
2904                append AliasText ",\n or clear it to remove it"
2905                incr row
2906            }
2907        }
2908        NAMEONLY {
2909            set action_ $alias_action
2910            set replace_ $nameREQ
2911            if {$action_ eq "modify" } {
2912                set AliasText "Replace NAME alias: $nameREQ\nwith ALIAS entered below"
2913                append AliasText ",\n or clear it to remove it"
2914                incr row
2915            } else {
2916                set AliasText "Replace NAME: $nameREQ\nwith ALIAS entered below"
2917            }
2918        }
2919        NMBRONLY {
2920            set action_ $alias_action
2921            set replace_ $nmbrREQ
2922            if {$action_ eq "modify" } {
2923                set AliasText "Replace NMBR alias: $nmbrREQ\nwith ALIAS entered below"
2924                append AliasText ",\n or clear it to remove it"
2925                incr row
2926            } else {
2927                set AliasText "Replace NMBR: $nmbrREQ\nwith ALIAS entered below"
2928            }
2929        }
2930        NMBRNAME {
2931            set action_ $alias_action
2932            set replace_ $nmbrREQ
2933            if {$action_ eq "modify" } {
2934                set AliasText "Replace NMBRNAME alias: $nameREQ\nand         NMBR: $nmbrREQ\nwith ALIAS entered below"
2935                append AliasText ",\n or clear it to remove it"
2936                incr row
2937            } else {
2938                set AliasText "Replace NAME: $nameREQ\nand     NMBR: $nmbrREQ\nwith ALIAS entered below"
2939            }
2940        }
2941        LINEONLY {
2942            set action_ $line_action
2943            set replace_ $lineREQ
2944            if {$action_ eq "modify" } {
2945                set AliasText "Replace LINE alias: $lineREQ\nwith ALIAS entered below"
2946                append AliasText ",\n or clear it to remove it"
2947                incr row
2948            } else {
2949                set AliasText "Replace LINE: $lineREQ\nwith ALIAS entered below"
2950            }
2951        }
2952    }
2953    append AliasText ".\n"
2954    .confirm.lab configure -text $AliasText
2955}
2956
2957proc doList {list action which} {
2958    global entry_ action_ list_ ClientJobResult replace_ comment_ from_
2959    global aliasList aliasTypes CIDaliasType LineAliasType SelAliasType
2960    global nameREQ nmbrREQ lineREQ AliasText alias_action line_action
2961    global nameWidth bckColor fgColor bckSelColor fgSelColor
2962
2963    toplevel .confirm -background $bckColor
2964    wm title .confirm "Confirmation"
2965    wm resizable .confirm 0 0
2966    set action_ $action
2967    set list_ $list
2968    set comment_ ""
2969    set from_ $nameREQ
2970
2971    if {$list eq "black" || $list eq "white"} {
2972        set entry [list "$nmbrREQ" "$nameREQ"]
2973        set entry_ [lindex $entry 0]
2974        if {[lindex $entry 1] eq "NO NAME" || $nameREQ eq $nmbrREQ} {
2975            set entry [lreplace $entry 1 1]
2976        }
2977
2978        if {$action eq "add"} {
2979            set _entry [join $entry "\" or \""]
2980        } elseif {$which eq "name"} {
2981            set entry_ [set _entry [lindex $entry 1]]
2982            set entry ""
2983        } else {
2984            set entry_ [set _entry [lindex $entry 0]]
2985            set entry ""
2986        }
2987        set _action [string toupper $action 0 0]
2988        set prep [expr {$action} eq "{add}" ? "{to}" : "{from}"]
2989        grid [ttk::label .confirm.lab -font FixedFontP -text "$_action \"$_entry\"\n$prep the server's ${list}list"] \
2990                -columnspan 2 -padx 12 -pady 10
2991        if {[llength $entry] == 2} {
2992            grid [ttk::radiobutton .confirm.rb1 -text [lindex $entry 0] \
2993                  -variable entry_ \
2994                  -value [lindex $entry 0]] -pady 5 -columnspan 2
2995            grid [ttk::radiobutton .confirm.rb2 -text [lindex $entry 1] \
2996                  -variable entry_ \
2997                  -value [lindex $entry 1]] -pady 5 -columnspan 2
2998            set row 3
2999        } else {
3000            set row 1
3001        }
3002        if {$action eq "add"} {
3003            grid [ttk::label .confirm.lab1 -font FixedFontP -text "Comment:"] -padx 12 -sticky w
3004            ttk::entry .confirm.entry -width $nameWidth -font FixedFontP
3005            grid .confirm.entry -sticky ew -columnspan 2 -padx 8
3006            focus .confirm.entry
3007            set row [expr $row + 2]
3008        }
3009    } else {
3010        grid [ttk::label .confirm.list -justify left -font FixedFontP -text "NAME: $nameREQ\nNMBR: $nmbrREQ\nLINE: $lineREQ\n\nChoose the alias type:"] -columnspan 2 -padx 12 -pady 10
3011        grid [listbox .confirm.lb -font FixedFontP -listvariable aliasList \
3012            -selectmode browse -height 0 -width 0 \
3013            -background $bckColor -foreground $fgColor \
3014            -selectbackground $bckSelColor -selectforeground $fgSelColor] \
3015            -columnspan 2
3016        if {$CIDaliasType eq "NOALIAS"} {
3017            set aliasList $aliasTypes
3018            set alias_action "add"
3019            set replace_ ""
3020        } else {
3021            set aliasList "$CIDaliasType LINEONLY"
3022            set alias_action "modify"
3023        }
3024        if {$LineAliasType eq "NOALIAS"} {
3025            set line_action "add"
3026            set replace_ $lineREQ
3027        } else {
3028            set line_action "modify"
3029            set replace_ $lineREQ
3030        }
3031        .confirm.lb selection set 0
3032        set SelAliasType [.confirm.lb get [.confirm.lb curselection]]
3033        if {$SelAliasType == "LINEONLY"} {set from_ $lineREQ}
3034        set AliasText ""
3035        focus .confirm.lb
3036        grid [ttk::label .confirm.lab -justify left -font FixedFontP -text $AliasText] \
3037         -columnspan 2 -padx 12 -pady 10
3038        ttk::entry .confirm.entry -exportselection 0 -font FixedFontP
3039        set aliaswidth [lindex $::aliasWidths \
3040                       [lsearch $aliasTypes $SelAliasType]]
3041        .confirm.entry configure -width $aliaswidth
3042        grid .confirm.entry -columnspan 2 -padx 12 -pady 10
3043        set row 4
3044        doAliasType
3045        append AliasText ".\n"
3046        if {$action_ eq "modify"} {
3047            .confirm.entry insert 0 "$replace_"
3048        }
3049    }
3050
3051    bind all <<ListboxSelect>> {
3052        global SelAliasType
3053
3054        if {[.confirm.lb curselection] eq ""} {break}
3055        set SelAliasType [.confirm.lb get [.confirm.lb curselection]]
3056        .confirm.entry configure -width [lindex $::aliasWidths \
3057            [lsearch $::aliasTypes $SelAliasType]]
3058        focus .confirm.entry
3059        doAliasType
3060        append AliasText ".\n"
3061        .confirm.entry delete 0 end
3062        if {$action_ eq "modify"} {
3063            .confirm.entry insert end "$replace_"
3064        }
3065    }
3066    grid [ttk::frame .confirm.fr] -pady 10 -columnspan 2 -row $row
3067    incr row
3068    grid [ttk::label .confirm.fr.lab1 -font FixedFontP -text "Status:"] -padx 3
3069    grid [ttk::label .confirm.fr.lab2 -font FixedFontP -textvariable ClientJobResult] -column 0 -row 1 -padx 3
3070    grid [ttk::button .confirm.cancel -text "Cancel" -command {destroy .confirm}] -pady 10
3071
3072    if {$list eq "alias"} {
3073        grid [ttk::button .confirm.ok -text "Apply" -command {
3074           global ClientJobResult
3075           set ClientJobResult ""
3076           set replace_ [.confirm.entry get]
3077           set replace_ [string trim $replace_]
3078           if {$replace_ eq ""} {
3079              set ClientJobResult "You must enter something\nwhen adding a new alias."
3080              continue
3081           }
3082           set aliaswidth [lindex $::aliasWidths \
3083                          [lsearch $::aliasTypes $::SelAliasType]]
3084           .confirm.entry configure -width $aliaswidth
3085           if {[string length $replace_] > $aliaswidth} {
3086              .confirm.entry delete $::nameWidth end
3087              set replace_ [.confirm.entry get]
3088              set replace_ [string trim $replace_]
3089              set ClientJobResult "Alias length truncated to fit\nwindow:  edit Cancel or Apply"
3090              continue
3091           }
3092           DoIt $action_ $list_ $nmbrREQ&&$replace_ "$SelAliasType&&$from_" 0}] \
3093          -pady 10 -row $row -column 1
3094    } else {
3095        grid [ttk::button .confirm.ok -text "Apply" -command {
3096                if {$action_ eq "add"} {
3097                    set comment_ [.confirm.entry get]; \
3098                    set comment_ [string trim $comment_]; \
3099                }; \
3100                DoIt $action_ $list_ $entry_ $comment_ 0}] \
3101                -pady 10 -row $row -column 1
3102    }
3103
3104    incr row
3105    grid [ttk::button .confirm.close -text "Close"  -command {
3106            Disable .menubar
3107            destroy .confirm} \
3108             -state disabled ] -columnspan 2 -row $row -pady 10
3109    grid remove .confirm.close
3110    set ClientJobResult "Waiting for user action..."
3111    modal .confirm
3112}
3113
3114proc DoIt {action list entry extra wantdial} {
3115    global Socket  ClientJobResult Try Dialed DialLineID
3116
3117    set Dialed $wantdial
3118    if {$Try} {
3119        set ClientJobResult "Server not connected ..."
3120        logMsg $::LEVEL1 "Server not connected for a REQ:"
3121        return
3122    }
3123    if {$Dialed > 0} {
3124        if {$Dialed == 2} {set entry [regsub -all {[^\d]} [.dial.number get] ""]}
3125        if {$list == "DIAL_ABORT"} {set Dialed 2}
3126        grid forget .dial.cancel .dial.call
3127        grid configure .dial.close -columnspan 1
3128        grid .dial.abort .dial.close
3129        puts $Socket "REQ: $list $entry&&$extra&&$DialLineID"
3130        flush $Socket
3131        logMsg $::LEVEL1 "REQ: $list $entry&&$extra&&$DialLineID"
3132    } else {
3133        set ClientJobResult "Working ..."
3134        grid forget .confirm.cancel .confirm.ok
3135        grid .confirm.close
3136        puts $Socket "REQ: $list $action \"$entry\" \"$extra\""
3137        flush $Socket
3138        logMsg $::LEVEL1 "REQ: $list $action \"$entry\" \"$extra\""
3139    }
3140}
3141
3142proc doClipboardPopup {title text} {
3143
3144  global ClipboardPopupCount ClipboardPopupButton ClipboardPopupTime
3145  global historyTextWidth bckColor
3146
3147  toplevel .cbp -background $bckColor
3148  wm withdraw .cbp
3149
3150  wm title .cbp $title
3151  wm resizable .cbp 0 0
3152
3153  set dismissBegin [clock clicks -milliseconds]
3154  set ClipboardPopupCount $ClipboardPopupTime
3155
3156  if {$ClipboardPopupTime > 0} {after 1000 {ClipboardPopupLoop}}
3157  set wrapwidth [expr $historyTextWidth - 30]
3158  if {[string length $text] > $wrapwidth} {
3159    logMsg $::LEVEL2 "Wrapping text of length [string length $text] to width $wrapwidth"
3160    set text [wrapParagraph $wrapwidth $text]
3161  }
3162
3163  updateClipboardPopupButton
3164
3165  set row 3
3166  grid [ttk::label .cbp.icon -image ::tk::icons::information ] \
3167       -column 1 -padx 12 -pady 10 -row $row
3168  grid [ttk::label .cbp.text -font FixedFontP  -text $text ] \
3169       -column 2 -padx 12 -pady 10 -row $row
3170
3171  incr row
3172  incr row
3173
3174  grid [ttk::button .cbp.ok -textvariable ClipboardPopupButton -command {
3175        destroy .cbp}] \
3176       -column 2 -pady 10 -pady 10 -row $row
3177
3178    if {$::tcl_platform(platform) != "unix"} {
3179      if {[catch {tk::PlaceWindow .cbp widget .} msg]} { logMsg $::LEVEL1 $msg}
3180    }
3181
3182  wm deiconify .cbp
3183  modal {.cbp}
3184
3185}
3186
3187proc doClipboard {flag passedData} {
3188
3189    # flag = 1 = copy phone number as shown
3190    #        2 = copy phone number digits only
3191    #        3 = copy entire line
3192    #        4 = copy name as shown
3193
3194    if {$flag != 3} {set passedData [string trim $passedData]}
3195
3196    clipboard clear
3197    switch $flag {
3198        1 -
3199        4 {
3200            clipboard append $passedData
3201        }
3202        2 {
3203            regsub -all -line {[^\d]} $passedData {} passedData
3204            clipboard append $passedData
3205        }
3206        3 {
3207            set templine ""
3208            for {set x 1} {$x < [ llength $passedData]} {incr x 3} {
3209                set tempfield [ lindex $passedData $x ]
3210                set templine "$templine$tempfield"
3211            }
3212            set passedData [string trim $templine]
3213            regsub -all -line {  +} $passedData { } passedData
3214            clipboard append $passedData
3215        }
3216    }
3217    logMsg $::LEVEL2 "Copied to clipboard: $passedData"
3218    doClipboardPopup "Selected History Line" "Copied to clipboard: $passedData"
3219}
3220
3221proc updateClipboardPopupButton {} {
3222    global ClipboardPopupCount ClipboardPopupTime ClipboardPopupButton
3223
3224    if {$ClipboardPopupTime == 0} {
3225       set ClipboardPopupButton "OK"
3226    } else {
3227      set ClipboardPopupButton "Dismiss or wait $ClipboardPopupCount second"
3228      if {$ClipboardPopupCount != 1} {append ClipboardPopupButton "s" }
3229    }
3230
3231}
3232
3233proc ClipboardPopupLoop {} {
3234     global ClipboardPopupCount ClipboardPopupButton
3235     incr ClipboardPopupCount -1
3236     updateClipboardPopupButton
3237
3238     if {[winfo exists .cbp]} {
3239        if {$ClipboardPopupCount <= 0 } {
3240           .cbp.ok invoke
3241        } else {
3242          after 1000 {ClipboardPopupLoop}
3243        }
3244     }
3245
3246}
3247
3248proc doSelectedTypes {} {
3249    global labBLK labCID labHUP labMSG labMWI labNOT labOUT labPID labPUT labRID labWID
3250    global SelectedTypes oldSelectedTypes TypeGroups oldTypeGroups
3251    global t_array msgToUser bckColor
3252
3253    # convert lists to arrays
3254    array set t_array $SelectedTypes
3255
3256    set maxwidth [expr [string length $labRID] + 2]
3257
3258    set msgToUser ""
3259
3260    toplevel .ctypes -background $bckColor
3261    wm title .ctypes "Select Call and Message Types to View"
3262    wm resizable .ctypes 0 0
3263
3264    set row 3
3265
3266    grid [ttk::label .ctypes.calls -font FixedFontP \
3267          -text "Call Types"] \
3268         -columnspan 2 -padx 12 -pady 10 -row $row
3269    incr row
3270    incr row
3271
3272    grid [ttk::frame .ctypes.fr1] -pady 10 -columnspan 1
3273    grid [ttk::checkbutton .ctypes.fr1.blk \
3274              -text                $labBLK \
3275              -variable        t_array(BLK) -width $maxwidth] -sticky w -pady 5 -column 1 -row $row
3276    incr row
3277
3278    grid [ttk::checkbutton .ctypes.fr1.cid \
3279              -text                $labCID \
3280              -variable        t_array(CID) -width $maxwidth] -sticky w -pady 5 -column 1 -row $row
3281    incr row
3282
3283    grid [ttk::checkbutton .ctypes.fr1.hup \
3284              -text                $labHUP \
3285              -variable        t_array(HUP) -width $maxwidth] -sticky w -pady 5 -column 1 -row $row
3286    incr row
3287
3288    grid [ttk::checkbutton .ctypes.fr1.mwi \
3289              -text                $labMWI \
3290              -variable        t_array(MWI) -width $maxwidth] -sticky w -pady 5 -column 1 -row $row
3291    incr row
3292
3293    grid [ttk::checkbutton .ctypes.fr1.out \
3294              -text                $labOUT \
3295              -variable        t_array(OUT) -width $maxwidth] -sticky w -pady 5 -column 1 -row $row
3296    incr row
3297
3298    grid [ttk::checkbutton .ctypes.fr1.pid \
3299              -text                $labPID \
3300              -variable        t_array(PID) -width $maxwidth] -sticky w -pady 5 -column 1 -row $row
3301    incr row
3302
3303    grid [ttk::checkbutton .ctypes.fr1.put \
3304              -text                $labPUT \
3305              -variable        t_array(PUT) -width $maxwidth] -sticky w -pady 5 -column 1 -row $row
3306    incr row
3307
3308    grid [ttk::checkbutton .ctypes.fr1.rid \
3309              -text                $labRID \
3310              -variable        t_array(RID) -width $maxwidth] -sticky w -pady 5 -column 1 -row $row
3311    incr row
3312
3313    grid [ttk::checkbutton .ctypes.fr1.wid \
3314              -text                $labWID \
3315              -variable        t_array(WID) -width $maxwidth] -sticky w -pady 5 -column 1 -row $row
3316    incr row
3317    incr row
3318
3319    grid [ttk::label .ctypes.msgs -font FixedFontP \
3320          -text "Message Types"] \
3321         -columnspan 2 -padx 12 -pady 10 -row $row
3322    incr row
3323    incr row
3324
3325    grid [ttk::frame .ctypes.fr2] -pady 10 -columnspan 1
3326    grid [ttk::checkbutton .ctypes.fr2.msg \
3327              -text                $labMSG \
3328              -variable        t_array(MSG) -width $maxwidth] -sticky w -pady 5 -column 1 -row $row
3329    incr row
3330
3331    grid [ttk::checkbutton .ctypes.fr2.not \
3332              -text                $labNOT \
3333              -variable        t_array(NOT) -width $maxwidth] -sticky w -pady 5 -column 1 -row $row
3334    incr row
3335    incr row
3336    incr row
3337    incr row
3338
3339    grid [ttk::label .ctypes.msgtouser -font FixedFontP \
3340          -textvariable msgToUser] \
3341         -columnspan 2 -padx 12 -pady 10 -row $row
3342
3343    grid [ttk::frame .ctypes.fr3]  -column 0 -pady 8
3344    grid [ttk::button .ctypes.fr3.selall -text "Select All" \
3345          -command {array set t_array {BLK 1 CID 1 HUP 1 MWI 1 OUT 1 \
3346                          PID 1 PUT 1 RID 1 WID 1 MSG 1 NOT 1}
3347               }] -padx 12 -pady 10 -row $row
3348
3349    grid [ttk::button .ctypes.fr3.clrall -text "Clear All" -command {
3350               array set t_array {BLK 0 CID 0 HUP 0 MWI 0 OUT 0 PID 0 PUT 0 RID 0 WID 0 \
3351                                  MSG 0 NOT 0}
3352               }] -column 1 -padx 12 -pady 10 -row $row
3353
3354    grid [ttk::button .ctypes.fr3.cancel -text "Cancel" -command {
3355               set TypeGroups $oldTypeGroups
3356               destroy .ctypes}] \
3357          -column 2 -padx 12 -pady 10 -row $row
3358
3359    grid [ttk::button .ctypes.fr3.apply -text "Apply" -command {
3360               global t_array msgToUser
3361               set sum 0
3362               foreach {name value} [array get t_array] {set sum [expr $sum + $value]}
3363               if {!$sum} {
3364                  set msgToUser "You must select at least one type"
3365                  logMsg $::LEVEL1 $msgToUser
3366                  continue
3367               }
3368               # then convert array to list
3369               set SelectedTypes [lsort -stride 2 [array get t_array]]
3370               destroy .ctypes
3371               #logTypeGroups causes call log to be re-read
3372               logTypeGroups}] \
3373          -column 3 -padx 12 -pady 10 -row $row
3374
3375    modal {.ctypes}
3376}
3377
3378proc doLineIDs {} {
3379  global DiscoveredLineIDs LineIDGroups oldLineIDGroups
3380  global SelectedLineIDs oldSelectedLineIDs
3381  global lineREQ
3382  global SelLINE msgToUser SelHistoryLINE SelHistoryLINEIndex
3383  global bckColor fgColor bckSelColor fgSelColor
3384
3385  set msgToUser ""
3386  set SelLINE   [list]
3387
3388  toplevel .clines -background $bckColor
3389  wm title .clines "Select Line Identifiers to View"
3390  wm resizable .clines 0 0
3391
3392  set oldSelectedLineIDs $SelectedLineIDs
3393
3394  set row 3
3395
3396  set viewselected $SelectedLineIDs
3397  set SelectedLineIDs ""
3398  set SelectedLineIDsIndex ""
3399
3400  foreach vs $viewselected {
3401    set index [lsearch -exact "$DiscoveredLineIDs" $vs]
3402    if {$index != -1} {
3403      lappend SelectedLineIDs $vs
3404      lappend SelectedLineIDsIndex $index
3405    }
3406  }
3407
3408  if {$SelectedLineIDsIndex == ""} {set SelectedLineIDsIndex -1}
3409
3410  # see if user selected a line in call history window
3411  set SelHistoryLINE ""
3412  set SelHistoryLINEIndex 0
3413  if {[info exists lineREQ]} {
3414    if {$lineREQ != "" } {
3415      set SelHistoryLINEIndex [lsearch -exact $DiscoveredLineIDs $lineREQ]
3416      if {$SelHistoryLINEIndex == -1} {
3417        set SelHistoryLINEIndex 0
3418      } else {set SelHistoryLINE $lineREQ}
3419    }
3420  }
3421
3422  if {[llength $DiscoveredLineIDs] > 0} {
3423    grid [listbox .clines.lb -font FixedFontP -height 4 -width 0 \
3424          -listvariable DiscoveredLineIDs -selectmode extended \
3425          -yscrollcommand {.clines.ys set} \
3426          -background $bckColor -foreground $fgColor \
3427          -selectbackground $bckSelColor -selectforeground $fgSelColor] \
3428          -columnspan 2
3429    grid [ttk::scrollbar .clines.ys -command {.clines.lb yview}] \
3430          -column 1 -row 0 -sticky nse -pady 1 -padx 1
3431    if {[.clines.lb index end] <= [lindex [.clines.lb configure -height] 4]} {
3432      grid remove .clines.ys
3433    } else {
3434      grid .clines.ys
3435    }
3436    if {$SelectedLineIDsIndex != -1} {
3437      foreach x $SelectedLineIDsIndex {
3438        .clines.lb selection set $x
3439      }
3440      set SelLINE {}
3441      foreach x [.clines.lb curselection] {
3442        lappend SelLINE [.clines.lb get $x]
3443      }
3444    }
3445
3446    bind all <<ListboxSelect>> {
3447      if {[info commands .clines.lb] ne ""} {
3448        set SelLINE {}
3449        foreach x [.clines.lb curselection] {
3450          lappend SelLINE [.clines.lb get $x]
3451        }
3452        if {[lindex [.clines.lb yview] 0] + \
3453            [lindex [.clines.lb yview] 1] == 1.0} {
3454          grid remove .clines.ys
3455        } else {
3456          grid .clines.ys
3457        }
3458      }
3459    }
3460  }
3461  set row [expr [llength $DiscoveredLineIDs] + 2]
3462
3463  if {[llength $DiscoveredLineIDs] > 1} {
3464    grid [ttk::label .clines.help -justify left -font FixedFontP \
3465          -text "Multiple selections are allowed."] \
3466          -columnspan 2 -padx 12 -pady 10 -row $row
3467    incr row
3468  }
3469
3470  if {$SelHistoryLINE != ""} {
3471    incr row
3472    incr row
3473    grid [ttk::label .clines.call -justify left -font FixedFontP \
3474          -text "Last selected call in history window:"] \
3475          -columnspan 2 -padx 12 -pady 10 -row $row
3476    incr row
3477    grid [ttk::button .clines.history -text "LineID: $SelHistoryLINE" -command {
3478          global SelHistoryLINE SelHistoryLINEIndex SelLINE
3479          .clines.lb selection clear 0 end
3480          .clines.lb selection set $SelHistoryLINEIndex
3481          .clines.lb curselection
3482          set SelLINE $SelHistoryLINE
3483          logMsg $::LEVEL4 "Selected index $SelHistoryLINEIndex for SelHistoryLINE=$SelHistoryLINE"
3484          }] \
3485          -columnspan 2 -pady 10 -row $row
3486    incr row
3487    incr row
3488  }
3489
3490  grid [ttk::label .clines.msgtouser -font FixedFontP \
3491        -textvariable msgToUser] \
3492        -columnspan 2 -padx 12 -pady 10 -row $row
3493  incr row
3494  incr row
3495
3496  grid [ttk::button .clines.cancel -text "Cancel" -command {
3497        global DiscoveredLineIDs LineIDGroups oldLineIDGroups
3498        set LineIDGroups $oldLineIDGroups
3499        logMsg $::LEVEL4 "Cancel: DiscoveredLineIDs contains $DiscoveredLineIDs"
3500        destroy .clines}] \
3501        -padx 12 -row $row
3502
3503  grid [ttk::button .clines.apply -text "Apply" -command {
3504        global DiscoveredLineIDs LineIDGroups oldLineIDGroups
3505        global SelectedLineIDs oldSelectedLineIDs
3506        global SelLINE msgToUser
3507        set msgToUser "You must select at least one lineid"
3508        if {$SelLINE == ""} {
3509          logMsg $::LEVEL1 $msgToUser
3510          continue
3511        }
3512        set msgToUser ""
3513        set SelectedLineIDs $SelLINE
3514        destroy .clines
3515        #logViewLineIDs causes call log to be re-read
3516        logViewLineIDs}] \
3517        -column 1 -padx 12 -row $row
3518
3519  modal {.clines}
3520
3521}
3522
3523proc helpItem {title passedMesg1 passedMesg2} {
3524    global Logo command Website
3525    global mesg1 mesg2 bckColor
3526
3527    set mesg1 [encoding convertfrom utf-8 $passedMesg1]
3528    set mesg2 [encoding convertfrom utf-8 $passedMesg2]
3529
3530    toplevel .hlp -background $bckColor
3531    wm title .hlp $title
3532    wm resizable .hlp 0 0
3533
3534    if [file exists $Logo] {
3535        image create photo ncid -file $Logo
3536        grid [ttk::label .hlp.img -image ncid] -columnspan 2 -padx 12 -pady 10
3537    }
3538
3539    if {$title == "About"} {set jt "center"} else {set jt "left"}
3540
3541    if {$mesg1 == ""} {
3542      grid [ttk::label .hlp.s1]
3543      grid [hyperlink .hlp.um -command [list eval exec $command "http://ncid.sourceforge.net/doc/NCID-UserManual.html" &] -text "User Manual"] -columnspan 2
3544      grid [hyperlink .hlp.mp -command [list eval exec $command "http://ncid.sourceforge.net/man/man.html" &] -text "Manual Pages"] -columnspan 2 -padx 42
3545      grid [ttk::label .hlp.s2]
3546    } else {
3547      grid [ttk::label .hlp.mesg1 -font FixedFontP -justify $jt -text $mesg1 ] -columnspan 2 -padx 12
3548      if { $title == "About" } {
3549      grid [hyperlink .hlp.web -command [list eval exec $command $Website &] -text "NCID website"] -columnspan 2
3550      grid [ttk::label .hlp.s3]
3551      }
3552    }
3553
3554    if { $mesg2 != "" } {
3555      grid [ttk::label .hlp.mesg2 -font FixedFontP -justify left -text $mesg2 ] -columnspan 2 -padx 12
3556    }
3557
3558    if { $title == "About" } {
3559      grid [ttk::button .hlp.clip -text "Copy to Clipboard" -command {
3560              clipboard clear
3561              clipboard append -- "$mesg1 $mesg2"
3562              doClipboardPopup "About" "Copied to clipboard: About window text"
3563            }] \
3564            -columnspan 2 -pady 10
3565    }
3566
3567    grid [ttk::button .hlp.ok -text "OK" -command {
3568          Disable .menubar
3569          destroy .hlp}] \
3570          -columnspan 2 -pady 10
3571
3572    if {$::tcl_platform(platform) != "unix"} {
3573      if {[catch {tk::PlaceWindow .hlp widget .} msg]} { logMsg $::LEVEL1 $msg}
3574    }
3575
3576    modal {.hlp}
3577}
3578
3579proc serverOPT {} {
3580    global Logo ServerOptions
3581
3582    set displayDesc "\nOptions sent to clients\n to indicate enabled:"
3583    set displayOPT "$ServerOptions"
3584    helpItem "Server Options" $displayDesc $displayOPT
3585}
3586
3587proc clearLog {} {
3588    global display_line_num Begin DiscoveredLineIDs SelHistoryLINE
3589
3590    set DiscoveredLineIDs [list]
3591    set SelHistoryLINE ""
3592    set display_line_num 0
3593    .vh configure -state normal
3594    .vh delete 1.0 end
3595    .vh yview moveto 0.0
3596    .vh configure -state disabled
3597}
3598
3599proc saveSize {flag} {
3600    global Txt
3601
3602    set save $Txt
3603    set Txt ""
3604    update
3605
3606    set geometry [wm geometry .]
3607    set Txt $save
3608    if {$flag == 0} {
3609        regexp {(\d+x\d+)\+} $geometry -> geometry
3610    }
3611    write_rc_file "geometry\\s+\\S+\\s+\[0-9x\]+" "wm geometry . $geometry"
3612
3613}
3614
3615proc write_rc_file {regexpr command} {
3616  global rcfile
3617
3618  if [file exists $rcfile] {
3619    if [file isdirectory $rcfile] {
3620      logMsg $::LEVEL1 "Unable to save data to $rcfile because it is a directory"
3621      return
3622    }
3623    set id [open $rcfile]
3624    set data [read $id]
3625    close $id
3626    set lines [lrange [split $data "\n"] 0 end-1]
3627    if {$regexpr == ""} {
3628      # command from list
3629      set lines [lsearch -all -inline -not -regexp $lines "$command"]
3630    } else {
3631      set index 0
3632      foreach line $lines {
3633        if [regexp $regexpr $line] {
3634          break
3635        }
3636        incr index
3637      }
3638      if {$index >= [llength $lines]} {
3639        lappend lines "$command"
3640      } else {
3641        lset lines $index "$command"
3642      }
3643    }
3644      set data [join $lines "\n"]
3645      set id [open $rcfile w]
3646      puts $id $data
3647  } else {
3648    set id [open $rcfile w]
3649    puts $id $command
3650  }
3651  close $id
3652}
3653
3654# Change Fonts
3655proc changeFont {} {
3656    global fontList bckColor
3657    global spinvalH spinvalM spinvalP
3658    global boldH boldM boldP
3659
3660    toplevel .f -background $bckColor
3661    wm title .f "Change Fixed Font"
3662    wm resizable .f 0 0
3663
3664    eval [concat {font create SelectionFontH} [font configure FixedFontH]]
3665    eval [concat {font create SelectionFontM} [font configure FixedFontM]]
3666    eval [concat {font create SelectionFontP} [font configure FixedFontP]]
3667
3668    set spinvalH [font configure FixedFontH -size]
3669    set boldH    [font configure FixedFontH -weight]
3670
3671    set spinvalM [font configure FixedFontM -size]
3672    set boldM    [font configure FixedFontM -weight]
3673
3674    set spinvalP [font configure FixedFontP -size]
3675    set boldP    [font configure FixedFontP -weight]
3676
3677    set currentFont [font configure FixedFontH -family]
3678
3679    label .f.ln -font FixedFontM -text "Font Name"
3680    grid [ttk::labelframe .f.fn -labelwidget .f.ln -labelanchor "n"] -pady 8 -padx 4 -sticky "nsew"
3681    grid [ttk::combobox .f.fn.cb -font FixedFontM -values $fontList -textvariable currentFont] -pady 5 -padx [list 60 90]
3682    grid [ttk::button .f.fn.btn -text "Re-scan"] -column 1 -row 0 -pady 5 -padx 5
3683    .f.fn.cb set $currentFont
3684
3685    ttk::label .f.hw -font FixedFontH -text "History Window Font"
3686    grid [ttk::labelframe .f.fh -labelwidget .f.hw -labelanchor "n"] -column 0 -pady 8 -padx 4 -sticky "nsew"
3687    grid [ttk::checkbutton .f.fh.cb -text "Bold" -variable boldH \
3688          -onvalue "bold" \
3689          -offvalue "normal" \
3690          -command {font configure SelectionFontH -weight $boldH}] \
3691          -pady 5 -padx 60
3692    grid [ttk::label .f.fh.label -font FixedFontH -text "Size: "] -column 1 -row 0 -pady 5 -padx 40
3693    grid [ttk::spinbox .f.fh.size -from 8 -to 36 -width 3 -font FixedFontH -textvariable spinvalH \
3694          -state readonly -command {font configure SelectionFontH -size $spinvalH}] \
3695          -column 2 -row 0 -pady 5 -padx 5
3696    grid [ttk::label .f.hw2 -text "Sample text 0123456789" -font SelectionFontH] -column 1 -row 1 -pady [list 35 5] -padx 25 -sticky "w"
3697
3698    ttk::label .f.mml -font FixedFontM -text "Message, Menu and Label Font"
3699    grid [ttk::labelframe .f.fm -labelwidget .f.mml -labelanchor "n"] -column 0 -pady 8 -padx 4 -sticky "nsew"
3700    grid [ttk::checkbutton .f.fm.cb -text "Bold" -variable boldM \
3701          -onvalue "bold" \
3702          -offvalue "normal" \
3703          -command {font configure SelectionFontM -weight $boldM}] \
3704          -pady 5 -padx 60
3705    grid [ttk::label .f.fm.label -font FixedFontM -text "Size: "] -column 1 -row 0 -pady 5 -padx 40
3706    grid [ttk::spinbox .f.fm.size -from 8 -to 36 -width 3 \
3707          -font FixedFontP -textvariable spinvalM \
3708          -state readonly \
3709          -command {font configure SelectionFontM -size $spinvalM}] \
3710          -column 2 -row 0 -pady 5 -padx 5
3711    grid [ttk::label .f.mml2 -text "Sample text 0123456789" -font SelectionFontM] -column 1 -row 2 -pady [list 35 5] -padx 25 -sticky "w"
3712
3713    ttk::label .f.pw -font FixedFontP -text "Popup Window Font"
3714    grid [ttk::labelframe .f.fp -labelwidget .f.pw -labelanchor "n"] -column 0  -pady 8 -padx 4 -sticky "nsew"
3715    grid [ttk::checkbutton .f.fp.cb -text "Bold" -variable boldP \
3716          -onvalue "bold" \
3717          -offvalue "normal" \
3718          -command {font configure SelectionFontP -weight $boldP}] \
3719          -pady 5 -padx 60
3720    grid [ttk::label .f.fp.label -font FixedFontP -text "Size: "] -column 1 -row 0 -pady 5 -padx 40
3721    grid [ttk::spinbox .f.fp.size -from 8 -to 36 -width 3 \
3722          -font FixedFontP -textvariable spinvalP \
3723          -state readonly \
3724          -command {font configure SelectionFontP -size $spinvalP}] \
3725          -column 2 -row 0 -pady 5 -padx 5
3726    grid [ttk::label .f.pw2 -text "Sample text 0123456789" -font SelectionFontP] -column 1 -row 3 -pady [list 35 5] -padx 25 -sticky "w"
3727
3728    grid [ttk::frame .f.f]  -column 0 -sticky "ew" -pady 8
3729    grid [ttk::button .f.f.cancel -text "Cancel"] -padx 12 -pady 6
3730    grid [ttk::button .f.f.apply -text "Apply"] -column 1 -row 0 -padx 12
3731    grid [ttk::button .f.f.ok -text "OK"] -column 2 -row 0 -padx 12
3732
3733    # change font family
3734    bind all <<ComboboxSelected>> {
3735        font configure SelectionFontH -family "$currentFont"
3736        font configure SelectionFontM -family "$currentFont"
3737        font configure SelectionFontP -family "$currentFont"
3738    }
3739
3740    bind TButton <ButtonRelease-1> {+
3741        switch -regexp %W {
3742            ".ctypes.*" { break }
3743            ".clines.*" { break }
3744            ".confirm.*" { break }
3745            ".view.*" { break }
3746            ".dial.*" { break }
3747            ".dt.*" { break }
3748            ".hlp.*" { break }
3749            ".rply.*" { break }
3750            ".reply.*" { break }
3751            default {
3752                set temp [%W cget -text]
3753                switch $temp {
3754                    "Cancel" {
3755                        destroy .f
3756                        break
3757                    }
3758                    "OK" -
3759                    "Apply" {
3760                        font configure FixedFontH -family "$currentFont" \
3761                            -size $spinvalH -weight $boldH
3762                        font configure FixedFontM -family "$currentFont" \
3763                            -size $spinvalM -weight $boldM
3764                        font configure FixedFontP -family "$currentFont" \
3765                            -size $spinvalP -weight $boldP
3766                        logFont
3767                        if {$temp eq "OK"} {
3768                            destroy .f
3769                        }
3770                        break
3771                    }
3772                    "Re-scan" {
3773                        .f.fn.cb configure -values {}
3774                        unset fontList
3775                        scanFonts
3776                        .f.fn.cb configure -values $fontList
3777                        break
3778                    }
3779                }
3780            }
3781        }
3782    }
3783
3784    modal {.f}
3785
3786    font delete SelectionFontH
3787    font delete SelectionFontM
3788    font delete SelectionFontP
3789}
3790
3791proc logFont {} {
3792    set fonth "[font configure FixedFontH]"
3793    set fontm "[font configure FixedFontM]"
3794    set fontp "[font configure FixedFontP]"
3795
3796    write_rc_file "FixedFontH" "font create FixedFontH $fonth"
3797    write_rc_file "FixedFontM" "font create FixedFontM $fontm"
3798    write_rc_file "FixedFontP" "font create FixedFontP $fontp"
3799
3800    logMsg $::LEVEL1 "history window font (FixedFontH) has been saved after command: $fonth"
3801    logMsg $::LEVEL1 "message window and display font (FixedFontM) has been saved after command: $fontm"
3802    logMsg $::LEVEL1 "help text font (FixedFontP) has been saved after command: $fontp"
3803}
3804
3805proc logOne {} {
3806  global Leading1 oldLeading1 nmbrREQ Country
3807
3808  if {$Leading1 != $oldLeading1} {
3809    logRCfileOldNewVarChange $::LEVEL2 oldLeading1 Leading1
3810    set oldLeading1 $Leading1
3811    write_rc_file "set Leading1" "set Leading1 \"$Leading1\""
3812  }
3813
3814  set dialnmbr $nmbrREQ
3815  if {$Country == "US"} {
3816    switch $Leading1 {
3817      "Leave" {
3818      }
3819      "Add" {
3820        if {[regexp {^91?} $dialnmbr] && [string length $dialnmbr] >= 11} {
3821          regsub {^91?} $dialnmbr {91} dialnmbr
3822        } else {
3823          regsub {^1?} $dialnmbr {1} dialnmbr
3824        }
3825      }
3826      "Remove" {
3827        if {[regexp {^91} $dialnmbr] && [string length $dialnmbr] == 12} {
3828          regsub {^91} $dialnmbr {9} dialnmbr
3829        } else {
3830          regsub {^1} $dialnmbr {} dialnmbr
3831        }
3832      }
3833    }
3834  }
3835  return $dialnmbr
3836}
3837
3838proc formatDT {} {
3839    global bckColor
3840
3841    toplevel .dt -background $bckColor
3842    wm title .dt "Date and Time Formats"
3843    wm resizable .dt 0 0
3844
3845    grid [ttk::label .dt.s1]
3846    grid [ttk::radiobutton .dt.12 -text "12 hour time" -variable clock -value 12 -command {logClock .vh}] -columnspan 2
3847    grid [ttk::radiobutton .dt.24 -text "24 hour time" -variable clock -value 24 -command {logClock .vh}] -columnspan 2 -rowspan 2
3848
3849    grid [ttk::label .dt.s2]
3850    grid [ttk::radiobutton .dt.mm -text "Date: MM DD YYYY" -variable AltDate -value 0 -command {logDate .vh}] -columnspan 2 -padx 12
3851    grid [ttk::radiobutton .dt.dd -text "Date: DD MM YYYY" -variable AltDate -value 1 -command {logDate .vh}] -columnspan 2 -padx 12 -rowspan 2
3852
3853    grid [ttk::label .dt.s3]
3854    grid [ttk::radiobutton .dt.s -text "Date Separator: /" -variable DateSepar -value "/" -command {logDate .vh}] -columnspan 2 -padx 12
3855    grid [ttk::radiobutton .dt.d -text "Date Separator: -" -variable DateSepar -value "-" -command {logDate .vh}] -columnspan 2 -padx 12
3856    grid [ttk::radiobutton .dt.p -text "Date Separator: ." -variable DateSepar -value "." -command {logDate .vh}] -columnspan 2 -padx 12
3857
3858    grid [ttk::label .dt.s4]
3859    grid [ttk::button .dt.ok -text "OK" -command {
3860          Disable .menubar
3861          destroy .dt}] \
3862          -columnspan 2 -pady 10
3863
3864    modal {.dt}
3865}
3866
3867proc logDate {widget} {
3868    global AltDate oldAltDate DateSepar oldDateSepar YearDot clock Socket display_line_num lbl1 lbl2 lbl3 lbl4 lbl5 lbl6 lbl7 lbl8 historyTextWidth
3869
3870    set dateflag 0
3871    if {$AltDate != $oldAltDate} {
3872        logRCfileOldNewVarChange $::LEVEL2 oldAltDate AltDate
3873        set oldAltDate $AltDate
3874        set dateflag 1
3875        write_rc_file "set AltDate" "set AltDate $AltDate"
3876    } elseif {$DateSepar != $oldDateSepar} {
3877        logRCfileOldNewVarChange $::LEVEL2 oldDateSepar DateSepar
3878        set oldDateSepar $DateSepar
3879        write_rc_file "set DateSepar" "set DateSepar $DateSepar"
3880    } else { return }
3881
3882    $widget configure -state normal
3883    for {set line 0} {1} {incr line} {
3884        set temp [$widget dump -text "1.0 + $line l" "1.0 + $line l lineend"]
3885        if {$temp eq ""} {break}
3886        # do not check for END or RLY in next line because these are ignored in this script
3887        if {![regexp {^(?:BLK|CID|HUP|MSG|MWI|NOT|OUT|PID|PUT|RID|WID)} [lindex $temp 1]]} {
3888            continue
3889        }
3890        # MSG lines may have no date or time, if the llength is 8
3891        if {[llength $temp] < 10} {continue}
3892        set date [lindex $temp 4]
3893        set start [lindex $temp 5]
3894        set stop [lindex $temp 8]
3895        set f1 [string range $date 0 1]
3896        set f2 [string range $date 3 4]
3897        set f3 [string range $date 6 9]
3898        if {$dateflag} {
3899            set date "$f2$DateSepar$f1$DateSepar$f3"
3900        } else {
3901            set date "$f1$DateSepar$f2$DateSepar$f3"
3902        }
3903        $widget insert "$stop - 1 c" "$date"
3904        $widget delete "$start" "$stop - 1 c"
3905    }
3906
3907    if {$clock == 24 && $DateSepar == "."} {set lbl $lbl1}
3908    if {$clock == 24 && $DateSepar == "-"} {set lbl $lbl2}
3909    if {$clock == 24 && $DateSepar == "/"} {set lbl $lbl3}
3910    if {$clock == 12 && $DateSepar == "."} {set lbl $lbl4}
3911    if {$clock == 12 && $DateSepar == "-"} {set lbl $lbl5}
3912    if {$clock == 12 && $DateSepar == "/"} {set lbl $lbl6}
3913    if {$clock == 24 && $DateSepar == "." && $YearDot == 1} {set lbl $lbl7}
3914    if {$clock == 12 && $DateSepar == "." && $YearDot == 1} {set lbl $lbl8}
3915
3916    if $YearDot {
3917      if {$Socket > 0} {
3918          set display_line_num 0
3919          clearLog
3920          puts $Socket "REQ: REREAD"
3921          flush $Socket
3922      }
3923      .la configure -text $lbl
3924      logMsg $::LEVEL1 "History Text Field Width changed to: $historyTextWidth characters"
3925      $widget configure -width $historyTextWidth
3926      set geometry [wm grid .]
3927      wm minsize . [lindex $geometry 0] [lindex $geometry 1]
3928      update
3929      $widget configure -state disabled
3930    }
3931}
3932
3933proc logTypeGroups {} {
3934  global TypeGroups oldTypeGroups SelectedTypes oldSelectedTypes
3935  global Socket display_line_num
3936
3937  set changeflag 0
3938
3939  if {$TypeGroups != $oldTypeGroups} {
3940    logRCfileOldNewVarChange $::LEVEL2 oldTypeGroups TypeGroups
3941    set oldTypeGroups $TypeGroups
3942    set changeflag 1
3943    updateViewDisplay
3944    write_rc_file "set TypeGroups" "set TypeGroups $TypeGroups"
3945  }
3946  if {$SelectedTypes != $oldSelectedTypes} {
3947    logRCfileOldNewVarChange $::LEVEL2 oldSelectedTypes SelectedTypes
3948    set oldSelectedTypes $SelectedTypes
3949    set changeflag 1
3950    updateViewDisplay
3951    write_rc_file "set SelectedTypes" "set SelectedTypes \{$SelectedTypes\}"
3952  }
3953
3954  if {$changeflag && $Socket > 0} {
3955     set display_line_num 0
3956     clearLog
3957     puts $Socket "REQ: REREAD"
3958     flush $Socket
3959  }
3960}
3961
3962proc logViewLineIDs {} {
3963  global DiscoveredLineIDs LineIDGroups oldLineIDGroups
3964  global SelectedLineIDs oldSelectedLineIDs
3965  global Socket display_line_num
3966
3967  set changeflag 0
3968
3969  if {$LineIDGroups != $oldLineIDGroups} {
3970    logRCfileOldNewVarChange $::LEVEL2 oldLineIDGroups LineIDGroups
3971    set oldLineIDGroups $LineIDGroups
3972    set changeflag 1
3973    write_rc_file "set LineIDGroups" "set LineIDGroups $LineIDGroups"
3974    if {$LineIDGroups == 0 && $SelectedLineIDs != $DiscoveredLineIDs} {
3975      set SelectedLineIDs $DiscoveredLineIDs
3976    }
3977    updateViewDisplay
3978  }
3979
3980  if {$SelectedLineIDs != $oldSelectedLineIDs} {
3981    logRCfileOldNewVarChange $::LEVEL2 oldSelectedLineIDs SelectedLineIDs
3982    set oldSelectedLineIDs $SelectedLineIDs
3983    set changeflag 1
3984    updateViewDisplay
3985    write_rc_file "set SelectedLineIDs" "set SelectedLineIDs \"$SelectedLineIDs\""
3986  }
3987
3988  if {$changeflag && $Socket > 0} {
3989     set display_line_num 0
3990     clearLog
3991     puts $Socket "REQ: REREAD"
3992     flush $Socket
3993  }
3994
3995}
3996
3997proc logHosts {} {
3998  global Hosts HostIndex Host Port oldHost oldPort SelHistoryLINE
3999  global ChangeHostFlag SelectedLineIDs LineIDGroups TypeGroups
4000  global ServerOptions
4001
4002  logMsg $::LEVEL2 "logHosts:"
4003
4004  set ChangeHostFlag 0
4005  lassign [split [lindex $Hosts $HostIndex] ":"] Host Port
4006  #logMsg $::LEVEL2 "Setting Host=$Host and Port=$Port by extracting HostIndex=$HostIndex from Hosts=$Hosts"
4007  logMsg $::LEVEL2 "    HostIndex=$HostIndex hosts=$Hosts"
4008  logMsg $::LEVEL2 "    host=$Host oldHost=$oldHost Port=$Port oldPort=$oldPort"
4009  if {$oldHost != $Host || $oldPort != $Port} {
4010      if {$oldHost != $Host} {
4011        logRCfileOldNewVarChange $::LEVEL2 oldHost Host
4012        set oldHost $Host
4013        set ChangeHostFlag 1
4014        write_rc_file "set Host" "set Host $Host"
4015      }
4016      if {$oldPort != $Port} {
4017        logRCfileOldNewVarChange $::LEVEL2 oldPort Port
4018        set oldPort $Port
4019        set ChangeHostFlag 1
4020        write_rc_file "set Port" "set Port $Port"
4021      }
4022  }
4023
4024  if {$ChangeHostFlag} {
4025    set LineIDGroups 0
4026    write_rc_file "set LineIDGroups" "set LineIDGroups 0"
4027    set TypeGroups 0
4028    write_rc_file "set TypeGroups" "set TypeGroups 0"
4029    set SelectedLineIDs ""
4030    set ServerOptions ""
4031    clearLog
4032    Reconnect
4033  }
4034}
4035
4036# when switching between multiple servers, make it obvious in the log file
4037proc logServerAddress {} {
4038  global Host Port
4039
4040  set bannerFill "="
4041  set bannerText "$bannerFill Server address: $Host:$Port $bannerFill"
4042  set bannerStars [string repeat $bannerFill [string length $bannerText]]
4043  logMsg $::LEVEL1 "$bannerStars"
4044  logMsg $::LEVEL1 "$bannerText"
4045  logMsg $::LEVEL1 "$bannerStars"
4046}
4047
4048proc processLineID {lineid} {
4049  global DiscoveredLineIDs SelectedLineIDs
4050
4051  set ret 0
4052
4053  # determine if DiscoveredLineIDs needs to be updated
4054  if {$lineid == ""} {set lineid "No-LineID"}
4055  if {[lsearch -exact $DiscoveredLineIDs "$lineid"] == -1} {
4056     lappend DiscoveredLineIDs "$lineid"
4057  }
4058
4059  # check if lineid is in SelectedLineIDs
4060  if {$SelectedLineIDs == ""} {
4061    set ret 1
4062  } else {
4063    foreach id_ $SelectedLineIDs {if {$id_ == $lineid} {set ret 1; break}}
4064  }
4065
4066  return $ret
4067}
4068
4069proc updateViewDisplay {} {
4070  global TypeGroups SelectedTypes
4071  global LineIDGroups SelectedLineIDs DiscoveredLineIDs
4072  global SelectedAllTypes SelectedCalls SelectedMessages SelectedSmartPhone
4073
4074  set selectview [list]
4075  .tv delete 1.0 2.end
4076  switch $TypeGroups {
4077    0 {
4078      set selectview $SelectedAllTypes; .tv insert 1.end "View All Types:"
4079    }
4080    1 {
4081      set selectview $SelectedCalls; .tv insert 1.end "View Call Types:"
4082    }
4083    2 {
4084      set selectview $SelectedMessages; .tv insert 1.end "View Message Types:"
4085    }
4086    3 {
4087      set selectview $SelectedSmartPhone;.tv insert 1.end "View Smart Phone Types:"
4088    }
4089    4 {
4090      set selectview $SelectedTypes; .tv insert 1.end "View Selected Types: "
4091    }
4092  }
4093  if {$selectview == ""} {
4094    .tv insert 1.end "View All Types: "
4095    set SelectedTypes "$SelectedAllTypes"
4096  } else {set SelectedTypes "$selectview"}
4097  foreach {type flag} $SelectedTypes {
4098    if {$flag == 1} {
4099      .tv insert 1.end " $type" "blank"
4100    } else {
4101      .tv insert 1.end " " "blank"
4102      .tv insert 1.end "$type" "strike $type"
4103    }
4104  }
4105
4106  if {$SelectedLineIDs == ""} {
4107    set SelectedLineIDs $DiscoveredLineIDs
4108  } else {
4109    set viewselected $SelectedLineIDs
4110    set SelectedLineIDs ""
4111    foreach vs $viewselected {
4112      set index [lsearch -exact "$DiscoveredLineIDs" $vs]
4113      if {$index != -1} {
4114        lappend SelectedLineIDs $vs
4115      }
4116    }
4117  }
4118
4119  .tv insert 1.end "\n"
4120  if {$LineIDGroups == 0} {
4121    .tv insert 2.end "View All LineIDs: $SelectedLineIDs"
4122  } else {
4123    .tv insert 2.end "View Selected LineIDs:"
4124    set lineids [list]
4125
4126    # Setup array for viewing lineids
4127    foreach lineid $DiscoveredLineIDs {
4128      lappend lineids $lineid 0
4129    }
4130
4131    # determine which lineids are wanted for viewing
4132    foreach lineid $SelectedLineIDs {
4133      if {[set pos [lsearch -exact $lineids $lineid]] != -1} {
4134        set pos1 [expr $pos + 1]
4135        set lineids "[lreplace $lineids $pos1 $pos1 1]"
4136      }
4137    }
4138
4139    # view each lineid
4140    foreach {lineid value} $lineids {
4141      # need to enclose lineid with {} again
4142      if {[regexp {\s} $lineid]} {set lineid "{$lineid}"}
4143      # determine when to strikeout a lineid
4144      if {$value == 1} {
4145        .tv insert 2.end " $lineid" "blank"
4146      } else {
4147        .tv insert 2.end " " "blank"
4148        .tv insert 2.end "$lineid" "strike $lineid"
4149      }
4150    }
4151  }
4152  update
4153  if {[lindex [.tv yview] 0] + [lindex [.tv yview] 1] != 1.0} {
4154    grid .vsb} else {grid remove .vsb}
4155}
4156
4157proc logTheme {} {
4158  global oldThemeName ThemeName
4159  global Socket display_line_num
4160
4161  if {$ThemeName != $oldThemeName} {
4162    logRCfileOldNewVarChange $::LEVEL2 oldThemeName ThemeName
4163    set oldThemeName $ThemeName
4164    write_rc_file "set ThemeName" "set ThemeName \"$ThemeName\""
4165
4166    setStyles
4167    if {$Socket > 0} {
4168      set display_line_num 0
4169      clearLog
4170      puts $Socket "REQ: REREAD"
4171      flush $Socket
4172    }
4173  }
4174}
4175
4176proc logClock {widget} {
4177    global  clock oldClock DateSepar YearDot Socket display_line_num lbl1 lbl2 lbl3 lbl4 lbl5 lbl6 lbl7 lbl8 historyTextWidth
4178
4179    if {$clock == $oldClock} { return }
4180    #not using logRCfileOldNewVarChange here as next line is more user friendly
4181    logMsg $::LEVEL2 "rcfile and Time display have been changed from: $oldClock to: $clock hours"
4182    set oldClock $clock
4183    write_rc_file "set clock" "set clock $clock"
4184    $widget configure -state normal
4185    for {set line 0} {1} {incr line} {
4186        set temp [$widget dump -text "1.0 + $line l" "1.0 + $line l lineend"]
4187        if {$temp eq ""} {break}
4188        # do not check for END or RLY in next line because these are ignored in this script
4189        if {![regexp {^(?:BLK|CID|HUP|MSG|MWI|NOT|OUT|PID|PUT|RID|WID)} [lindex $temp 1]]} {
4190            continue
4191        }
4192        # MSG lines may have no date or time, if so the llength is 8
4193        if {[llength $temp] < 10} {continue}
4194        set time [lindex $temp 7]
4195        set start [lindex $temp 8]
4196        set stop [lindex $temp 11]
4197        if {$clock == 12} {
4198            set hours [string range $time 0 1]
4199            set minutes [string range $time 3 4]
4200            set time [convertTo12 $hours $minutes]
4201        } else {
4202            set hours [string range $time 0 1]
4203            set minutes [string range $time 3 4]
4204            set AmPm [string range $time 6 7]
4205            set time [convertTo24 $hours $minutes $AmPm]
4206        }
4207        $widget insert "$stop - 1 c" "$time"
4208        $widget delete "$start" "$stop - 1 c"
4209    }
4210
4211    if {$clock == 24 && $DateSepar == "."} {set lbl $lbl1}
4212    if {$clock == 24 && $DateSepar == "-"} {set lbl $lbl2}
4213    if {$clock == 24 && $DateSepar == "/"} {set lbl $lbl3}
4214    if {$clock == 12 && $DateSepar == "."} {set lbl $lbl4}
4215    if {$clock == 12 && $DateSepar == "-"} {set lbl $lbl5}
4216    if {$clock == 12 && $DateSepar == "/"} {set lbl $lbl6}
4217    if {$clock == 24 && $DateSepar == "." && $YearDot == 1} {set lbl $lbl7}
4218    if {$clock == 12 && $DateSepar == "." && $YearDot == 1} {set lbl $lbl8}
4219
4220    if {$Socket > 0} {
4221        set display_line_num 0
4222        clearLog
4223        puts $Socket "REQ: REREAD"
4224        flush $Socket
4225    }
4226    .la configure -text $lbl
4227    logMsg $::LEVEL1 "History Text Field Width changed to: $historyTextWidth characters"
4228    $widget configure -width $historyTextWidth
4229    set geometry [wm grid .]
4230    wm minsize . [lindex $geometry 0] [lindex $geometry 1]
4231    update
4232    $widget configure -state disabled
4233}
4234
4235proc logAuto {menu} {
4236    global ExitOn autoSave oldAutoSave m
4237
4238    if {$autoSave eq $oldAutoSave} { return }
4239    set oldAutoSave $autoSave
4240    write_rc_file "set autoSave" "set autoSave \"$autoSave\""
4241    switch $autoSave {
4242        "size" {
4243            set temp "save size only"
4244            $menu entryconfigure *Size -state disabled
4245            $menu entryconfigure *Position -state disabled
4246            $menu entryconfigure Quit -command {do_goodbye; saveSize 0; exit}
4247            wm protocol . WM_DELETE_WINDOW {do_goodbye; saveSize 0; $ExitOn}
4248        }
4249        "both" {
4250            set temp "save size and position"
4251            $menu entryconfigure *Size -state disabled
4252            $menu entryconfigure *Position -state disabled
4253            $menu entryconfigure Quit -command {do_goodbye; saveSize 1; exit}
4254            wm protocol . WM_DELETE_WINDOW {saveSize 1; $ExitOn}
4255        }
4256        "off" {
4257            set temp "off"
4258            $menu entryconfigure *Size -state normal
4259            $menu entryconfigure *Position -state normal
4260            $menu entryconfigure Quit -command {do_goodbye; exit}
4261            wm protocol . WM_DELETE_WINDOW $ExitOn
4262        }
4263    }
4264    logMsg $::LEVEL2 "rcfile and autoSave have been set to $temp"
4265}
4266
4267proc logStart {} {
4268    global      autoStart oldAutoStart dtfile asfile
4269
4270    if {$autoStart eq $oldAutoStart} { return }
4271    set oldAutoStart $autoStart
4272    switch $autoStart {
4273        "on" {
4274            file copy -force $dtfile $asfile
4275        }
4276        "on+alert" {
4277            set in  [open $dtfile r]
4278            set out [open $asfile w]
4279            while {[gets $in line] != -1} {
4280                regsub {NCID client} $line {NCID client with ncid-alert} line
4281                regsub {ID\) client} $line {ID) client with desktop notifications} line
4282                regsub {Exec=ncid} $line {Exec=ncid --module ncid-alert} line
4283                puts $out $line
4284            }
4285            close $in
4286            close $out
4287        }
4288        "alert" {
4289            set in  [open $dtfile r]
4290            set out [open $asfile w]
4291            while {[gets $in line] != -1} {
4292                regsub {NCID client} $line {NCID Alert} line
4293                regsub {NCID \(Network Caller ID\) client} $line {Send NCID call or message desktop notifications} line
4294                regsub {Exec=ncid} $line {Exec=ncid --no-gui --module ncid-alert} line
4295                puts $out $line
4296            }
4297            close $in
4298            close $out
4299        }
4300        "off" {
4301            file delete $asfile
4302        }
4303    }
4304    write_rc_file "set autoStart" "set autoStart \"$autoStart\""
4305    logMsg $::LEVEL2 "rcfile and autoStart have been set to $autoStart"
4306}
4307
4308# Handle MSG from GUI
4309proc handleGUIMSG {} {
4310
4311  # get MSG and clear text input box
4312  set line [.im get]
4313  .im delete 0 end
4314  # get rid of non-printable characters at start/end of string
4315  set line [string trim $line]
4316  # send MSG to server, if $line not empty
4317  if {[string length $line] > 0} {handleMSG $line}
4318}
4319
4320# Handle MSG sent to server
4321proc handleMSG {msg} {
4322  global Socket LoginName LineName
4323
4324  puts $Socket "MSG: $msg ###NAME*$LoginName*LINE*$LineName"
4325  flush $Socket
4326}
4327
4328# Handle verbosity levels
4329proc logMsg {level msg} {
4330    global Verbose LogChan
4331
4332    if {$Verbose >= $level} {
4333       puts "$msg"
4334       if {$LogChan != ""} {
4335           set systemTime [clock seconds]
4336           puts $LogChan "\[[clock format $systemTime -format "%m/%d %H:%M"]\] $msg"
4337       }
4338    }
4339}
4340
4341# https://stackoverflow.com/a/34221864
4342proc logArray {verboseLevel heading a {pattern *}} {
4343    upvar 1 $a array
4344    if {![array exists array]} {
4345        return -code error "\"$a\" isn't an array"
4346    }
4347    set maxl 0
4348    set names [lsort [array names array $pattern]]
4349    foreach name $names {
4350        if {[string length $name] > $maxl} {
4351            set maxl [string length $name]
4352        }
4353    }
4354    set maxl [expr {$maxl + [string length $a] + 2}]
4355    set indent ""
4356    if {$heading != ""} {
4357       logMsg $verboseLevel $heading
4358       set indent "     "
4359    }
4360    foreach name $names {
4361        set nameString [format %s(%s) $a $name]
4362        logMsg $verboseLevel [format "%s$%-*s = %s" "$indent" $maxl $nameString $array($name)]
4363    }
4364}
4365
4366
4367# handle a PID file, if it can not be created, ignore it
4368proc doPID {} {
4369    global PIDfile
4370
4371    if {$PIDfile != ""} {
4372        set activepid ""
4373        set PIDdir [file dirname $PIDfile]
4374        if {[file writable $PIDfile]} {
4375            # get the pid's on the first line of the pidfile
4376            set chan [open $PIDfile r ]
4377            gets $chan line
4378            close $chan
4379            # save any active pid
4380            foreach p $line {
4381                if {[file exists /proc/$p]} {set activepid "$p "}
4382            }
4383            # truncate the pidfile
4384            set chan [open $PIDfile w ]
4385            if {$activepid == ""} {
4386                # write current PID into pidfile
4387                puts $chan [pid]
4388            } else {
4389                # write active PID's and current PID into pidfile
4390                puts $chan "$activepid [pid]"
4391            }
4392            close $chan
4393        } elseif {[file writable $PIDdir]} {
4394            # create the pidfile
4395            set chan [open $PIDfile "CREAT WRONLY" 0644]
4396            puts $chan [pid]
4397            close $chan
4398
4399        }
4400        logMsg $::LEVEL1 "Using pidfile: $PIDfile"
4401    } else {logMsg $::LEVEL1 "Not using a PID file"}
4402}
4403
4404# handle log file, if it can not be created, ignore it
4405proc doLogOpen {} {
4406    global LogEnable LogDir LogChan LogStatus LogFile
4407
4408  set access "CREAT WRONLY TRUNC"
4409  switch $LogEnable {
4410    1 {
4411      # create embed process ID in file name
4412      set LogFile [file normalize [file join $LogDir "ncid-client-[pid].log"]]
4413      set LogStatus "Log File:      $LogFile\n               enabled - process ID"
4414    }
4415    2 {
4416      # create/overwrite, do not embed process ID in file name
4417      set log_file ncid-client.log
4418      if {[machine platform] == "windows"} {set log_file ncid-windows.log}
4419      if {[machine platform] == "unix"} {set log_file "ncid-[info hostname].log"}
4420      if {[machine platform] == "android"} {set log_file ncid-androwish.log}
4421      if {[machine os] == "Darwin"} {regsub {^ncid} $log_file {ncid-mac} log_file}
4422      set LogFile [file normalize [file join $LogDir $log_file]]
4423      set LogStatus "Log File:      $LogFile\n               enabled - overwrite"
4424    }
4425    default {
4426      # this should never happen
4427      set LogStatus "LogEnable out of range: $LogEnable"
4428      set LogEnable 0
4429    }
4430  }
4431
4432  if {$LogEnable > 0} {
4433    if {[catch {set LogChan [open $LogFile $access "0644"]} failmsg]} {
4434      # logfile open failed
4435      set LogStatus "$failmsg"
4436      set LogEnable 0
4437    } else {
4438      fconfigure $LogChan -buffering line
4439    }
4440  }
4441  set LogStatus [regsub {/.*/} $LogStatus ""]
4442}
4443
4444proc scanFonts {} {
4445    global fontList currentFont
4446
4447    set numberFonts 0
4448    set numberFixed 0
4449    # find a fixed-width font and use it
4450    foreach family [font families] {
4451        incr numberFonts
4452
4453        # Skip Bauhaus9 on Apple Mac -- triggers wish error:
4454        # CoreText: Invalid 'kern' Table In CTFont <name: Bauhaus93....
4455        if {$family == "Bauhaus 93"} {
4456            logMsg $::LEVEL4 "skipping fixed font: $family"
4457            continue
4458        }
4459
4460        # Skip '.LastResort' on Apple Mac -- garbles all text
4461        if {$family == ".LastResort"} {
4462            logMsg $::LEVEL4 "skipping fixed font: $family"
4463            continue
4464        }
4465
4466        # skip Emoji fonts, color ones cause "X Error of failed request"
4467        if {[regexp {Emoji} $family]} {
4468            logMsg $::LEVEL4 "skipping fixed font: $family"
4469            continue
4470        }
4471
4472        # Microsoft has duplicate fonts that start with @
4473        if {[regexp {^@} $family]} {
4474            logMsg $::LEVEL4 "skipping fixed font: $family"
4475            continue
4476        }
4477
4478        if {[font metrics \"$family\" -fixed]} {
4479            incr numberFixed
4480            logMsg $::LEVEL4 "detected fixed font $family"
4481            lappend fontList $family
4482        }
4483    }
4484    # sort and remove duplicates
4485    set fontList [lsort -dictionary -unique $fontList]
4486
4487    set currentFont [lindex $fontList 0]
4488    logMsg $::LEVEL1 "current font set to: $currentFont"
4489    logMsg $::LEVEL1 "$numberFixed fixed fonts out of $numberFonts fonts"
4490    write_rc_file "fontList " "set fontList \"$fontList\""
4491}
4492
4493proc modal {window} {
4494  wm transient $window .
4495
4496  # Tk Command: tkwait visibility
4497  # https://www.tcl.tk/man/tcl/TkCmd/tkwait.htm
4498  #   Waits for a change in the visibility state of a window as indicated by a <VisibilityNotify> event.
4499  #   This is typically used to wait for a newly-created window to appear on the screen before taking some action.
4500  # https://stackoverflow.com/questions/8929031/grabbing-a-new-window-in-tcl-tk
4501  #   This prevents the error from sometimes appearing:
4502  #     RGError: grab failed: window not viewable
4503  #
4504  # Tk Command: wininfo viewable
4505  # https://www.tcl.tk/man/tcl/TkCmd/winfo.htm
4506  # http://wiki.tcl.tk/10013
4507  #   Needed for (Windows, OSX/Aqua) because <VisibilityNotify> events are never delivered.
4508  #   On a windows platform, tkwait visibility does not return on a visable vindow.
4509  if {![winfo viewable $window]} { tkwait visibility $window }
4510
4511  grab $window
4512
4513  # Resolves the lack of focus on a newly created window
4514  focus $window
4515
4516  wm protocol $window WM_DELETE_$window {grab release $window; destroy $window}
4517  raise $window
4518  tkwait window $window
4519}
4520
4521# Hyperlink Widget: https://wiki.tcl.tk/36776
4522proc hyperlink { name args } {
4523  global fgColor
4524
4525  if {"Underline-Font" ni [font names]} {
4526    font create Underline-Font
4527  }
4528  # font size may have changed
4529  font configure Underline-Font {*}[font actual FixedFontP] -underline true
4530
4531  if { [ dict exists $args -command ] } {
4532    set command [ dict get $args -command ]
4533    dict unset args -command
4534  }
4535
4536  # add -foreground, -font and -cursor, but only if they are missing
4537  set args [ dict merge [ dict create -foreground $fgColor \
4538            -font Underline-Font -cursor hand2 ] $args ]
4539
4540  ttk::label $name {*}$args
4541
4542  if { [ info exists command ] } {
4543    bind $name <Button-1> $command
4544  }
4545
4546  return $name
4547}
4548
4549proc setBrowser {} {
4550  global command browser
4551
4552  # open is the OS X equivalent to xdg-open on Linux, start is used on Windows
4553  set commands {xdg-open open start}
4554  foreach browser $commands {
4555    if {$browser eq "start"} {
4556      set command [list {*}[auto_execok start] {}]
4557    } else {
4558      set command [auto_execok $browser]
4559    }
4560    if {[string length $command]} {
4561      break
4562    }
4563  }
4564
4565  if {[string length $command] == 0} {
4566    return -code error "couldn't find browser"
4567  }
4568}
4569
4570########################################################################
4571#                      MAIN ROUTINE STARTS HERE                        #
4572########################################################################
4573
4574if {$nameWidth == ""
4575    || ![regexp {^[2345]+[0-9]+$} $nameWidth]
4576    || $nameWidth > 50} {
4577    exitMsg 10 "nameWidth should be 20-50 but is \"$nameWidth\""
4578}
4579
4580if {$ClipboardPopup == 1} {
4581  if {$ClipboardPopupTime == "" || ![regexp {^\d$} $ClipboardPopupTime]} {
4582    exitMsg 10 "ClipboardPopupTime should be 0-9 but is \"$ClipboardPopupTime\""
4583  }
4584} elseif {$ClipboardPopup != 0} {
4585  exitMsg 10 "ClipboardPopup should be 0-1 but is \"$ClipboardPopup\""
4586}
4587
4588# AndroWish - packages sdltk and borg are included, no need to install
4589set sdltk_present [expr {[info commands "sdltk"]} ne {""}]
4590set borg_present [expr {[info commands "borg"]} ne {""}]
4591
4592if {$NoGUI} {
4593    if {$Host == ""} {set Host $DefaultHost}
4594    if {$Port == ""} {set Port $DefaultPort}
4595} else {
4596    switch $::tcl_platform(platform) {
4597      "unix" {
4598        set rcfile $UnixRCfile
4599        set asfile $UnixASfile
4600      }
4601      "windows" {
4602        #set rcfile [file join $::env(AppData) "ncid.dat"]
4603        set rcfile $WinRCfile
4604      }
4605    }
4606   if {$PortableDir != ""} {set rcfile [file join $PortableDir ".ncid"]}
4607
4608   processRCfile
4609   set delayedMsgs "$delayedMsgs\nProcessed RC file: $rcfile"
4610   set RCfileHost $Host
4611   set RCfilePort $Port
4612   set RCfileoldHost $oldHost
4613   set RCfileoldPort $oldPort
4614   set RCfileHosts $Hosts
4615   set RCfileHostIndex $HostIndex
4616   set RCfileThemeName $ThemeName
4617}
4618
4619getArg
4620set delayedMsgs "$delayedMsgs\nProcessed command line arguments"
4621
4622checkHosts
4623
4624if {![regexp {^[0-2]+$} $LogEnable]} {
4625      set LogStatus "Log enable out of range: $LogEnable"
4626      set LogEnable 0
4627}
4628
4629if {$LogEnable > 0} {
4630    # Make sure LogDir is created or set LogEnable 0
4631    if {$PortableDir != ""} {set LogDir [file join $PortableDir "logs"]}
4632    if {[regexp {[/+\w+]$} $LogDir]} {
4633        if {![file isdirectory $LogDir]} {
4634            if {[catch {file mkdir $LogDir} msg]} {
4635                set LogEnable 0; set LogStatus $msg
4636            }
4637        }
4638        set LogDirLocation "Log Directory: [file normalize $LogDir]"
4639    } else {set LogEnable 0}
4640    if {$LogEnable > 0} {doLogOpen}
4641}
4642
4643set oldHost $Host
4644set oldPort $Port
4645set ArgHost $Host
4646set ArgPort $Port
4647set ArgoldHost $oldHost
4648set ArgoldPort $oldPort
4649set ArgHosts $Hosts
4650set ArgHostIndex $HostIndex
4651
4652if {$NoGUI && $Verbose == 0} {set Verbose 1}
4653
4654if {$HostnameFlag} {
4655    regsub {ncid} $VersionIDENT "$hostname/ncid" VersionIDENT
4656} else {
4657    set LineName ncid
4658}
4659
4660if {$Module != ""} {
4661    regsub {.*/(.*)} $Module {\1} ModName
4662    if {$NoGUI} {
4663        regsub {ncid} $VersionInfo "$ModName" VersionInfo
4664        regsub {ncid} $VersionIDENT "$ModName" VersionIDENT
4665    } else {
4666        regsub {ncid} $VersionInfo "ncid using module $ModName" VersionInfo
4667        regsub {ncid} $VersionIDENT "ncid using module $ModName" VersionIDENT
4668    }
4669}
4670
4671# LoginName on chromebook and android are system-generated user names
4672# like uX_aYY where X is a user# like 0, 1, 2 and YY seems to be a
4673# process id for that user instance. It's currently not possible to
4674# retrieve the gmail address associated with the login user.
4675if {[machine platform] == "chromebook" } {
4676   set LoginName "chromebook user"
4677} elseif {[machine platform] == "android" } {
4678   set LoginName "android user"
4679} else {
4680   set LoginName $tcl_platform(user)
4681}
4682
4683# log command line and any options on separate lines
4684  set cl "Command line: $::argv0"
4685  for {set cnt 0} {$cnt < $::argc} {incr cnt} {
4686      set optarg [lindex $::argv [expr $cnt + 1]]
4687      set opt [lindex $::argv $cnt]
4688      if {[string index $opt 0] == "-"} {
4689        logMsg $::LEVEL1 $cl
4690        set cl "              $opt"
4691      } else {
4692        append cl " " $opt;
4693      }
4694  }
4695  logMsg $::LEVEL1 $cl
4696
4697logMsg $::LEVEL1 "$VersionInfo"
4698if {$NoGUI} {
4699    logMsg $::LEVEL1 "        Command line mode"
4700} else {
4701    logMsg $::LEVEL1 "        GUI mode"
4702}
4703logMsg $::LEVEL1 "Verbose Level: $Verbose"
4704
4705# bug in AndroWish - as of version 2018-06-30,
4706# '[info nameofexecutable]' returns null
4707if {($Interpreter == "") && ([machine platform] == "android")} {
4708    set Interpreter $::env(PACKAGE_CODE_PATH)/wish
4709}
4710logMsg $::LEVEL1 "Interpreter: $Interpreter"
4711logMsg $::LEVEL1 "Default Host: $DefaultHost"
4712logMsg $::LEVEL1 "Default Port: $DefaultPort"
4713if {!$NoGUI && $::tcl_platform(platform) == "unix"} {
4714    logMsg $::LEVEL1 "AutoStart File: $asfile"
4715}
4716
4717# Observed OS encoding systems
4718# Windows 10:       cp1252
4719# Mac (native GUI): utf-8
4720# Mac (XQuartz):    utf-8
4721# AndroWish:        utf-8
4722# Linux:            utf-8
4723
4724logMsg $::LEVEL1 "Operating System Encoding: [encoding system]"
4725
4726if {$LogDirLocation != ""} {logMsg $::LEVEL1 $LogDirLocation}
4727logMsg $::LEVEL1 $LogStatus
4728logMsg $::LEVEL1 "Platform: [machine platform]"
4729logMsg $::LEVEL1 "OS: [machine os]"
4730if {[machine platform] == "android"} {
4731   logMsg $::LEVEL1 "Android device model: [machine model]"
4732}
4733logMsg $::LEVEL1 "TCL library: [info library]"
4734logMsg $::LEVEL1 "TCL version: [info patchlevel]"
4735
4736if {$PortableDir != ""} {
4737    logMsg $::LEVEL1 "Using PortableDir: $PortableDir"
4738}
4739logMsg $::LEVEL1 $delayedMsgs
4740
4741if {$OptPmsg != ""} {logMsg $::LEVEL1 "$OptPmsg"}
4742
4743if {!$NoGUI} {
4744    set DistThemes [lsort -dictionary [ttk::themes]]
4745
4746    # only allow default theme for now
4747    set DistThemes default
4748
4749    set AddonThemes ""
4750    if {[file isdirectory $ThemeDir] && [glob -nocomplain -dir $ThemeDir *] != 0} {
4751        lappend auto_path $ThemeDir
4752
4753        # This forces an update of the available packages list.
4754        # It's required for package names to find the themes in
4755        # <path>/themes/*.tcl
4756        eval [package unknown] Tcl [package provide Tcl]
4757        set AddonThemes [lsort -dictionary [ttk::themes]]
4758        foreach t $DistThemes {
4759          set AddonThemes [lsearch -all -inline -not -exact $AddonThemes "$t"]
4760        }
4761    }
4762
4763    #on Windows, force dialogs to have readable checkboxes and radiobuttons
4764    #test by going to View->TYPEs->Select and Preferences->Date and Time
4765    #acceptable  : alt default classic
4766    #unacceptable: winnative clam vista
4767    if {[machine platform] == "windows"} {ttk::style theme use "default"}
4768
4769    #on AndroWish, any style besides droid is acceptable
4770    if {[ttk::style theme use] == "droid"} {ttk::style theme use "default"}
4771
4772}
4773
4774logMsg $::LEVEL1 "HostnameFlag: $HostnameFlag"
4775logMsg $::LEVEL1 "LineName: $LineName"
4776logMsg $::LEVEL1 "LoginName: $LoginName"
4777logMsg $::LEVEL1 "Delay between reconnect tries to the server: $Delay (seconds)"
4778
4779# dump environment variables - useful when running in portable mode
4780logArray $::LEVEL5 "Environment variables:" ::env
4781
4782set About \
4783"
4784$VersionInfo
4785$Author
4786"
4787
4788if {!$NoGUI} {
4789    logMsg $::LEVEL1 "Detected windowing system: [machine osgui]"
4790    logMsg $::LEVEL1 "Distributed Themes: $DistThemes"
4791    logMsg $::LEVEL1 "Custom Themes: day night"
4792    if {$AddonThemes != ""} {
4793        logMsg $::LEVEL1 "Addon Themes Dir: $ThemeDir"
4794        logMsg $::LEVEL1 "Addon Themes: $AddonThemes"
4795    }
4796    logMsg $::LEVEL1 "Current Theme: $ThemeName"
4797    logMsg $::LEVEL1 "ImageDir: $ImageDir"
4798    logMsg $::LEVEL1 "Popup time: $PopupTime"
4799    if {$NoExit} {
4800        set ExitOn do_nothing
4801        logMsg $::LEVEL1 "The \"Close Window\" ttk::button is disabled"
4802    }
4803    if {![regexp {^(:?char|word|none)$} $WrapLines]} {
4804        logMsg $::LEVEL1 "WrapLines set to invalid value of \"$WrapLines\", using default"
4805        set WrapLines "char"
4806    } else {
4807        logMsg $::LEVEL1 "WrapLines set to \"$WrapLines\""
4808    }
4809    if {$DialPrefix != ""} {
4810        logMsg $::LEVEL1 "Dial prefix: $DialPrefix"
4811    } else {
4812        logMsg $::LEVEL1 "Dial prefix: none"
4813    }
4814    setBrowser
4815    makeWindow
4816
4817    logMsg $::LEVEL1 "Time Field Width: $timeWidth characters"
4818    logMsg $::LEVEL1 "Number Field Width: $nmbrWidth characters"
4819    logMsg $::LEVEL1 "Name Field Width: $nameWidth characters"
4820    logMsg $::LEVEL1 "Line Label Field Width: $lineIDWidth characters"
4821    logMsg $::LEVEL1 "Mesg Type Field Width: $mtypeWidth characters"
4822    logMsg $::LEVEL1 "Calculated History Text Field Width: $historyTextWidth characters"
4823
4824    if {$ClipboardPopup == 0} {
4825        set cpt "$ClipboardPopupTime second"
4826        if {$ClipboardPopupTime != 1} {append cpt "s"}
4827        logMsg $::LEVEL2 "Clipboard Window Popup Time:  $cpt"
4828    } else {logMsg $::LEVEL2 "Clipboard Popup Window Time: forever"}
4829
4830    switch $TypeGroups {
4831        0 {logMsg $::LEVEL1 "View Types: All"}
4832        1 {logMsg $::LEVEL1 "View Types: Calls"}
4833        2 {logMsg $::LEVEL1 "View Types: Messages"}
4834        3 {logMsg $::LEVEL1 "View Types: Smart Phone"}
4835        4 {logMsg $::LEVEL1 "View Types: Custom"
4836           logMsg $::LEVEL3 "            SelectedTypes contains $SelectedTypes"
4837    }
4838    }
4839    switch $LineIDGroups {
4840        0   {logMsg $::LEVEL1 "View Lines: All"}
4841        >=1 {logMsg $::LEVEL1 "View Lines: $SelectedLineIDs"}
4842    }
4843}
4844
4845checkCountry $Country
4846logMsg $::LEVEL1 "Country Code: $Country"
4847if {$Country == "US"} {
4848    if $NoOne {
4849        logMsg $::LEVEL1 "Leading digit '1' in phone number will NOT be displayed"
4850    } else {
4851        logMsg $::LEVEL1 "Leading digit '1' in phone number WILL be displayed"
4852    }
4853}
4854
4855if {$DateSepar != "/" && $DateSepar != "-" && $DateSepar != "."} {
4856    exitMsg 7 "Date separator \"$DateSepar\" is not supported. Please change it."
4857}
4858
4859if $AltDate {
4860    logMsg $::LEVEL1 "Date Format: DD${DateSepar}MM${DateSepar}YYYY"
4861} else { logMsg $::LEVEL1 "Date Format: MM${DateSepar}DD${DateSepar}YYYY" }
4862
4863if {$WakeUp} {
4864    if {![file executable $ModDir/ncid-wakeup]} {
4865        set WakeUp 0
4866        logMsg $::LEVEL1 "Module ncid-wakeup not found or not executable, wakeup option removed"
4867    }
4868}
4869
4870if {$Module != ""} {
4871    if {[file exists $Module]} {
4872        if {![file executable $Module]} {
4873            # Simple test to see if running under Cygwin
4874            if {[file exists $CygwinBat]} {
4875                # The Cygwin TCL cannot execute shell scripts
4876                set ExecSh 1
4877            } else {
4878                exitMsg 2 "Module Not Executable: $Module"
4879            }
4880        }
4881    } else {exitMsg 3 "Module Not Found: $Module"}
4882    logMsg $::LEVEL1 "Using output Module: $Module"
4883    # change module name from <path>/ncid-<name> to ncid_<name>
4884    regsub {.*/(.*)} $Module {\1} ModName
4885    regsub {\-} $ModName {_} modopt
4886    # set the module option variable in $$modopt
4887    if {[catch {eval [subst $$modopt]} oops]} {
4888        logMsg $::LEVEL1 "No optional \"$modopt\" variable in ncid.conf"
4889    } else {
4890        regsub {.*set *(\w+)\s+.*} [eval concat $$modopt] {\1} modvar
4891        regsub {.*set *(\w+)\s+(\w+).*} [eval concat $$modopt] {\2} modval
4892        if {$modvar == "Ring"} { set CallOnRing 1 }
4893        logMsg $::LEVEL1 "Optional \"$modopt\" variable set \"$modvar\" to \"$modval\" in ncid.conf"
4894    }
4895    if {$CallOnRing} {
4896      switch -- $Ring {
4897        -9 {logMsg $::LEVEL1 "Will execute $Module every ring after CID"}
4898        -2 {logMsg $::LEVEL1 "Will execute $Module after hangup after answer"}
4899        -1 {logMsg $::LEVEL1 "Will execute $Module after hangup with no answer"}
4900         0 {logMsg $::LEVEL1 "Will execute $Module when ringing stops"}
4901         default {logMsg $::LEVEL1 "Will execute $Module at Ring $Ring"}
4902      }
4903    } elseif {$Module != ""} {
4904       logMsg $::LEVEL1 "Will execute $Module when CID arrives"
4905    }
4906}
4907
4908# dump certain variables for troubleshooting
4909  logMsg $::LEVEL5 ""
4910  logMsg $::LEVEL5 "Status of variables after processing the config file:"
4911  logMsg $::LEVEL5 "    Host:      $ConfigFileHost"
4912  logMsg $::LEVEL5 "    Port:      $ConfigFilePort"
4913  logMsg $::LEVEL5 "    oldHost:   $ConfigFileoldHost"
4914  logMsg $::LEVEL5 "    oldPort:   $ConfigFileoldPort"
4915  logMsg $::LEVEL5 "    Hosts:     $ConfigFileHosts"
4916  logMsg $::LEVEL5 "    HostIndex: $ConfigFileHostIndex"
4917
4918  if {!$NoGUI} {
4919  logMsg $::LEVEL5 ""
4920  logMsg $::LEVEL5 "Status of variables after processing RC file:"
4921  logMsg $::LEVEL5 "    Host:      $RCfileHost"
4922  logMsg $::LEVEL5 "    Port:      $RCfilePort"
4923  logMsg $::LEVEL5 "    oldHost:   $RCfileoldHost"
4924  logMsg $::LEVEL5 "    oldPort:   $RCfileoldPort"
4925  logMsg $::LEVEL5 "    Hosts:     $RCfileHosts"
4926  logMsg $::LEVEL5 "    HostIndex: $RCfileHostIndex"
4927  logMsg $::LEVEL5 "    ThemeName: $RCfileThemeName"
4928  }
4929
4930  logMsg $::LEVEL5 ""
4931  logMsg $::LEVEL5 "Status of variables after processing command line arguments:"
4932  logMsg $::LEVEL5 "    Host:      $ArgHost"
4933  logMsg $::LEVEL5 "    Port:      $ArgPort"
4934  logMsg $::LEVEL5 "    oldHost:   $ArgoldHost"
4935  logMsg $::LEVEL5 "    oldPort:   $ArgoldPort"
4936  logMsg $::LEVEL5 "    Hosts:     $ArgHosts"
4937  logMsg $::LEVEL5 "    HostIndex: $ArgHostIndex"
4938  logMsg $::LEVEL5 ""
4939
4940if {$NoGUI} doPID
4941connectCID
4942if {!$NoGUI} {bind .im <KeyPress-Return> handleGUIMSG}
4943
4944# enter event loop
4945vwait forever
4946