File: symbol_viewer.tcl

package info (click to toggle)
mcu8051ide 1.4.9-1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye, buster, sid
  • size: 18,820 kB
  • sloc: tcl: 94,956; xml: 2,122; sh: 2,114; asm: 246; ansic: 96; makefile: 30; awk: 18
file content (856 lines) | stat: -rw-r--r-- 26,924 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
#!/usr/bin/tclsh
# Part of MCU 8051 IDE ( http://https://sourceforge.net/projects/mcu8051ide/ )

############################################################################
#    Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 by Martin Ošmera     #
#    martin.osmera@gmail.com                                               #
#                                                                          #
#    Copyright (C) 2014 by Moravia Microsystems, s.r.o.                    #
#    martin.osmera@gmail.com                                #
#                                                                          #
#    This program is free software; you can redistribute it and#or modify  #
#    it under the terms of the GNU General Public License as published by  #
#    the Free Software Foundation; either version 2 of the License, or     #
#    (at your option) any later version.                                   #
#                                                                          #
#    This program is distributed in the hope that it will be useful,       #
#    but WITHOUT ANY WARRANTY; without even the implied warranty of        #
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         #
#    GNU General Public License for more details.                          #
#                                                                          #
#    You should have received a copy of the GNU General Public License     #
#    along with this program; if not, write to the                         #
#    Free Software Foundation, Inc.,                                       #
#    59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             #
############################################################################

