forked from idl-coyote/coyote
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathcgcmdwindow__define.pro
3166 lines (2734 loc) · 136 KB
/
cgcmdwindow__define.pro
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
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
; docformat = 'rst'
;
; NAME:
; cgCmdWindow
;
; PURPOSE:
; Creates a "command" window for IDL traditional commands (Plot, Contour,
; Surface, etc. or for Coyote Graphics routines, cgPlot, cgContour, cgSurf, etc.).
; In addition, the window contents can be saved as PostScript files or as raster image
; files. If ImageMagick is installed on your machine, the raster image files can be
; created in very high quality from PostScript files. This program can be used in
; place of a normal draw widget or as a stand-alone program.
;
;******************************************************************************************;
; ;
; Copyright (c) 2011, by Fanning Software Consulting, Inc. All rights reserved. ;
; ;
; Redistribution and use in source and binary forms, with or without ;
; modification, are permitted provided that the following conditions are met: ;
; ;
; * Redistributions of source code must retain the above copyright ;
; notice, this list of conditions and the following disclaimer. ;
; * Redistributions in binary form must reproduce the above copyright ;
; notice, this list of conditions and the following disclaimer in the ;
; documentation and/or other materials provided with the distribution. ;
; * Neither the name of Fanning Software Consulting, Inc. nor the names of its ;
; contributors may be used to endorse or promote products derived from this ;
; software without specific prior written permission. ;
; ;
; THIS SOFTWARE IS PROVIDED BY FANNING SOFTWARE CONSULTING, INC. ''AS IS'' AND ANY ;
; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ;
; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT ;
; SHALL FANNING SOFTWARE CONSULTING, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, ;
; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED ;
; TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; ;
; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ;
; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ;
; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ;
; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ;
;******************************************************************************************;
;+
; Creates a "command" window for IDL traditional commands (Plot, Contour,
; Surface, etc. or for Coyote Graphics routines, cgPlot, cgContour, cgSurf, etc.).
; In addition, the window contents can be saved as PostScript files or as raster image
; files. If ImageMagick is installed on your machine, the raster image files can be
; created in very high quality from PostScript files. This program can be used in
; place of a normal draw widget or as a stand-alone program.
;
; The program is designed to work with any IDL traditional graphics routine
; that is a procedure and includes no more than four positional parameters.
; Any number of keywords can be used to specify properties of the graphical
; output. Any number of graphics commands can be "added" the the window.
; Simply use the AddCommand method to add commands.
;
; If your program does not load its own color tables, the color tables in
; effect when this program is initiated are used to display the graphics
; commands.
;
; To create PostScript output from within the program, your graphics program
; has to be written in such a way that it can work properly in the PostScript
; device. This means there are no Window commands, WSet commands, and the like
; that are not allowed in the PostScript device. Such commands are allowed in
; programs, of course, if they are "protected". Usually such protection looks
; like this::
;
; IF (!D.Flags AND 256) NE 0 THEN Window, ...
;
; The Coyote Graphics program `cgDisplay` is a good program for opening graphics
; "windows", because such PostScript protection is built into the program. In a PostScript
; device, cgDisplay produces a "window" with the same aspect ratio as the current
; display graphics window, which is an aid in producing PostScript output that
; looks like the same output in the display window.
;
; Much better looking raster files can be created from the graphics window contents,
; if the raster files are created by converting PostScript files to the raster
; file. If the ImageMagick "convert" command can be found on your machine, you
; will have the option to create raster files using this method. I *highly*
; recommend doing so, as fonts and other plot annotation will be of much higher
; quality using this method.
;
; This program has been designed to work with other Coyote Graphics routines: `cgPlot`,
; `cgContour`, `cgSurf`, and so on, although I expect it to work with any IDL
; traditional graphics routine, if the routine is well written.
;
; :Categories:
; Graphics
;
; :Author:
; FANNING SOFTWARE CONSULTING::
; David W. Fanning
; 1645 Sheely Drive
; Fort Collins, CO 80526 USA
; Phone: 970-221-0438
; E-mail: david@idlcoyote.com
; Coyote's Guide to IDL Programming: http://www.idlcoyote.com
;
; :Copyright:
; Copyright (c) 2011, Fanning Software Consulting, Inc.
;
; :History:
; Change History::
; Separated from old cgWindow program 22 Jan 2012, by David W. Fanning.
; Fixed a typo in PackageCommand method that prevented extra keywords from
; being collectd. 26 Jan 2012. DWF.
; Fixed a problem with MULTI keyword. 31 Jan 2012. DWF.
; Added a COPY method. 7 Feb 2012. DWF.
; Fixed a problem with input filenames in the AutoRasterFile method. 22 Feb 2012. DWF.
; Modified Draw_Widget creation routine to account for events that were added in
; different versions of IDL. 23 Feb 2012. DWF.
; Added ability to use IM_WIDTH keyword to set the width of raster file output
; created with ImageMagick. 3 April 2012. DWF.
; Forgot to specify the GROUP_LEADER when calling cgPS_Open. Caused cgPS_Config to
; run though its block when cgWindow was called from a blocking widget program.
; 5 June 2012. DWF.
; Added the ability to save the file name and directory of the last output file, so
; that subsequent file saves can use that last name and directory as a starting
; default filename. 11 July 2012. DWF.
; In decompling cgWindow from cgCmdWindow, I accidentally named the WASPECT keyword ASPECT. Restored
; original name in this version. 13 July 2012. DWF.
; I added a new method, ReplaceEscapeSequences, that can evaluate escape sequences in
; string parameters and keywords to call the appropriate cgSymbol value at run-time.
; This eliminates the need for alternate keywords. 27 July 2012. DWF.
; Added WDestroyObjects keyword to destroy objects parameters, if needed. 11 November 2012. DWF.
; Not adding IM_WIDTH parameter from cgWindow_GetDefs. 19 November 2012. DWF.
; Modified ReplaceEscapeSequence method to use cgCheckForSymbols. 24 November 2012. DWF.
; Modified to allow keywords to turn off messages from cgPS_Open and cgPS_Close with keywords. 27 November 2012. DWF.
; The output filename was not specified correctly when making PDF file automatically. Fixed. 2 Dec 2012. DWF.
; Renamed the MULTI keyword to WMULTI, as it was suppose to be. 11 Feb 2013. DWF.
; Misspelled keyword in PackageCommand method. 21 February 2013. DWF.
; Modified GUI so there is only one Raster Image File choice, depending on if ImageMagick is
; installed. 21 May 2013. DWF.
; I made the IM_WIDTH field in the object a pointer, so it could remain an undefined variable,
; if necessary. 20 FEB 2014. DWF.
; Typo in the spelling of DESTROYOBJECTS when restoring a graphic visualization. Fixed. 20 Feb 2015. DWF.
;-
;+
; This method initializes the object that is at the heart of the Coyote Graphics system.
; It allows you to both configure the draw widget for events and to load a command into
; the command window.
;
; :Params:
; parent: in, optional
; The parent base widget for this draw widget object. If not defined,
; the program will create its own top-level base widget.
;
; :Keywords:
; altps_Keywords: in, optional, type=string
; A structure containing alternative keyword names (as tags) and values for
; those keywords to be used when the current device is the PostScript device.
; See http://www.idlcoyote.com/cg_tips/kwexpressions.php and the examples
; below for details on how to use this keyword.
; altps_Params: in, optional, type=IntArr(3)
; A structure containing alternative parameter values to be used when
; the current device is the PostScript device. Structure names are restricted
; to the names "P1", "P2", "P3" and "P4"to correspond to the equivalent positional
; parameter. See http://www.idlcoyote.com/cg_tips/kwexpressions.php and the
; examples below for details on how to use this keyword.
; background: in, optional, type=varies, default=!P.Background
; The background color of the window. Specifying a background color
; automatically sets the WErase keyword.
; button_events: in, optional, type=boolean
; Set this keyword to turn button events on for the draw widget.
; command: in, required, type=string
; The graphics procedure command to be executed. This parameter
; must be a string and the the command must be a procedure. Examples
; are 'Surface', 'Contour', 'Plot', 'cgPlot', cgContour, etc.
; cmddelay: in, optional, type=float
; If this keyword is set to a value other than zero, there will be a
; delay of this many seconds between command execution. This will permit
; "movies" of command sequences to be displayed.
; drop_events: in, optional, type=boolean
; Set this keyword to turn drop events on for the draw widget.
; eraseit: in, optional, type=boolean, default=0
; Set this keyword to cause the window to be erased before graphics commands
; are drawn. This may need to be set, for example, to display images.
; event_handler: in, optional, type=string
; The name of an event handler procedure to accept events from
; the draw widget. Write the event handler procedure with two
; positional parameters. The first will be the event structure
; returned from the draw widget. The second will be the cgCmdWindow
; object reference (i.e., self) to allow you to manipulate the
; command window.
; group_leader: in, optional
; The identifier of a widget to serve as a group leader for this program.
; If the group leader is destroyed, this program is also destroyed. Used
; when calling this program from another widget program.
; keyboard_events: in, optional, type=boolean
; Set this keyword to turn keyboard events on for the draw widget.
; method: in, optional, type=boolean, default=0
; Set this keyword if the command is an object method call rather than a
; procedure call. If this keyword is set, the first positional parameter, p1,
; must be present and must be a valid object reference.
; motion_events: in, optional, type=boolean
; Set this keyword to turn motion events on for the draw widget.
; oxmargin: in, optional, type=float
; A two-element array indicating the left and right X outside margins for the
; graphical display. Used only when doing multiple plots with `WMulti`.
; oymargin: in, optional, type=float
; A two-element array indicating the bottom and top Y outside margins for the
; graphical display. Used only when doing multiple plots with `WMulti`.
; p1: in, optional, type=any
; The first positional parameter appropriate for the graphics command.
; p2: in, optional, type=any
; The second positional parameter appropriate for the graphics command.
; p3: in, optional, type=any
; The third positional parameter appropriate for the graphics command.
; p4: in, optional, type=any
; The fourth positional parameter appropriate for the graphics command.
; replacecmd: in, optional, type=boolean, default=0
; Set this keyword to replace a graphics command from an cgWindow.
; If CmdIndex is undefined, *all* commands in the window are replaced. Use
; WinID to identify the cgWindow you are interested in. If WinID is
; undefined, the last cgWindow created is used for the replacement.
; storage: in, optional, type=any
; Any user-defined IDL variable will be stored in the object storage location.
; Defined here for convenience. Same as `Storage` keyword for the SetProperty method.
; tracking_events: in, optional, type=boolean
; Set this keyword to turn tracking events on for the draw widget.
; waspect: in, optional, type=float, default=normal
; Set this keyword to the aspect ratio you would like the window to have.
; The aspect ratio is calculated as (ysize/xsize). Must be a float value.
; If this keyword is set, the window will maintain this aspect ratio,
; even when it is resized.
; wdestroyobjects: in, optional, type=boolean, default=0
; If this keyword is set, and any of the input parameters p1-p4 is an object,
; the object parameter will be destroyed when the window is destroyed.
; wheel_events: in, optional, type=boolean
; Set this keyword to turn wheel events on for the draw widget.
; wmulti: in, optional, type=intarr(5)
; Set this keyword in exactly the same way you would set the !P.Multi keyword.
; It will allow you to display multi-plots in the cgWindow graphics window.
; wtitle: in, optional, type=string, default='Resizeable Graphics Window'
; The title of the graphics window if the program creates its own top-level
; base widget. A window index number is appended to the title so multiple cgWindow
; programs can be selected. Also used to register the widget program and as the
; title of the object when it is stored.
; wxpos: in, optional, type=integer, default=5
; The x offset in device coordinates of the window from the upper-left corner of
; the display, if the program creates its own top-level base. Otherwise, this keyword
; is ignored.
; wypos: in, optional, type=integer, default=5
; The y offset in device coordinates of the window from the upper-left corner of
; the display, if the program creates its own top-level base. Otherwise, this keyword
; is ignored.
; wxsize: in, optional, type=integer, default=640
; The x size in device coordinates of the graphics window.
; wysize: in, optional, type=integer, default=512
; The y size in device coordinates of the the graphics window.
; _extra: in, optional
; The "extra" keywords for the command that is being added to the window.
;-
FUNCTION cgCmdWindow::Init, parent, $
AltPS_Keywords=altps_Keywords, $ ; A structure of PostScript alternative keywords and values.
AltPS_Params=altps_Params, $ ; A structure of PostScript alternative parameters and values.
Background = background, $ ; The background color. Set to !P.Background by default.
Button_Events=button_events, $ ; Set this keyword to allow button events in the draw widget.
CmdDelay=cmdDelay, $ ; Set this keyword to a value to "wait" before executing the next command.
Command=command, $ ; The graphics "command" to execute.
Drop_Events=drop_events, $ ; Set this keyword to allow drop events in the draw widget.
EraseIt = eraseit, $ ; Set this keyword to erase the display before executing the command.
Event_Handler=event_handler, $ ; Set this keyword to the name of an event handler procedure to handle draw widget events.
Group_Leader = group_leader, $ ; The group leader of the cgWindow program.
Keyboard_Events=keyboard_events, $ ; Set this keyword to allow keyboard events in the draw widget.
Method=method, $ ; If set, will use CALL_METHOD instead of CALL_PROCEDURE to execute command.
Motion_Events=motion_events, $ ; Set this keyword to allow motion events in the draw widget.
OXMargin = xomargin, $ ; Set the !X.OMargin. A two element array.
OYMargin = yomargin, $ ; Set the !Y.OMargin. A two element array
P1=p1, $ ; The first postitional parameter in a graphics command loaded for display.
P2=p2, $ ; The sedond postitional parameter in a graphics command loaded for display.
P3=p3, $ ; The third postitional parameter in a graphics command loaded for display.
P4=p4, $ ; The fourth postitional parameter in a graphics command loaded for display.
ReplaceCmd=replacecmd, $ ; Replace the current command and execute in the current window.
Storage=storage, $ ; A storage pointer location. Used like a user value in a widget.
Tracking_Events=tracking_events, $ ; Set this keyword to allow tracking events in the draw widget.
WAspect = waspect, $ ; Set the window aspect ratio to this value.
WDestroyObjects=wdestroyobjects, $ ; Set this keyword to destroy object parameters upon exit.
Wheel_Events=wheel_events, $ ; Set this keyword to allow wheel events in the draw widget.
WMulti = wmulti, $ ; Set this in the same way !P.Multi is used.
WTitle = title, $ ; The window title.
WXPos = xpos, $ ; The X offset of the window on the display. The window is centered if not set.
WXSize = xsize, $ ; The X size of the cgWindow graphics window in pixels. By default: 400.
WYPos = ypos, $ ; The Y offset of the window on the display. The window is centered if not set.
WYSize = ysize, $ ; The Y size of the cgWindow graphics window in pixels. By default: 400.
_Extra = extra ; Any extra keywords. Usually the "command" keywords.
Compile_Opt idl2
Catch, theError
IF theError NE 0 THEN BEGIN
Catch, /CANCEL
void = cgErrorMsg()
RETURN, 0
ENDIF
; Check keywords.
method = Keyword_Set(method)
; Get the global defaults.
cgWindow_GetDefs, $
AdjustSize = d_adjustsize, $ ; Adjust charsize to window size.
Background = d_background, $ ; The background color.
Delay = d_delay, $ ; The amount of delay between command execution.
EraseIt = d_eraseit, $ ; Set this keyword to erase the display before executing the commands.
Multi = d_multi, $ ; Set this in the same way !P.Multi is used.
XOMargin = d_xomargin, $ ; Set the !X.OMargin. A two element array.
YOMargin = d_yomargin, $ ; Set the !Y.OMargin. A two element array
XSize = d_xsize, $ ; The X size of the cgWindow graphics window.
YSize = d_ysize, $ ; The Y size of the cgWindow graphics window.
Title = d_title, $ ; The window title.
XPos = d_xpos, $ ; The X offset of the window on the display.
YPos = d_ypos, $ ; The Y offset of the window on the display.
Aspect = d_aspect, $ ; The aspect ratio of the window.
Palette = d_palette, $ ; The color table palette to use for the window.
; PDF properties.
PDF_Unix_Convert_Cmd = d_pdf_unix_convert_cmd, $ ; Command to convert PS to PDF.
PDF_Path = d_pdf_path, $ ; The path to the Ghostscript conversion command.
; ImageMagick Properties.
IM_Transparent = d_im_transparent, $ ; Sets the "alpha" keyword on ImageMagick convert command.
IM_Density = d_im_density, $ ; Sets the density parameter on ImageMagick convert command.
IM_Raster = d_im_raster, $ ; Create raster files via ImageMagick.
IM_Resize = d_im_resize, $ ; Sets the resize parameter on ImageMagick convert command.
IM_Options = d_im_options, $ ; Sets extra ImageMagick options on the ImageMagick convert command.
IM_Width = d_im_width, $ ; The width of the raster file output.
; PostScript properties.
PS_Decomposed = d_ps_decomposed, $ ; Sets the PostScript color mode.
PS_Delete = d_ps_delete, $ ; Delete PS file when making IM raster.
PS_Metric = d_ps_metric, $ ; Select metric measurements in PostScript output.
PS_Encapsulated = d_ps_encapsulated, $ ; Create Encapsulated PostScript output.
PS_FONT = d_ps_font, $ ; Select the font for PostScript output.
PS_CHARSIZE = d_ps_charsize, $ ; Select the character size for PostScript output.
PS_QUIET = d_ps_quiet, $ ; Select the QUIET keyword for cgPS_Open.
PS_SCALE_FACTOR = d_ps_scale_factor, $ ; Select the scale factor for PostScript output.
PS_TT_FONT = d_ps_tt_font ; Select the true-type font to use for PostScript output.
; If method is set, the first positional parameter must be present,
; and it must be a valid object reference.
IF method THEN BEGIN
IF N_Elements(p1) EQ 0 THEN $
Message, 'The parameter P1 must be present to make a method call.'
IF ~Obj_Valid(p1) THEN $
Message, 'The parameter P1 must be a valid object reference when making method calls.'
ENDIF
SetDefaultValue, waspect, d_aspect
SetDefaultValue, xsize, d_xsize
SetDefaultValue, ysize, d_ysize
SetDefaultValue, xpos, d_xpos
SetDefaultValue, ypos, d_ypos
SetDefaultValue, button_events, 0, /Boolean
SetDefaultValue, drop_events, 0, /Boolean
SetDefaultValue, keyboard_events, 0, /Boolean
SetDefaultValue, motion_events, 0, /Boolean
SetDefaultValue, tracking_events, 0, /Boolean
SetDefaultValue, wheel_events, 0, /Boolean
IF N_Elements(storage) NE 0 THEN self.storage = Ptr_New(storage)
IF N_Elements(background) EQ 0 THEN BEGIN
background = d_background
IF N_Elements(command) EQ 0 THEN eraseit = 1
IF (N_Elements(command) NE 0) && cgCoyoteGraphic(command) THEN eraseit = 1
ENDIF ELSE BEGIN
eraseit = 1
ENDELSE
IF N_Elements(eraseIt) EQ 0 THEN eraseIt = d_eraseit ELSE eraseIt = Keyword_Set(eraseIt)
; The commands will be placed in a linked list for execution.
self.cmds = Obj_New('LinkedList')
IF Obj_Valid(self.cmds) EQ 0 THEN Message, 'Failed to make the LinkedList for the commands.'
; Add a command, if you have one. Otherwise, just make the window.
IF (N_Elements(command) NE 0) THEN BEGIN
thisCommand = Obj_New('cgWindow_Command', COMMAND=command, $
P1=p1, P2=p2, P3=p3, P4=p4, KEYWORDS=extra, TYPE=method, $
ALTPS_KEYWORDS=altps_Keywords, ALTPS_PARAMS=altps_Params, $
DESTROYOBJECTS=Keyword_Set(wdestroyobjects))
IF Obj_Valid(thisCommand) THEN self.cmds -> Add, thisCommand ELSE Message, 'Failed to make command object.'
ENDIF
; If there is a palette, use it. Otherwise, use the current color table vectors.
IF Total(d_palette) NE 0 THEN BEGIN
IF Size(d_palette, /N_DIMENSIONS) NE 2 THEN Message, 'Color palette is not a 3xN array.'
dims = Size(d_palette, /DIMENSIONS)
threeIndex = Where(dims EQ 3)
IF ((threeIndex)[0] LT 0) THEN Message, 'Color palette is not a 3xN array.'
IF threeIndex[0] EQ 0 THEN d_palette = Transpose(d_palette)
self.r = Ptr_New(d_palette[*,0])
self.g = Ptr_New(d_palette[*,1])
self.b = Ptr_New(d_palette[*,2])
ENDIF ELSE BEGIN
TVLCT, rr, gg, bb, /Get
self.r = Ptr_New(rr)
self.g = Ptr_New(gg)
self.b = Ptr_New(bb)
ENDELSE
; Create the widgets for the program.
IF N_Elements(parent) NE 0 THEN BEGIN
self.tlb = parent
createParent = 0
ENDIF ELSE BEGIN
self.tlb = Widget_Base(/TLB_SIZE_EVENTS, MBar=menuID, UNAME='TLB_RESIZE')
createParent = 1
ENDELSE
; Create buttons if you created the parent widget.
IF createParent THEN BEGIN
fileID = Widget_Button(menuID, Value='File')
saveID = Widget_Button(fileID, Value='Save Window As...', /MENU)
button = Widget_Button(saveID, Value='PostScript File', UNAME='POSTSCRIPT')
button = Widget_Button(saveID, Value='PDF File', UNAME='PDF')
; If you can find ImageMagick on this machine, you can convert to better
; looking raster files.
IF cgHasImageMagick() EQ 1 THEN BEGIN
imraster = Widget_Button(saveID, Value='Raster Image File', /MENU)
button = Widget_Button(imraster, Value='BMP', UNAME='IMAGEMAGICK_BMP')
button = Widget_Button(imraster, Value='GIF', UNAME='IMAGEMAGICK_GIF')
button = Widget_Button(imraster, Value='JPEG', UNAME='IMAGEMAGICK_JPEG')
button = Widget_Button(imraster, Value='PNG', UNAME='IMAGEMAGICK_PNG')
button = Widget_Button(imraster, Value='TIFF', UNAME='IMAGEMAGICK_TIFF')
ENDIF ELSE BEGIN
raster = Widget_Button(saveID, Value='Raster Image File', /MENU)
button = Widget_Button(raster, Value='BMP', UNAME='RASTER_BMP')
button = Widget_Button(raster, Value='GIF', UNAME='RASTER_GIF')
button = Widget_Button(raster, Value='JPEG', UNAME='RASTER_JPEG')
button = Widget_Button(raster, Value='PNG', UNAME='RASTER_PNG')
button = Widget_Button(raster, Value='TIFF', UNAME='RASTER_TIFF')
ENDELSE
button = Widget_Button(fileID, Value='Save Current Visualization', /Separator, UNAME='SAVECOMMANDS')
button = Widget_Button(fileID, Value='Restore Visualization', UNAME='RESTORECOMMANDS')
button = Widget_Button(fileID, Value='Quit', /Separator, UNAME='QUIT')
ENDIF
; Check to see if you have to create a window with a particular aspect ratio.
; If so, your xsize and ysize values will need to be adjusted.
IF waspect NE 0 THEN BEGIN
IF waspect GE 1 THEN BEGIN
xsize = Round(ysize / waspect)
ENDIF ELSE BEGIN
ysize = Round(xsize * waspect)
ENDELSE
ENDIF
; Create draw widget. UNIX versions of IDL have a bug in which creating
; a draw widget as the very first window in an IDL session causes both
; !P.Background and !P.Color to be set to white. I know, it's odd. But
; doing this little trick fixes the problem.
tempBackground = !P.Background
tempColor = !P.Color
retain = (StrUpCase(!Version.OS_Family) EQ 'UNIX') ? 2 : 1
IF ~createParent THEN BEGIN
notify_realize = 'cgCmdWindowDrawRealize'
kill_notify = 'cgCmdWindowKillNotify'
ENDIF
self.drawID = Widget_Draw(self.tlb, XSIZE=xsize, YSize=ysize, RETAIN=retain, $
UVALUE=self, Notify_Realize=notify_realize, $
UNAME='DRAW_WIDGET', Kill_Notify=kill_notify, $
BUTTON_EVENTS=button_events, $
KEYBOARD_EVENTS=keyboard_events, $
MOTION_EVENTS=motion_events, $
TRACKING_EVENTS=tracking_events)
release = Float(!Version.Release)
IF (N_Elements(wheel_events) NE 0) && (release GE 6.2) THEN $
Widget_Control, self.drawID, DRAW_WHEEL_EVENTS=wheel_events
IF (N_Elements(drop_events) NE 0) && (release GE 6.3) THEN $
Widget_Control, self.drawID, SET_DROP_EVENTS=drop_events
!P.Background = Temporary(tempBackground)
!P.Color = Temporary(tempColor)
; Do we need to center the widget? Only do this if you created the parent.
IF (xpos EQ -1) && (ypos EQ -1) && createParent THEN BEGIN
DefSysV, '!FSC_WINDOW_LIST', EXISTS=exists
IF ~exists THEN BEGIN
xpos = 5
ypos = 5
ENDIF ELSE BEGIN
IF Obj_Valid(!FSC_WINDOW_LIST) THEN BEGIN
count = !FSC_WINDOW_LIST -> Get_Count()
xpos = 5 + (30*count)
ypos = 5 + (25*count)
ENDIF ELSE BEGIN
xpos = 5
ypos = 5
ENDELSE
ENDELSE
cgCenterTLB, self.tlb, xpos, ypos, /NOCENTER, /DEVICE
ENDIF ELSE BEGIN
IF createParent THEN cgCenterTLB, self.tlb, xpos, ypos, /NOCENTER, /DEVICE
ENDELSE
; Display the widget and get window index number.
currentWindow = !D.Window
IF createParent THEN BEGIN
Widget_Control, self.tlb, /Realize
Widget_Control, self.drawID, Get_Value=wid
self.wid = wid
WSet, wid
IF N_Elements(title) EQ 0 THEN BEGIN
title = d_title
title = title + ' (' + StrTrim(wid,2) + ')'
ENDIF
Widget_Control, self.tlb, TLB_Set_Title=title
ENDIF ELSE BEGIN
IF N_Elements(title) EQ 0 THEN title = "cgCmdWindow"
ENDELSE
self.title = title
; Load object properties.
self.background = Ptr_New(background)
IF N_Elements(cmdDelay) NE 0 THEN self.delay = cmdDelay ELSE self.delay = d_delay
self.eraseIt = eraseIt
IF N_Elements(wmulti) NE 0 THEN BEGIN
FOR j=0,N_Elements(wmulti)-1 DO self.pmulti[j] = wmulti[j]
ENDIF ELSE self.pmulti = d_multi
IF N_Elements(xomargin) NE 0 THEN self.xomargin = xomargin ELSE self.xomargin = d_xomargin
IF N_Elements(yomargin) NE 0 THEN self.yomargin = yomargin ELSE self.yomargin = d_yomargin
self.adjustsize = d_adjustsize
self.createParent = createParent
self.destroyObjects = Keyword_Set(wdestroyobjects)
self.im_transparent = d_im_transparent
self.im_density = d_im_density
self.im_options = d_im_options
self.im_raster = d_im_raster
self.im_resize = d_im_resize
self.im_width = Ptr_New(d_im_width)
self.msysvar = Ptr_New(/Allocate_Heap)
self.pdf_unix_convert_cmd = d_pdf_unix_convert_cmd
self.pdf_path = d_pdf_path
self.ps_decomposed = d_ps_decomposed
self.ps_delete = d_ps_delete
self.ps_encapsulated = d_ps_encapsulated
self.ps_metric = d_ps_metric
self.ps_charsize = d_ps_charsize
self.ps_font = d_ps_font
self.ps_quiet = d_ps_quiet
self.ps_scale_factor = d_ps_scale_factor
self.ps_tt_font = d_ps_tt_font
self.waspect = waspect
IF createParent THEN BEGIN
; Get it running.
WIDGET_CONTROL, /MANAGED, self.tlb
XManager, 'cgwindow', self.tlb, /No_Block, $
Event_Handler='cgCmdWindow_Dispatch_Events', $
Cleanup = 'cgCmdWindow_Cleanup', $
Group_Leader=group_leader
; Store the self object in UVALUE of TLB. This is so it can be cleaned up properly.
Widget_Control, self.tlb, SET_UValue=self
; Store the object reference on the external linked list.
self -> StoreObjectReference
; Execute the commands
self -> ExecuteCommands
ENDIF
; Restore the current graphics window, if you can.
IF (currentWindow GE 0) && WindowAvailable(currentWindow) THEN BEGIN
WSet, currentWindow
ENDIF ELSE WSet, -1
RETURN, 1
END ;----------------------------------------------------------------------------------------------------------------
;+
; This program is called when the draw widget is realized. The purpose is to find
; and set the window index number in the program, store the object reference on
; an external linked list so it can found again later, and to execute the loaded
; commands.
;
; :Params:
; drawID: in, required
; The widget identifier of the draw widget.
;-
PRO cgCmdWindowDrawRealize, drawID
Widget_Control, drawID, Get_UVALUE=theObject, Get_VALUE=wid
theObject -> SetProperty, WID=wid
; Store the object reference on the external linked list.
theObject -> StoreObjectReference
; Execute the commands
theObject -> ExecuteCommands
END
;+
; This program is called when the draw widget is destroyed. The purpose
; is to clean up the object.
;
; :Params:
; drawID: in, required
; The widget identifier of the draw widget.
;-
PRO cgCmdWindowKillNotify, drawID
Widget_Control, drawID, GET_UVALUE=theObject
Obj_Destroy, theObject
END
;
;
;+
; The cleanup method for the cgCmdWindow object.
;-
PRO cgCmdWindow::Cleanup
Compile_Opt idl2
; Error handling.
Catch, theError
IF theError NE 0 THEN BEGIN
Catch, /CANCEL
void = cgErrorMsg()
RETURN
ENDIF
; Free any pointers.
Ptr_Free, self.background
Ptr_Free, self.r
Ptr_Free, self.g
Ptr_Free, self.b
Ptr_Free, self.msysvar
Ptr_Free, self.storage
Ptr_Free, self.im_width
; Destroy the command objects.
IF Obj_Valid(self.cmds) THEN BEGIN
count = self.cmds -> Get_Count()
FOR j=0,count-1 DO Obj_Destroy, self.cmds -> Get_Item(j, /DEREFERENCE)
Obj_Destroy, self.cmds
ENDIF
; You have to remove yourself from the list of valid cgWindows.
theList = !FSC_WINDOW_LIST
IF Obj_Valid(theList) THEN BEGIN
structs = theList -> Get_Item(/ALL, /DEREFERENCE)
index = Where(structs.windowObj[*] EQ self, count)
IF count GT 0 THEN theList -> Delete, index[0]
ENDIF
; If the list doesn't have any more cgWindows objects in it,
; delete the list so it doesn't waste memory.
IF ((theList -> Get_Count()) EQ 0) THEN Obj_Destroy, theList
; If your widget ID is valid, destroy the widget program.
IF self.createParent && Widget_Info(self.tlb, /VALID_ID) THEN Widget_Control, self.tlb, /Destroy
END ;----------------------------------------------------------------------------------------------------------------
;+
; This procedure builds a command structure and saves the structure
; to the main IDL level. Its purpose is to allow the user to work
; with restored commands independently of the window interface.
;
; :Params:
; structname: in, optional, type=string, default="cmd"
; The name of the command structure to be saved at the
; main IDL level.
;
; :Keywords:
; quiet: in, optional, type=boolean
; If set, the message indicated where the variable was saved
; and what its name is is not printed in the IDL command window.
;-
PRO cgWindow_Command::CreateCommandStruct, structName, Quiet=quiet
Compile_Opt idl2
; Error handling.
Catch, theError
IF theError NE 0 THEN BEGIN
Catch, /CANCEL
void = cgErrorMsg()
RETURN
ENDIF
; Struct variable name
IF N_Elements(structName) EQ 0 THEN structName='cmd'
cmdString = self.command
cmdStruct = Create_Struct('Command', cmdString, 'nparams', self.nparams, 'type', self.type)
CASE self.nparams OF
0:
1: cmdStruct = Create_Struct(cmdStruct, 'p1', *self.p1)
2: cmdStruct = Create_Struct(cmdStruct, 'p1', *self.p1, 'p2', *self.p2)
3: cmdStruct = Create_Struct(cmdStruct, 'p1', *self.p1, 'p2', *self.p2, 'p3', *self.p3)
4: cmdStruct = Create_Struct(cmdStruct, 'p1', *self.p1, 'p2', *self.p2, 'p3', *self.p3, 'p4', *self.p4)
ENDCASE
IF Ptr_Valid(self.keywords) THEN BEGIN
cmdStruct = Create_Struct(cmdStruct, 'keywords', *self.keywords)
ENDIF
; Copy the variable to the MAIN level
(Scope_VarFetch(structName, /Enter, Level=1)) = Temporary(cmdStruct)
IF NOT Keyword_Set(quiet) THEN $
PRINT, 'Created command struct variable ', structName, ' in IDL $MAIN level.'
END ;----------------------------------------------------------------------------------------------------------------
;+
; This procedure makes and returns a copy of a command object.
;-
FUNCTION cgWindow_Command::Copy
IF Ptr_Valid(self.keywords) THEN BEGIN
CASE self.nparams OF
0: copyObj = Obj_New('cgWindow_Command', COMMAND=self.command, $
KEYWORDS=*self.keywords, TYPE=self.type, $
DESTROYOBJECTS=self.destroyObjects)
1: copyObj = Obj_New('cgWindow_Command', COMMAND=self.command, P1=*self.p1, $
KEYWORDS=*self.keywords, TYPE=self.type, $
DESTROYOBJECTS=self.destroyObjects)
2: copyObj = Obj_New('cgWindow_Command', COMMAND=self.command, P1=*self.p1, P2=*self.p2, $
KEYWORDS=*self.keywords, TYPE=self.type, $
DESTROYOBJECTS=self.destroyObjects)
3: copyObj = Obj_New('cgWindow_Command', COMMAND=self.command, P1=*self.p1, P2=*self.p2, $
P3=*self.p3, KEYWORDS=*self.keywords, TYPE=self.type, $
DESTROYOBJECTS=self.destroyObjects)
4: copyObj = Obj_New('cgWindow_Command', COMMAND=self.command, P1=*self.p1, P2=*self.p2, $
P3=*self.p3, P4=*self.p4, KEYWORDS=*self.keywords, TYPE=self.type, $
DESTROYOBJECTS=self.destroyObjects)
ENDCASE
ENDIF ELSE BEGIN
CASE self.nparams OF
0: copyObj = Obj_New('cgWindow_Command', COMMAND=self.command, $
TYPE=self.type, DESTROYOBJECTS=self.destroyObjects)
1: copyObj = Obj_New('cgWindow_Command', COMMAND=self.command, P1=*self.p1, $
TYPE=self.type, DESTROYOBJECTS=self.destroyObjects)
2: copyObj = Obj_New('cgWindow_Command', COMMAND=self.command, P1=*self.p1, P2=*self.p2, $
TYPE=self.type, DESTROYOBJECTS=self.destroyObjects)
3: copyObj = Obj_New('cgWindow_Command', COMMAND=self.command, P1=*self.p1, p2=*self.p2, P3=*self.p3, $
TYPE=self.type, DESTROYOBJECTS=self.destroyObjects)
4: copyObj = Obj_New('cgWindow_Command', COMMAND=self.command, P1=*self.p1, p2=*self.p2, P3=*self.p3, P4=*self.p4, $
TYPE=self.type, DESTROYOBJECTS=self.destroyObjects)
ENDCASE
ENDELSE
RETURN, copyObj
END ;----------------------------------------------------------------------------------------------------------------
;+
; This procedure returns the keyword in a command object, if there
; are any keywords.
;
; :Keywords:
; has_keywords: out, optional, type=boolean
; If set to 1, the function returned keywords. If set to 0,
; there were no keywords in the command to return.
;-
FUNCTION cgWindow_Command::Get_Keywords, HAS_KEYWORDS=has_keywords
IF Ptr_Valid(self.keywords) THEN BEGIN
has_keywords = 1
RETURN, *self.keywords
ENDIF ELSE BEGIN
has_keywords = 0
RETURN, Ptr_New()
ENDELSE
END ;----------------------------------------------------------------------------------------------------------------
PRO cgWindow_Command::Draw, SUCCESS=success, KEYWORDS=keywords
Compile_Opt idl2
; Can't really catch CALL_PROCEDURE errors, so I'm not sure what this is doing here.
Catch, theError
IF theError NE 0 THEN BEGIN
Catch, /CANCEL
void = cgErrorMsg()
success = 0
RETURN
ENDIF
; It seems it is not possible to catch errors generated by CALL_PROCEDURE,
; so I have to fake it out. I reset the !ERROR_STATE structure at the beginning
; and assume failure. After I get through the code, I check to see if the
; !ERROR_STATE.MSG field is still a null string. If it is, I assume we successfully
; executed the command and change the success flag.
success = 0
Message, /RESET
; Do we have alternative keywords?
IF Ptr_Valid(self.altps_Keywords) THEN BEGIN
keywords = self -> EvaluateKeywords(*self.keywords, SUCCESS=success)
IF success EQ 0 THEN Ptr_Free, self.keywords
ENDIF ELSE BEGIN
IF Ptr_Valid(self.keywords) THEN BEGIN
keywords = *self.keywords
keywords = self -> ReplaceEscapeSequences(keywords)
ENDIF
ENDELSE
; Do we have alternative parameters?
IF Ptr_Valid(self.altps_params) THEN BEGIN
altTags = Tag_Names(*self.altps_params)
IF (Where(altTags EQ 'P1') NE -1) THEN BEGIN
IF (!D.Name EQ 'PS') $
THEN p1 = (*self.altps_params).p1 $
ELSE IF Ptr_Valid(self.p1) THEN p1 = *self.p1
ENDIF ELSE IF Ptr_Valid(self.p1) THEN p1 = *self.p1
IF (Where(altTags EQ 'P2') NE -1) THEN BEGIN
IF (!D.Name EQ 'PS') $
THEN p2 = (*self.altps_params).p2 $
ELSE IF Ptr_Valid(self.p2) THEN p2 = *self.p2
ENDIF ELSE IF Ptr_Valid(self.p2) THEN p2 = *self.p2
IF (Where(altTags EQ 'P3') NE -1) THEN BEGIN
IF (!D.Name EQ 'PS') $
THEN p3 = (*self.altps_params).p3 $
ELSE IF Ptr_Valid(self.p3) THEN p3 = *self.p3
ENDIF ELSE IF Ptr_Valid(self.p3) THEN p3 = *self.p3
IF (Where(altTags EQ 'P4') NE -1) THEN BEGIN
IF (!D.Name EQ 'PS') $
THEN p4 = (*self.altps_params).p4 $
ELSE IF Ptr_Valid(self.p4) THEN p4 = *self.p4
ENDIF ELSE IF Ptr_Valid(self.p4) THEN p4 = *self.p4
ENDIF ELSE BEGIN
IF Ptr_Valid(self.p1) THEN p1 = *self.p1
IF Ptr_Valid(self.p2) THEN p2 = *self.p2
IF Ptr_Valid(self.p3) THEN p3 = *self.p3
IF Ptr_Valid(self.p4) THEN p4 = *self.p4
ENDELSE
; Remove escape sequences, if you need to.
IF Size(p1, /TNAME) EQ 'STRING' THEN p1 = self -> ReplaceEscapeSequences(p1)
IF Size(p2, /TNAME) EQ 'STRING' THEN p2 = self -> ReplaceEscapeSequences(p2)
IF Size(p3, /TNAME) EQ 'STRING' THEN p3 = self -> ReplaceEscapeSequences(p3)
IF Size(p4, /TNAME) EQ 'STRING' THEN p4 = self -> ReplaceEscapeSequences(p4)
; What kind of command is this?
CASE self.type OF
; Command calls a procedure.
0: BEGIN
IF Ptr_Valid(self.keywords) THEN BEGIN
CASE self.nparams OF
0: Call_Procedure, self.command, _Extra=keywords
1: Call_Procedure, self.command, p1, _Extra=keywords
2: Call_Procedure, self.command, p1, p2, _Extra=keywords
3: Call_Procedure, self.command, p1, p2, p3, _Extra=keywords
4: Call_Procedure, self.command, p1, p2, p3, p4, _Extra=keywords
ENDCASE
ENDIF ELSE BEGIN
CASE self.nparams OF
0: Call_Procedure, self.command
1: Call_Procedure, self.command, p1
2: Call_Procedure, self.command, p1, p2
3: Call_Procedure, self.command, p1, p2, p3
4: Call_Procedure, self.command, p1, p2, p3, p4
ENDCASE
ENDELSE
END
; Command calls a method.
1: BEGIN
IF Ptr_Valid(self.keywords) THEN BEGIN
CASE self.nparams OF
0: Message, 'Call_Method requires at least one positional parameter.'
1: Call_Method, self.command, p1, _Extra=keywords
2: Call_Method, self.command, p1, p2, _Extra=keywords
3: Call_Method, self.command, p1, p2, p3, _Extra=keywords
4: Call_Method, self.command, p1, p2, p3, p4, _Extra=keywords
ENDCASE
ENDIF ELSE BEGIN
CASE self.nparams OF
0: Message, 'Call_Method requires at least one positional parameter.'
1: Call_Method, self.command, p1
2: Call_Method, self.command, p1, p2
3: Call_Method, self.command, p1, p2, p3
4: Call_Method, self.command, p1, p2, p3, p4
ENDCASE
ENDELSE
END
ENDCASE
; If nothing has been put into the message field, we much have executed the command
; successfully.
IF !Error_State.MSG EQ "" THEN success = 1
; For some reason, CALL_PROCEDURE does not flush the graphics buffer on UNIX machines.
; We have to do it ourself to get the program to resize correctly on UNIX machines.
EMPTY
END ;----------------------------------------------------------------------------------------------------------------
;+
; This method replaces stored keyword values with alternative keyword values, if they
; are available. The return variable is a list of the keywords with the
; alternative values substituted for the stored values.
;
; :Params:
; keywords: in, required, type=structure
; The list of input keywords.
;
; :Keywords:
; success: out, optional, type=boolean
; If the command executed successfully, return a 1. Otherwise
; return a 0.
;-
FUNCTION cgWindow_Command::EvaluateKeywords, keywords, SUCCESS=success
; Error handling.
Catch, theError
IF theError NE 0 THEN BEGIN
Catch, /CANCEL
success = 0
void = cgErrorMsg()
IF N_Elements(keywords) NE 0 THEN RETURN, keywords ELSE RETURN, 0
ENDIF
Compile_Opt idl2
; Assume success.
success = 1
; Need to have keywords.
IF N_Elements(keywords) EQ 0 THEN $
Message, 'A keyword structure must be passed to the method.'
; Get the keyword names.
tags = Tag_Names(keywords)
; Find the keywords to be evaluated in this keyword structure.
evalTags = Tag_Names(*self.altps_keywords)
theseTags = Ptr_New(/Allocate_Heap)
FOR j=0,N_Elements(evalTags)-1 DO BEGIN
index = Where(tags EQ evalTags[j], count)
IF (count EQ 1) THEN BEGIN
IF N_Elements(*theseTags) NE 0 THEN *theseTags = [*theseTags, index] ELSE *theseTags = [index]
ENDIF ELSE IF count GT 1 THEN Message, 'Ambiguous keywords to evaluate.'
ENDFOR
; Evaluate the keywords and, if necessary, replace the keyword value
; with the evaluated value. The evaluated keyword value must initially be a
; STRING that can be evaluated with the EXECUTE command.
FOR j=0,N_Elements(tags)-1 DO BEGIN
index = Where(*theseTags EQ j, count)
IF count EQ 0 THEN BEGIN
IF N_Elements(mkeys) EQ 0 THEN BEGIN
mkeys = Create_Struct(tags[j], keywords.(j))
ENDIF ELSE BEGIN
mkeys = Create_Struct(mkeys, tags[j], keywords.(j))
ENDELSE
ENDIF ELSE BEGIN
; If this is PostScript, use the alternative keyword value.
tagValue = (!D.Name EQ 'PS') ? (*self.altps_keywords).(index) : keywords.(j)
IF N_Elements(mkeys) EQ 0 THEN BEGIN
mkeys = Create_Struct(tags[j], tagValue)
ENDIF ELSE BEGIN
mkeys = Create_Struct(mkeys, tags[j], tagValue)
ENDELSE
ENDELSE
ENDFOR
; Evaluate string keywords for escape sequences (e.g, $\mu$).
;mkeys = cgWindow_Command -> ReplaceEscapeSequences(mkeys)
Ptr_Free, theseTags
RETURN, mkeys
END ;----------------------------------------------------------------------------------------------------------------
;+
; This method lists the current command by printing a representation
; of the command in the command log window.