# >>> File inclusion guard
if { ! [ info exists _SYMBOL_VIEWER_TCL ] } {
set _SYMBOL_VIEWER_TCL _
# <<< File inclusion guard

# --------------------------------------------------------------------------
# DESCRIPTION
# Implements assembly language symbols viewer (from code listing)
# --------------------------------------------------------------------------

class SymbolViewer {
	## Class variables
	 # Int: Counter of object intances
	public common count	0
	 # Font: Just normal font used in the table
	public common normal_font	[font create			\
		-family $::DEFAULT_FIXED_FONT			\
		-size [expr {int(-14 * $::font_size_factor)}]	\
		-weight normal					\
	]
	 # Font: Bold font (the same size as $normal_font)
	public common bold_font	[font create			\
		-family $::DEFAULT_FIXED_FONT			\
		-size [expr {int(-14 * $::font_size_factor)}]	\
		-weight bold					\
	]
	if {$::MICROSOFT_WINDOWS} {
		# it's better to do not use bold font for this purpose on MS Windows®
		set bold_font $normal_font
	}
	 # Dialog configuration
	public common config_list	$::CONFIG(SYMBOL_VIEWER_CONFIG)

	## Private object variables
	private variable obj_idx			;# Int: Current object number
	private variable symbol_table_data	{}	;# List: Data loaded from the code listing (see func. open_file)
	private variable current_line		{}	;# List: Current line data (selected lide in the table)
	private variable opened_file		{}	;# String: Full file name of the currently loaded code listing file
	private variable win				;# Widget: Dialog window
	private variable menu				;# Widget: Popup menu for the text widget
	private variable main_frame			;# Widget: Dialog main frame
	private variable reload_but			;# Widget: Button "Reload"
	private variable search_entry			;# Widget: EntryBox "Search"
	private variable clear_but			;# Widget: Button "Clear search entrybox"
	private variable status_bar_lbl			;# Widget: Status bar label widget
	private variable opened_file_lbl		;# Widget: Label on statusbar showing name of currently opened file
	private variable text_widget			;# Widget: Text widget containing the table of symbols

	constructor {} {
		# Create dialog window
		set win [toplevel .symbolviewer$count -class {Defined symbols} -bg ${::COMMON_BG_COLOR}]
		set obj_idx $count
		incr count

		# Create dialog GUI
		set main_frame [frame $win.main_frame]
		create_gui	;# Create widgets
		create_menus	;# Create menus
		create_tags	;# Create text tags
		create_bindings	;# Set event bindings

		# Set values for checkboxes in panel "Display"
		set i 0
		foreach name {DATA IDATA XDATA CODE BIT Number used unused} {
			set ::SymbolViewer::display_${obj_idx}($name) [lindex $config_list $i]
			incr i
		}
		# Set values for radiobuttons in panel "Sort by"
		set ::SymbolViewer::sort_by_${obj_idx}		[lindex $config_list $i]
		incr i
		set ::SymbolViewer::sort_by_order_${obj_idx}	[lindex $config_list $i]
		incr i

		# Finalize GUI
		pack $main_frame -fill both -expand 1 -padx 5 -pady 5
		bindtags $win [list $win Toplevel all .]
		focus -force $search_entry

		# Configure dialog window
		wm iconphoto $win ::ICONS::16::symbol
		wm title $win [mc "Assembly symbol table - MCU 8051 IDE"]
		wm minsize $win 520 350
		wm protocol $win WM_DELETE_WINDOW "$this close_window"
		update
		catch {
			wm geometry $win [regsub {^\=?\d+x\d+} [lindex $config_list $i]	\
				[regsub {\+\d+\+\d+$} [wm geometry $win] {}]	\
			]
		}

		# Set ...
		incr i
		set ::SymbolViewer::display_${obj_idx}(Special)	[lindex $config_list $i]
		if {[subst -nocommands "\$::SymbolViewer::display_${obj_idx}(Special)"] == {}} {
			set ::SymbolViewer::display_${obj_idx}(Special) 1
		}
	}

	destructor {
		# Save tool configuration
		if {[llength $config_list] < 12} {
			set config_list [list {} {} {} {} {} {} {} {} {} {} {} {}]
		}
		set i 0
		foreach name {DATA IDATA XDATA CODE BIT Number used unused} {
			lset config_list $i [subst -nocommands "\$::SymbolViewer::display_${obj_idx}($name)"]
			incr i
		}
		lset config_list $i [subst -nocommands "\$::SymbolViewer::sort_by_${obj_idx}"]
		incr i
		lset config_list $i [subst -nocommands "\$::SymbolViewer::sort_by_order_${obj_idx}"]
		incr i
		lset config_list $i [wm geometry $win]
		incr i
		lset config_list $i [subst -nocommands "\$::SymbolViewer::display_${obj_idx}(Special)"]

		# Clean up
		unset ::SymbolViewer::sort_by_${obj_idx}
		unset ::SymbolViewer::sort_by_order_${obj_idx}
		array unset ::SymbolViewer::display_${obj_idx}

		# Remove dialog window
		destroy $win
	}

	## Create menus
	 # @return void
	private method create_menus {} {
		## Create text widget popup menu
		set menu [menu $text_widget.menu]
		$menu add command -label [mc "Copy symbol name"] -compound left	\
			-underline 12 -command "$this text_copy_proc name"
		$menu add command -label [mc "Copy hex value"] -compound left	\
			-underline 5 -command "$this text_copy_proc hex"
		$menu add command -label [mc "Copy dec value"] -compound left	\
			-underline 5 -command "$this text_copy_proc dec"
		$menu add separator
		$menu add command -label [mc "Copy line"] -compound left	\
			-underline 1 -command "$this text_copy_proc line"	\
			-image ::ICONS::16::editcopy -accelerator {Ctrl+C}
	}

	## Set event bindings for window widgets
	 # @return void
	private method create_bindings {} {
		bind $text_widget <ButtonRelease-3>	"$this invoke_popup_menu %X %Y %x %y; break"
		bind $text_widget <<Selection>>		"false_selection $text_widget; break"
		bind $text_widget <Button-1>		"focus %W; $this select_line %x %y"
		bind $text_widget <Key-Down>		"$this key_down"
		bind $text_widget <Key-Up>		"$this key_up"
		bind $text_widget <Control-Key-c>	"$this text_copy_proc line; break"

		bind $win <Control-Key-o>		"$this open_file_dialog; break"
		bind $win <Key-F5>			"$this reload; break"
		bind $win <Control-Key-q>		"$this close_window; break"

		bindtags $search_entry	[list $search_entry TEntry $win all .]
		bindtags $text_widget	[list $text_widget Text $win all .]
	}

	## Create text tags
	 # @return void
	private method create_tags {} {
		$text_widget tag configure type_DATA	-foreground {#00DD00}
		$text_widget tag configure type_IDATA	-foreground {#0000DD}
		$text_widget tag configure type_XDATA	-foreground {#DD0000}
		$text_widget tag configure type_CODE	-foreground {#00DDDD}
		$text_widget tag configure type_BIT	-foreground {#AA8800}
		$text_widget tag configure type_Special	-foreground {#AA00FF}
		$text_widget tag configure type_Number	-foreground {#DD00DD}
		$text_widget tag configure used_YES	-foreground {#00DD00}
		$text_widget tag configure used_NO	-foreground {#DD0000}
		$text_widget tag configure tag_sel	-background {#DDDDDD} -font $bold_font
		$text_widget tag configure nth_row	-background ${::COMMON_BG_COLOR}

		$text_widget tag raise tag_sel nth_row
	}

	## Create window widgets
	 # @return void
	private method create_gui {} {
		# Create window frames
		set top_frame		[frame $main_frame.top_frame]		;# Button "Open"+"Reload" + Search bar
		set middle_frame	[frame $main_frame.middle_frame]	;# Table of symbols
		set bottom_frame	[frame $main_frame.bottom_frame]	;# Display options
		set sbar_frame		[frame $main_frame.sbar_frame]		;# Status bar

		## Create status bar
		set status_bar_lbl [label $sbar_frame.main_lbl	\
			-justify left -anchor w			\
		]
		set opened_file_lbl [label $sbar_frame.opened_file_lbl -fg {#0000DD}]
		pack $status_bar_lbl -fill x -side left
		pack $opened_file_lbl -side right -after $status_bar_lbl

		## Create top frame
		 # Button "Open file"
		pack [ttk::button $top_frame.open_but		\
			-image ::ICONS::16::fileopen		\
			-text [mc "Open *.LST"]			\
			-compound left 				\
			-command "$this open_file_dialog"	\
		] -side left
		DynamicHelp::add $top_frame.open_but	\
			-text [mc "Load table of symbols from list file (*.lst)\n\tOnly for: ASEM-51, MCU8051IDE and ASM51"]
		set_locat_status_tip $top_frame.open_but [mc "Open code listing"]
		 # Button "Reload"
		set reload_but [ttk::button $top_frame.reload_but	\
			-image ::ICONS::16::reload			\
			-text [mc "Reload"]				\
			-compound left 					\
			-command "$this reload"				\
			-state disabled					\
		]
		pack $reload_but -side left -padx 5
		set_locat_status_tip $reload_but [mc "Reload opened file"]
		 ## Create search bar
		set top_r_frame [frame $top_frame.right_frame]
		  # - Label
		pack [label $top_r_frame.search_lbl	\
			-text [mc "Search:"]		\
		] -side left
		  # - Entry
		set search_entry [ttk::entry $top_r_frame.search_entry		\
			-validate all						\
			-validatecommand "$this search_validate %P"		\
		]
		DynamicHelp::add $search_entry	\
			-text [mc "Search for symbol by its name or value"]
		set_locat_status_tip $search_entry [mc "Search for symbol"]
		pack $search_entry -side left
		  # - Button
		set clear_but [ttk::button $top_r_frame.clear_but	\
			-state disabled					\
			-style Flat.TButton				\
			-image ::ICONS::16::clear_left			\
			-command "$search_entry delete 0 end"		\
		]
		set_locat_status_tip $clear_but [mc "Clear search entry box"]
		pack $clear_but -side left
		pack $top_r_frame -side right

		## Create table of symbols
		set middle_l_frame [frame $middle_frame.left_frame -bd 1 -relief sunken]
		pack [label $middle_l_frame.header_lbl			\
			-bg white -padx 0 -pady 0 -width 0		\
			-text [mc "Symbol\t\t\t\tType\tHEX\tDEC\tUsed"]	\
			-font $bold_font -anchor w -justify left	\
		] -fill x
		pack [ttk::separator $middle_l_frame.sep	\
			-orient horizontal			\
		] -fill x
		set text_widget [text $middle_l_frame.text		\
			-bg white -width 0 -height 0 -bd 0 -relief flat	\
			-yscrollcommand "$middle_frame.scrollbar set"	\
			-cursor left_ptr -font $normal_font	\
			-state disabled					\
		]
		pack $text_widget -fill both -expand 1

		pack $middle_l_frame -side left -fill both -expand 1
		pack [ttk::scrollbar $middle_frame.scrollbar		\
			-orient vertical -command "$text_widget yview"	\
		] -fill y -side right -after $middle_l_frame

		## Create display options
		set main_opt_frame [ttk::labelframe	\
			$bottom_frame.main_opt_frm	\
			-text [mc "Display"]		\
			-padding 10			\
		]
		set row 0
		set col 0
		set i 0
		foreach name {DATA IDATA XDATA CODE BIT Number Special} {
			if {$col > 2} {
				set col 0
				incr row
			}
			grid [checkbutton $main_opt_frame.cb_x_$i			\
				-text $name -onvalue 1 -offvalue 0			\
				-variable ::SymbolViewer::display_${obj_idx}($name)	\
				-command "$this refresh"				\
			] -sticky w -row $row -column $col
			incr i
			incr col
		}
		incr row
		grid [ttk::separator $main_opt_frame.sep	\
			-orient horizontal			\
		] -sticky we -row $row -column 0 -columnspan 3
		incr row
		grid [checkbutton $main_opt_frame.cb_x_us			\
			-text [mc "Used symbols"] -onvalue 1 -offvalue 0	\
			-variable ::SymbolViewer::display_${obj_idx}(used)	\
			-command "$this refresh"				\
		] -sticky w -row $row -column 0 -columnspan 3
		incr row
		grid [checkbutton $main_opt_frame.cb_x_uus			\
			-text [mc "Unused symbols"] -onvalue 1 -offvalue 0	\
			-variable ::SymbolViewer::display_${obj_idx}(unused)	\
			-command "$this refresh"				\
		] -sticky w -row $row -column 0 -columnspan 3
		pack $main_opt_frame -side left -fill y -anchor n -padx 5

		# Create frame "Sort by"
		set sort_by_frame [ttk::labelframe	\
			$bottom_frame.sort_by_frm	\
			-text [mc "Sort by"]		\
			-padding 10			\
		]
		set row 0
		set col 0
		set i 0
		foreach name {{Symbol name} Type {Hex value} {Dec value} {Usage}} {
			if {$col > 2} {
				set col 0
				incr row
			}
			grid [radiobutton $sort_by_frame.rb_x_$i		\
				-text $name -value $i				\
				-variable ::SymbolViewer::sort_by_${obj_idx}	\
				-command "$this refresh"			\
			] -sticky w -row $row -column $col
			incr i
			incr col
		}
		incr row
		grid [ttk::separator $sort_by_frame.sep	\
			-orient horizontal		\
		] -sticky we -row $row -column 0 -columnspan 3
		incr row
		grid [radiobutton $sort_by_frame.rb_x_inc			\
			-text [mc "Incremental order"] -value 0			\
			-variable ::SymbolViewer::sort_by_order_${obj_idx}	\
			-command "$this refresh"				\
		] -sticky w -row $row -column 0 -columnspan 3
		incr row
		grid [radiobutton $sort_by_frame.rb_x_dec			\
			-text [mc "Decremental order"] -value 1			\
			-variable ::SymbolViewer::sort_by_order_${obj_idx}	\
			-command "$this refresh"				\
		] -sticky w -row $row -column 0 -columnspan 3
		pack $sort_by_frame -side left -fill y -anchor n -padx 10

		# Pack window frames
		pack $top_frame		-fill x
		pack $middle_frame	-fill both -expand 1 -pady 7
		pack $bottom_frame	-fill x
		pack $sbar_frame	-fill x
	}

	## Set local statusbar tip
	 # @parm Widget widget	- Target widget
	 # @parm String text	- Statusbar tip itselft
	 # @return void
	private method set_locat_status_tip {widget text} {
		bind $widget <Enter> [list $status_bar_lbl configure -text $text]
		bind $widget <Leave> [list $status_bar_lbl configure -text {}]
	}

	## Close dialog window
	 # @return void
	public method close_window {} {
		::itcl::delete object $this
	}

	## Invoke file selection dialog to load a new table of symbols for LST file
	 # @return void
	public method open_file_dialog {} {
		# Determinate initial directory
		if {$opened_file == {}} {
			if {${::X::project_menu_locked}} {
				set directory {~}
			} else {
				set directory [${::X::actualProject} cget -projectPath]
			}
		} else {
			set directory [file dirname $opened_file]
		}

		# Invoke project selection dialog
		KIFSD::FSD ::fsd	 				\
			-title [mc "Load symbol table - MCU 8051 IDE"]	\
			-directory $directory -master $win		\
			-defaultmask 0 -multiple 0 -filetypes [list		\
				[list [mc "Code listing"]	{*.lst}	]	\
				[list [mc "All files"]		{*}	]	\
			]

		# Open the selected after press of OK button
		::fsd setokcmd "
			::fsd deactivate
			$this open_file 0 \[::fsd get\]"

		::fsd activate	;# Activate the dialog
	}

	## Reload opened file if any
	 # @return void
	public method reload {} {
		open_file 0 $opened_file
	}

	## Try to open LST file and load table of symbols from it
	 # @parm Bool ignore_errors	- Ignore erros while opening the file
	 # @parm String filename	- Name of file to load
	 # @return void
	public method open_file {ignore_errors filename} {
		if {[catch {
			set file [open $filename r]
		}]} then {
			if {!$ignore_errors} {
				tk_messageBox		\
					-parent $win	\
					-type ok	\
					-icon warning	\
					-title [mc "Invalid file"]	\
					-message [mc "Unable to use selected file. Please check your permissions. File: '%s'" $filename]
			}
			return
		}

		# Parse file
		set symbol_table_data {}
		set read_line 0
		set line {}
		set name {}
		set addr {}
		set type {}
		set used 1
		while {![eof $file]} {
			set used 1
			set line [gets $file]

			# Empty line - stop reading
			if {![string length [string trimright $line "  \f"]]} {
				set read_line 0
				continue

			# MCU 8051 IDE Assembler symbol table
			} elseif {![string first {SYMBOL TABLE:} $line]} {
				set read_line 1
				continue

			# ASEM-51 Assembler symbol table
			} elseif {![string first {------------------------------------------------------------} $line]} {
				set read_line 2
				continue
			}


			# MCU 8051 IDE Assembler symbol
			if {$read_line == 1} {
				if {![regexp {^(\?\?)?\w+} $line name]} {
					continue
				}
				if {![regexp {[\w\s]+$} $line line]} {
					continue
				}
				set type [lindex $line 0]
				if {$type == {S}} {
					set addr [lindex $line end]
				} else {
					set addr [string replace [lindex $line 2] end end]
				}

				# Determinate whether symbol is used
				if {[lindex $line end-1] == {NOT} || [lindex $line end-2] == {NOT}} {
					set used 0
				}

			# ASEM-51 Assembler symbol
			} elseif {$read_line == 2} {
				# Remove dangerous characters
				regsub -all {\{\}\"\"} $line {} line

				if {[llength $line] < 4} {
					set used 0
				}
				set addr [lindex $line 2]
				set name [lindex $line 0]
				set type [string index [lindex $line 1] 0]

				# Hexadecimal address must be 4 characters long
				if {!($type == {S} || $type == {R})} {
					set addr "[string repeat 0 [expr {4 - [string length $addr]}]]$addr"
				}

			# This line is not a part of symbol table
			} else {
				continue
			}

			# Address must be a valid hexadecimal value
			if {![string is xdigit -strict $addr] && !($type == {S} || $type == {R})} {
				continue
			}

			# Create new register watch
			if {[string length $name] > 31} {
				set name [string range $name 0 27]
				append name {...}
			}

			if {$type == {S} || $type == {R}} {
				lappend symbol_table_data [list $name $type $addr {0} $used]
			} else {
				lappend symbol_table_data [list $name $type $addr [expr "0x$addr"] $used]
			}
		}

		set opened_file $filename
		$reload_but configure -state normal
		$opened_file_lbl configure -text [file tail $filename]
		close $file
		refresh
	}

	## Sort list of loaded symbols according to user settings
	 # @return void
	private method sort_table {} {
		if {[subst -nocommands "\$::SymbolViewer::sort_by_order_${obj_idx}"]} {
			set order {-decreasing}
		} else {
			set order {-increasing}
		}
		set index [subst -nocommands "\$::SymbolViewer::sort_by_${obj_idx}"]
		if {$index == 3} {
			set type {-integer}
		} else {
			set type {-dictionary}
		}

		set symbol_table_data [lsort $order $type -index $index $symbol_table_data]
	}

	## Filter record with the given type and use flag
	 # @parm Char type	- Record type
	 # @parm Bool used	- Used flag
	 # @return List - {$long_type $long_used} or {0}
	private method filter_record {type used} {
		# Adjust symbol type
		switch -- $type {
			{D} {set type {DATA}}
			{I} {set type {IDATA}}
			{X} {set type {XDATA}}
			{C} {set type {CODE}}
			{B} {set type {BIT}}
			{N} {set type {Number}}
			{S} {set type {Special}}
			{R} {set type {Special}}
		}
		if {![subst -nocommands "\$::SymbolViewer::display_${obj_idx}($type)"]} {
			return 0
		}

		# Adjust flag USED
		if {$used} {
			if {![subst -nocommands "\$::SymbolViewer::display_${obj_idx}(used)"]} {
				return 0
			}
			set used [mc "YES"]
		} else {
			if {![subst -nocommands "\$::SymbolViewer::display_${obj_idx}(unused)"]} {
				return 0
			}
			set used [mc "NO"]
		}

		return [list $type $used]
	}

	## Refresh table of symbols (reload contents of the text widget from $symbol_table_data)
	 # @return void
	public method refresh {} {
		# There must be something loaded
		if {$opened_file == {}} {
			return
		}

		# Save data of the selected line
		if {$current_line != {}} {
			set current_line [$text_widget get $current_line.0 [list $current_line.0 lineend]]
			set current_line [list						\
				[lindex $current_line 0] [lindex $current_line 1]	\
				[lindex $current_line 2] [lindex $current_line 3]	\
				[lindex $current_line 4]				\
			]

			lset current_line 1 [string index [lindex $current_line 1] 0]
			if {[lindex $current_line 4] == {YES}} {
				lset current_line 4 1
			} else {
				lset current_line 4 0
			}
		}

		# Sort loaded table of symbols
		sort_table

		# Clear the text widget
		$text_widget configure -state normal
		$text_widget delete 0.0 end

		# Load table of symbols to the text widget
		set idx 0	;# Int: Symbol number (just index in the table, nothing more)
		set name {}	;# String: Symbol name defined in source code
		set type {}	;# Char: Symbol type (see func. code)
		set hexv {}	;# String: Hexadecimal symbol value
		set decv {}	;# Int: Decimal symbol value
		set used {}	;# Bool: Symbol used in source code
		set cur_found 0	;# Bool: Current line found
		foreach symbol_def $symbol_table_data {
			set name [lindex $symbol_def 0]
			set type [lindex $symbol_def 1]
			set hexv [lindex $symbol_def 2]
			set decv [lindex $symbol_def 3]
			set used [lindex $symbol_def 4]

			# Filter record
			set used [filter_record $type $used]
			if {$used == {0}} {
				continue
			}
			set type [lindex $used 0]
			set used [lindex $used 1]

			# Insert new record into the table
			$text_widget insert insert $name
			$text_widget insert insert [string repeat { } [expr {32 - [string length $name]}]]
			$text_widget insert insert $type
			$text_widget insert insert [string repeat { } [expr {8 - [string length $type]}]]
			$text_widget tag add type_${type} insert-8c insert
			if {$type == {Special}} {
				$text_widget insert insert $hexv
				$text_widget insert insert [string repeat { } [expr {16 - [string length $hexv]}]]
			} else {
				$text_widget insert insert $hexv
				$text_widget insert insert {    }
				$text_widget insert insert $decv
				$text_widget insert insert [string repeat { } [expr {8 - [string length $decv]}]]
			}
			$text_widget insert insert $used
			$text_widget tag add used_${used} insert-3c insert
			$text_widget insert insert "\n"
			if {!($idx % 3)} {
				$text_widget tag add nth_row insert-1l insert
			}

			# Try to find the selected line
			if {!$cur_found && [string equal $current_line $symbol_def]} {
				set cur_found 1
				set current_line $idx
				incr current_line
			}

			incr idx
		}

		# Restore selection
		if {$cur_found} {
			$text_widget tag add tag_sel $current_line.0 $current_line.0+1l
			$text_widget see $current_line.0
		} else {
			set current_line {}
		}

		# Disable the text widget and clear search entrybox
		$text_widget configure -state disabled
		$search_entry delete 0 end
	}

	## Select line in the table (event: <Button-1>)
	 # @parm Int x - Relative X coordinate
	 # @parm Int y - Relative Y coordinate
	 # @return void
	public method select_line {x y} {
		set current_line [expr {int([$text_widget index @$x,$y])}]
		if {$current_line == int([$text_widget index end])-1} {
			set current_line {}
			return
		}
		$search_entry delete 0 end
		$text_widget tag remove tag_sel 0.0 end
		$text_widget tag add tag_sel $current_line.0 $current_line.0+1l
	}

	## Search entrybox validator
	 # @parm String string	- String to validate (search for)
	 # @return Bool - always 1
	public method search_validate {string} {
		# Not empty string
		if {[string length $string]} {
			set string [string tolower [string trimleft $string 0]]
			$clear_but configure -state normal
		# Empty string -> abort
		} else {
			$search_entry configure -style TEntry
			$clear_but configure -state disabled
			return 1
		}

		# Search in the table
		set i 0
		set found_idx -1
		foreach symbol_def $symbol_table_data {
			set name [string tolower [lindex $symbol_def 0]]
			set type [lindex $symbol_def 1]
			set hexv [string trimleft [string tolower [lindex $symbol_def 2]] 0]
			set decv [lindex $symbol_def 3]
			set used [lindex $symbol_def 4]

			if {[filter_record $type $used] == {0}} {
				continue
			}

			if {![string first $string $name]} {
				set found_idx $i
				break
			}
			if {![string first $string $hexv]} {
				set found_idx $i
				break
			}
			if {![string first $string $decv]} {
				set found_idx $i
				break
			}

			incr i
		}

		set current_line {}
		$text_widget tag remove tag_sel 0.0 end

		# String not found
		if {$found_idx == -1} {
			$search_entry configure -style StringNotFound.TEntry
		# String found
		} else {
			set current_line [expr {$found_idx + 1}]
			$text_widget tag add tag_sel $current_line.0 $current_line.0+1l
			$text_widget see $current_line.0
			$search_entry configure -style StringFound.TEntry
		}

		return 1
	}

	## Event handler of the text widget <Key-Up>
	 # Select line above the currently selected one
	 # @return void
	public method key_up {} {
		if {$current_line == {} || $current_line < 2} {
			return
		}
		incr current_line -1
		$text_widget tag remove tag_sel 0.0 end
		$text_widget tag add tag_sel $current_line.0 $current_line.0+1l
		$search_entry delete 0 end
	}

	## Event handler of the text widget <Key-Down>
	 # Select line below the currently selected one
	 # @return void
	public method key_down {} {
		if {$current_line == {} || $current_line >= ([$text_widget index end] - 2)} {
			return
		}
		incr current_line
		$text_widget tag remove tag_sel 0.0 end
		$text_widget tag add tag_sel $current_line.0 $current_line.0+1l
		$search_entry delete 0 end
	}

	## Invoke text widget popup menu
	 # @parm Int X - Absolute mouse pointer X coordinate
	 # @parm Int Y - Absolute mouse pointer X coordinate
	 # @parm Int x - Relative mouse pointer X coordinate
	 # @parm Int y - Relative mouse pointer X coordinate
	 # @return void
	public method invoke_popup_menu {X Y x y} {
		select_line $x $y
		if {$current_line == {}} {
			set state disabled
		} else {
			set state normal
		}
		foreach entry {{Copy symbol name} {Copy hex value} {Copy dec value} {Copy line}} {
			$menu entryconfigure [::mc $entry] -state $state
		}
		tk_popup $menu $X $Y
	}

	## Copy piece of the table into clipboard
	 # @parm Char mode - What to copy
	 #	name	- Symbol name
	 #	hex	- Symbol hexadecimal value
	 #	dec	- Symbol decimal value
	 #	line	- Whole symbol definition
	 # @return void
	public method text_copy_proc {mode} {
		switch -- $mode {
			{name} {
				set s 0
				set e 31
			}
			{hex} {
				set s 39
				set e 44
			}
			{dec} {
				set s 47
				set e 52
			}
			{line} {
				set s 0
				set e 63
			}
		}

		clipboard clear
		clipboard append [string trim [$text_widget get $current_line.$s $current_line.$e]]
	}
}

# >>> File inclusion guard
}
# <<< File inclusion guard