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
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
|
."remove .ig hn for full docs
.de hi
.ig eh
..
.de eh
..
.TH "" 3 "" "Version 3.0" "Free Widget Foundation"
.SH NAME
XfwfCommon
.SH DESCRIPTION
The Common class is not meant to be instantiated. It only serves as
the common superclass for a family of widgets, to ensure that these
widgets have some common methods and resources. The Common class
defines common types, symbolic constants, and type converters and it
also provides the basis for keyboard traversal. The code for keyboard
traversal is roughly based on that in the Xw widget set (created by
Hewlett Packard), but it uses the \fIaccept_focus\fP method.
When the resource \fItraversalOn\fP is set to \fITrue\fP (either at creation
time, or via a \fIXtSetValues\fP later), a set of translations is added to
the widget. If the widget's parent is also a subclass of Common, these
translations will then implement keyboard traversal, using the cursor
keys (up, down, prev, etc.) Of course, when the widget already uses
these keys for other purposes, the keyboard traversal will not work.
.SS "Public variables"
.ps-2
.TS
center box;
cBsss
lB|lB|lB|lB
l|l|l|l.
XfwfCommon
Name Class Type Default
XtNtraversalOn XtCTraversalOn Boolean True
XtNhighlightThickness XtCHighlightThickness Dimension 2
XtNhighlightColor XtCHighlightColor Pixel XtDefaultForeground
XtNhighlightPixmap XtCHighlightPixmap Pixmap None
XtNnextTop XtCNextTop Callback NULL
XtNuserData XtCUserData Pointer NULL
.TE
.ps
.TP
.I "XtNtraversalOn"
The resource \fItraversalOn\fP determines whether keyboard traversal is
used. If it is \fITrue\fP initially, or if it is set to \fITrue\fP later, a
set of translations will be added to the widget.
.hi
.nf
Boolean traversalOn = True
.fi
.eh
.TP
.I "XtNhighlightThickness"
Keyboard focus is indicated by border highlighting. When keyboard
traversal is on and the widget receives the focus, the highlight border
is filled with the highlight color or tile. If the widget does not
have the focus, the area is left in the default background.
.hi
.nf
Dimension highlightThickness = 2
.fi
.eh
.TP
.I "XtNhighlightColor"
The highlight border can have a color or it can be tiled with a
pixmap. Whichever of the resources \fIhighlightColor\fP or
\fIhighlightPixmap\fP is set latest, is used. When both are set, the
pixmap is used.
.hi
.nf
Pixel highlightColor = <String>XtDefaultForeground
.fi
.eh
.TP
.I "XtNhighlightPixmap"
The \fIhighlightPixmap\fP can be set to a pixmap with which the
highlight border will be tiled. Only one of \fIhighlightPixmap\fP and
\fIhighlightColor\fP can be set, see above.
.hi
.nf
Pixmap highlightPixmap = None
.fi
.eh
.TP
.I "XtNnextTop"
When an application has several top level windows, it should have a
way of setting the focus between windows. The Enter key in any widget
with keyboard traversal on normally invokes the \fItraverseNextTop\fP
action, that will call the callbacks of the topmost Common (or
subclass) widget in the hierarchy. The callback may set the focus to
another top level widget, with \fIXtCallAcceptFocus\fP.
.hi
.nf
<Callback> XtCallbackList nextTop = NULL
.fi
.eh
.TP
.I "XtNuserData"
The resource \fIuserData\fP is provided for applications that want to
attach their own data to a widget. It is not used by the widget itself
in any way.
.hi
.nf
<Pointer> XtPointer userData = NULL
.fi
.eh
.ps-2
.TS
center box;
cBsss
lB|lB|lB|lB
l|l|l|l.
Composite
Name Class Type Default
XtNchildren XtCChildren WidgetList NULL
insertPosition XtCInsertPosition XTOrderProc NULL
numChildren XtCNumChildren Cardinal 0
.TE
.ps
.ps-2
.TS
center box;
cBsss
lB|lB|lB|lB
l|l|l|l.
Core
Name Class Type Default
XtNx XtCX Position 0
XtNy XtCY Position 0
XtNwidth XtCWidth Dimension 0
XtNheight XtCHeight Dimension 0
borderWidth XtCBorderWidth Dimension 0
XtNcolormap XtCColormap Colormap NULL
XtNdepth XtCDepth Int 0
destroyCallback XtCDestroyCallback XTCallbackList NULL
XtNsensitive XtCSensitive Boolean True
XtNtm XtCTm XTTMRec NULL
ancestorSensitive XtCAncestorSensitive Boolean False
accelerators XtCAccelerators XTTranslations NULL
borderColor XtCBorderColor Pixel 0
borderPixmap XtCBorderPixmap Pixmap NULL
background XtCBackground Pixel 0
backgroundPixmap XtCBackgroundPixmap Pixmap NULL
mappedWhenManaged XtCMappedWhenManaged Boolean True
XtNscreen XtCScreen Screen * NULL
.TE
.ps
.SS "Exports"
The type \fIAlignment\fP is actually an integer, but it is given a
different name to allow a type converter to be installed for it.
.nf
.B type
Alignment = int
.fi
The symbolic constants can be added together to form an alignment.
Various widgets use this to position labels, other widgets, etcetera.
\fBdef\fP XfwfCenter = 0
\fBdef\fP XfwfLeft = 1
\fBdef\fP XfwfRight = 2
\fBdef\fP XfwfTop = 4
\fBdef\fP XfwfBottom = 8
For convenience, the eight possible combinations also have symbolic
names.
\fBdef\fP XfwfTopLeft = (XfwfTop +XfwfLeft )
\fBdef\fP XfwfTopRight = (XfwfTop +XfwfRight )
\fBdef\fP XfwfBottomLeft = (XfwfBottom +XfwfLeft )
\fBdef\fP XfwfBottomRight = (XfwfBottom +XfwfRight )
The directions of traversal are used as arguments to the \fItraverse\fP
method. They are probably only useful to subclasses.
.nf
.B type
TraversalDirection = enum {
TraverseLeft, TraverseRight, TraverseUp, TraverseDown,
TraverseNext, TraversePrev, TraverseHome, TraverseNextTop }
.fi
To know the inside area of a Common widget might be useful to other
widgets than subclasses alone. Calling \fIXfwfCallComputeInside\fP will
call the \fIcompute_inside\fP method, if available.
.nf
XfwfCallComputeInside( $, Position * x, Position * y, Dimension * w, Dimension * h)
.fi
.hi
{
if (XtIsSubclass($, xfwfCommonWidgetClass) $compute_inside)
$compute_inside($, x, y, w, h);
}
.eh
.hi
.SS "Actions"
When the widget receives or looses the focus, the border highlight
is drawn or removed. This action function draws the highlight border
and in case the widget has set \fItraversalOn\fP, it also sets the
keyboard focus to the widget itself, or one of its children.
However, FocusIn events may also be so-called virtual events, meaning
that not the receiving widget, but one of its descendants gets the
real focus. When \fIfocusIn\fP receives one of those, it removes the
highlight border.
.hi
\fBdef\fP focus_detail(detail) =
(detail ==NotifyAncestor ?"NotifyAncestor":detail ==NotifyVirtual ?"NotifyVirtual":detail ==NotifyInferior ?"NotifyInferior":detail ==NotifyNonlinear ?"NotifyNonlinear":detail ==NotifyNonlinearVirtual ?"NotifyNonlinearVirtual":detail ==NotifyPointer ?"NotifyPointer":detail ==NotifyPointerRoot ?"NotifyPointerRoot":detail ==NotifyDetailNone ?"NotifyDetailNone":"???")
.eh
.TP
.I "focusIn
.hi
.nf
void focusIn($, XEvent* event, String* params, Cardinal* num_params)
{
Time time = CurrentTime;
if (event->type != FocusIn)
XtError("focusIn action may only be bound to FocusIn events");
if (! $traversalOn)
return;
if (event->xfocus.detail == NotifyAncestor
|| event->xfocus.detail == NotifyInferior
|| event->xfocus.detail == NotifyNonlinear) {
if (! $traversal_focus) (void) $accept_focus($, time);
} else if ($traversal_focus) {
$unhighlight_border($);
$traversal_focus = False;
}
}
.fi
.eh
.TP
.I "focusOut
This action removes the highlight border.
.hi
.nf
void focusOut($, XEvent* event, String* params, Cardinal* num_params)
{
if (event->type != FocusOut)
XtError("focusOut action may only be bound to FocusOut events");
if ($traversal_focus) {
$unhighlight_border($);
$traversal_focus = False;
}
}
.fi
.eh
.TP
.I "traverseDown
This and the following actions all call the \fItraverse\fP method of the
widget's parent, with the appropiate direction arguments.
\fItraverseDown\fP tries to set the focus to a widget that is located
roughly below the current one.
.hi
.nf
void traverseDown($, XEvent* event, String* params, Cardinal* num_params)
{
$traverse($, TraverseDown, $, event->xkey.time);
}
.fi
.eh
.TP
.I "traverseUp
The action tries to set the focus to a widget that is above the this
one.
.hi
.nf
void traverseUp($, XEvent* event, String* params, Cardinal* num_params)
{
$traverse($, TraverseUp, $, event->xkey.time);
}
.fi
.eh
.TP
.I "traverseLeft
\fItraverseLeft\fP looks for a widget to the left of the current one and
sets the keyboard focus to that.
.hi
.nf
void traverseLeft($, XEvent* event, String* params, Cardinal* num_params)
{
$traverse($, TraverseLeft, $, event->xkey.time);
}
.fi
.eh
.TP
.I "traverseRight
The action looks for a widget that will aceept the focus to the
right of the current one.
.hi
.nf
void traverseRight($, XEvent* event, String* params, Cardinal* num_params)
{
$traverse($, TraverseRight, $, event->xkey.time);
}
.fi
.eh
.TP
.I "traverseNext
The next sibling gets the focus. The precise order is determined by
the parent, but usually is will be the order in which the widgets were
created. If there is no suitable sibling, the request is passed to the
grandparent, so that an `aunt widget' or other relation can get the
focus.
.hi
.nf
void traverseNext($, XEvent* event, String* params, Cardinal* num_params)
{
$traverse($, TraverseNext, $, event->xkey.time);
}
.fi
.eh
.TP
.I "traversePrev
The previous widget gets the focus. See also the description of
\fItraverseNext\fP above.
.hi
.nf
void traversePrev($, XEvent* event, String* params, Cardinal* num_params)
{
$traverse($, TraversePrev, $, event->xkey.time);
}
.fi
.eh
.TP
.I "traverseNextTop
\fItraverseNextTop\fP finds the topmost ancestor that is a subclass of
Common and lets it call the \fInextTop\fP callbacks that have been
registered there. These callbacks can be used by an application that
has multiple top level windows to set the focus to another window.
.hi
.nf
void traverseNextTop($, XEvent* event, String* params, Cardinal* num_params)
{
$traverse($, TraverseNextTop, $, event->xkey.time);
}
.fi
.eh
.TP
.I "traverseHome
The action sets the focus to the sibling widget that is closest to
the upper left corner of the parent.
.hi
.nf
void traverseHome($, XEvent* event, String* params, Cardinal* num_params)
{
$traverse($, TraverseHome, $, event->xkey.time);
}
.fi
.eh
.TP
.I "traverseCurrent
The \fItraverseCurrent\fP action can be used by widgets to set the focus
to themselves. It is not used in the set of translations that is added
when \fItraversalOn\fP is set to \fITrue\fP.
.hi
.nf
void traverseCurrent($, XEvent* event, String* params, Cardinal* num_params)
{
Time time = CurrentTime;
if ($traversalOn) (void) $accept_focus($, time);
}
.fi
.eh
.hi
.hi
.SH "Importss"
Most of the included files have to do with the \fIIcon\fP type. Six
icons are preloaded.
.nf
.B incl
<stdio.h>
.fi
.nf
.B incl
<X11/Xmu/Converters.h>
.fi
.nf
.B incl
<Xfwf/Converters.h>
.fi
.hi
.hi
.SS "Private variables"
\fIabs\fP, \fImin\fP and \fImax\fP are used often enough in various subclasses
to define them here. They will end up in the private(!) header file.
.nf
max
.fi
.nf
min
.fi
.nf
abs
.fi
A private variable is used to track the keyboard focus, but only
while traversal is on. If \fItraversal_focus\fP is \fITrue\fP, it means that
the widget has keyboard focus and that that focus is a result of
keyboard traversal. It also means that the widget's border is
highlighted, although that is only visible if the \fIhighlightThickness\fP
is positive.
.nf
Boolean traversal_focus
.fi
The highlight border is filled with a color or a tile.
.nf
GC bordergc
.fi
.hi
.hi
.SH "Class variables"
\fItraversal_trans\fP holds the compiled version of the
\fIextraTranslations\fP.
.nf
XtTranslations traversal_trans = NULL
.fi
.hi
.hi
.SS "Methods"
The type converter \fIcvtStringToAlignment\fP is installed in the
\fIclass_initialize\fP method, after the quarks for the recognized strings
are created.
The converter from String to Icon needs one extra argument, viz., the
widget for which the icon is loaded. An offset of 0 should give a
pointer to the widget itself.
.nf
class_initialize()
{
static XtConvertArgRec args[] = {
{ XtWidgetBaseOffset, 0, sizeof(Widget) } };
XtSetTypeConverter(XtRString, "Alignment", cvtStringToAlignment,
NULL, 0, XtCacheNone, NULL);
XtSetTypeConverter("Alignment", XtRString, cvtAlignmentToString,
NULL, 0, XtCacheNone, NULL);
/* init_icon_quarks(); */
XtSetTypeConverter(XtRString, "Icon", cvtStringToIcon,
args, XtNumber(args), XtCacheNone, NULL);
}
.fi
The \fIextraTranslations\fP are compiled into Xt's internal form and
stored in a class variable \fItraversal_trans\fP, but only if that hasn't
been done before. (It should have been done in the \fIclass_initialize\fP
method, but wbuild's `\fI$\fP' syntax doesn't work there (yet)).
If the widget has the \fItraversalOn\fP resource set, the translations are
merged with the widgets existing translations.
.nf
initialize(Widget request, $, ArgList args, Cardinal * num_args)
{
if ($traversal_trans == NULL)
$traversal_trans = XtParseTranslationTable(extraTranslations);
if ($traversalOn) {
XtAugmentTranslations($, $traversal_trans);
$visible_interest = True;
}
$traversal_focus = False;
$bordergc = NULL;
create_bordergc($);
}
.fi
The \fIset_values\fP method checks if the keyboard traversal has been
turned on and adds the traversal translations. (It can only be turned
on, not turned off.)
If something changes that causes the widget to loose keyboard focus,
the parent is asked to put the focus somewhere else. Otherwise the
whole application might suddenly loose keyboard focus.
.nf
Boolean set_values(Widget old, Widget request, $, ArgList args, Cardinal * num_args)
{
Boolean need_redraw = False;
Widget parent = XtParent($);
Time time = CurrentTime;
if ($traversalOn != $old$traversalOn $traversalOn) {
XtAugmentTranslations($, $traversal_trans);
$visible_interest = True;
}
if (($sensitive != $old$sensitive
|| $ancestor_sensitive != $old$ancestor_sensitive
|| $traversalOn != $old$traversalOn)
$traversal_focus) {
if (XtIsSubclass(parent, xfwfCommonWidgetClass))
$parent$traverse(parent, TraverseHome, $, time);
}
if ($highlightThickness != $old$highlightThickness)
need_redraw = True;
if ($highlightPixmap != $old$highlightPixmap) {
create_bordergc($);
need_redraw = True;
} else if ($highlightColor != $old$highlightColor) {
$highlightPixmap = None;
create_bordergc($);
need_redraw = True;
}
return need_redraw;
}
.fi
A new method \fIcompute_inside\fP is defined, that returns the area
inside the highlight border. Subclasses should use this to compute
their drawable area, in preference to computing it from \fI$width\fP and
\fI$height\fP. Subclasses, such as the Frame widget, redefine the method
if they add more border material.
.nf
compute_inside($, Position * x, Position * y, Dimension * w, Dimension * h)
{
*x = $highlightThickness;
*y = $highlightThickness;
*w = $width - 2 * $highlightThickness;
*h = $height - 2 * $highlightThickness;
}
.fi
The \fIexpose\fP method draws the highlight border, if there is one.
.nf
expose($, XEvent * event, Region region)
{
if (! XtIsRealized($)) return;
if (region != NULL) XSetRegion(XtDisplay($), $bordergc, region);
if ($traversal_focus) $highlight_border($);
if (region != NULL) XSetClipMask(XtDisplay($), $bordergc, None);
}
.fi
When the widget is destroyed and the widget still has the keyboard
focus, the parent is asked to give it to another widget.
.nf
destroy($)
{
Widget parent = XtParent($);
Time time = CurrentTime;
if ($traversal_focus) {
$sensitive = False;
if (XtIsSubclass(parent, xfwfCommonWidgetClass))
$parent$traverse(parent, TraverseHome, $, time);
}
}
.fi
The border highlight is drawn and removed with two methods, although
few subclasses will want to redefine them. The methods are called by
the \fIfocusIn\fP and \fIfocusOut\fP actions and \fIhighlight_border\fP is also
called by \fIexpose\fP.
.nf
highlight_border($)
{
XRectangle rect[4];
if ($highlightThickness == 0) return;
rect[0].x = 0;
rect[0].y = 0;
rect[0].width = $width;
rect[0].height = $highlightThickness;
rect[1].x = 0;
rect[1].y = 0;
rect[1].width = $highlightThickness;
rect[1].height = $height;
rect[2].x = $width - $highlightThickness;
rect[2].y = 0;
rect[2].width = $highlightThickness;
rect[2].height = $height;
rect[3].x = 0;
rect[3].y = $height - $highlightThickness;
rect[3].width = $width;
rect[3].height = $highlightThickness;
XFillRectangles(XtDisplay($), XtWindow($), $bordergc, rect[0], 4);
}
.fi
.nf
unhighlight_border($)
{
if ($highlightThickness == 0) return;
XClearArea(XtDisplay($), XtWindow($),
0, 0, $width, $highlightThickness, False);
XClearArea(XtDisplay($), XtWindow($),
0, 0, $highlightThickness, $height, False);
XClearArea(XtDisplay($), XtWindow($),
$width - $highlightThickness, 0,
$highlightThickness, $height, False);
XClearArea(XtDisplay($), XtWindow($),
0, $height - $highlightThickness,
$width, $highlightThickness, False);
}
.fi
When the \fIaccept_focus\fP method is called, the widget should try to set
the focus to itself or one of its children. If it succeeds, it returns
\fITrue\fP else \fIFalse\fP. If there are children, each is asked in turn,
until one is found that accepts the focus. If none is found, the
widget checks it's own \fIsensitive\fP resource, to see if it can receive
keyboard events. If so, it sets the focus to itself and returns
\fITrue\fP, otherwise \fIFalse\fP.
.nf
Boolean accept_focus($, Time * time)
{
int i;
if (! XtIsRealized($) || ! $sensitive || ! $traversalOn
|| ! $visible || ! $ancestor_sensitive || ! $managed
|| ! $mapped_when_managed || $being_destroyed) return False;
for (i = 0; i < $num_children; i++)
if (XtCallAcceptFocus($children[i], time)) return True;
if (! $traversal_focus) {
XSetInputFocus(XtDisplay($), XtWindow($), RevertToParent, *time);
$traversal_focus = True;
$highlight_border($);
}
return True;
}
.fi
A Common widget (and most subclasses) return \fITrue\fP for
\fIwould_accept_focus\fP, if the \fIsensitive\fP, \fIvisible\fP and \fItraversalOn\fP
resources are set and none of the children wants the focus.
.nf
Boolean would_accept_focus($)
{
int i;
Widget child;
if (! XtIsRealized($) || ! $sensitive || ! $visible || ! $traversalOn)
return False;
else {
for (i = 0; i < $num_children; i++) {
child = $children[i];
if (XtIsSubclass(child, xfwfCommonWidgetClass)
$child$would_accept_focus(child))
return False;
}
return True;
}
}
.fi
The algorithm behind keyboard traversal
* Handling focus events
If a widget receives a (non-virtual) FocusIn event, this is usually
caused by the \fIaccept_focus\fP method of that widget, except in the case
that a top level widget receives the focus from the window manager. In
the first case, the window can just draw the highlight border, in the
second case, the widget should try to set the focus to one of its
children.
To be able to distinguish the two cases, the \fIaccept_focus\fP method
sets the private instance variable \fItraversal_focus\fP to \fITrue\fP before
it calls \fIXSetInputFocus\fP. The \fIfocusIn\fP action then checks this
variable and if it is not set, calls the \fIaccept_focus\fP method.
The \fIfocusOut\fP action resets \fItraversal_focus\fP to \fIFalse\fP.
The \fItraversal_focus\fP variable can be interpreted to mean, that the
widget has the keyboard focus and that it is because of keyboard
traversal. At least in the Common widget, it can never be \fITrue\fP when
\fItraversalOn\fP is not set. It can also only be \fITrue\fP when the widget
actually has the focus, except in the short time between the
\fIXSetInputFocus\fP call and the delivery of the \fIFocusIn\fP event.
(However, this scheme depends on the \fIfocusOut\fP action for resetting
\fItraversal_focus\fP to \fIFalse\fP, so, if the translation for the
\fIFocusOut\fP event is overridden, it will break down.)
* User events
The \fItraverseXXX\fP actions can be bound to keyboard events. They call
the \fItraverse\fP method, which will try to change the focus in the
indicated direction. The directions are: Home, Up, Left, Down, Right,
Next, Prev. Each direction can be considered a constraint or
criterium for choosing the focus widget, e.g., `Up' selects the
nearest widget that is above the current widget. `Next' and `Prev' are
simpler, in that they do not check the distance, but only the order in
the list of children.
The \fItraverseCurrent\fP action is different. It is usually bound to a
mouse click and its task is to set the focus to the widget itself. It
does this by calling \fIaccept_focus\fP on itself.
The \fItraverse\fP method looks for a widget in the indicated direction,
within the same application. If the direction is not `Next' or `Prev',
the method first recurses upwards, to the toplevel widget. From there
it recurses down again, to all children, grandchildren, etc., looking
for the widget that best matches the criterium. If a widget is found,
the focus will be set to it with a call to \fIXSetInputFocus\fP. The
private variable \fItraversal_focus\fP will be set to \fITrue\fP to indicate
that the widget received the focus as a result of keyboard traversal,
and not from the window manager or any other source.
If the \fIdirection\fP argument is `Next' or `Prev', \fItraverse\fP will try
to set the focus to a sister widget, using the \fIaccept_focus\fP method.
If there is no suitable sister, the parent will be asked to find an
aunt widget, and so on.
Note that the \fItraverse\fP and \fIaccept_focus\fP methods of the Common
widget only set the focus to a child, if the widget itself has
\fItraversalOn\fP. Thus, setting \fItraversalOn\fP to \fIFalse\fP for a certain
widget not only excludes the widget itself from keyboard traversal,
but also all its children.
The \fItraverse\fP function is a method and not a utility function,
because it is expected that a few subclasses may want to redefine it.
E.g., the (not yet existing) Group widget may want to limit traversal
to widgets within itself. (And presumably define new actions to jump
outside the group.)
To check if a widget suits the criterium, two things must be
determined: is the widget eligible for the focus and what is the
distance between the widget and the target position. To be able to
determine if the widget can accept the focus without actually setting
it, a method \fIwould_accept_focus\fP is defined, that returns \fITrue\fP if
the widget is willing to set the focus to itself.
If the \fIdir\fP argument to \fItraverse\fP is \fITraverseNext\fP or
\fITraversePrev\fP, the \fItraverse_to_next\fP or \fItraverse_to_prev\fP utility
functions are called. Otherwise, the \fItraverse\fP method checks the
class of the parent. If the parent is a subclass of \fIXfwfCommon\fP, it
also has a \fItraverse\fP method and the task of finding a widget to
traverse to is delegated to the parent. Otherwise, the desired widget
is looked for with the help of a utility function.
The \fIdir\fP argument is one of Home, Up, Down, Left, Right, Next or
Prev. The \fIcurrent\fP argument holds the widget that currently has the
focus and relative to which the focus will have to move.
\fBdef\fP LARGE_NUMBER = 2000000000
.nf
traverse($, TraversalDirection dir, Widget current, Time * time)
{
Widget w, parent = XtParent($);
Position x, y;
int distance = LARGE_NUMBER;
if (dir == TraverseNextTop)
traverse_to_next_top($, current, time);
else if (dir == TraverseNext)
traverse_to_next($, current, time);
else if (dir == TraversePrev)
traverse_to_prev($, current, time);
else if (XtIsSubclass(parent, xfwfCommonWidgetClass))
$parent$traverse(parent, dir, current, time);
else {
switch (dir) {
case TraverseHome: x = 0; y = 0; break;
case TraverseLeft: x = 0; y = $current$height/2; break;
case TraverseDown: x = $current$width/2; y = $current$height; break;
case TraverseRight: x = $current$width; y = $current$height/2; break;
case TraverseUp: x = $current$width/2; y = 0; break;
}
if (dir != TraverseHome) XtTranslateCoords(current, x, y, x, y);
if (traverse_to_direction($, dir, x, y, w, distance))
XtCallAcceptFocus(w, time);
}
}
.fi
To choose a color that is somewhat darker or lighter than another
color, the function \fIchoose_color\fP queries the RGB values of a pixel
and multiplies them with a factor. If all goes well, the function
returns \fITrue\fP. If the chosen color ends up being the same as the
original, the color gray75 is returned instead.
It is defined as a method, to make it accessible to subclasses. There
seems to be no need to make it available globally.
.nf
Boolean choose_color($, double factor, Pixel base, Pixel * result)
{
XColor color1, color2, dummy;
color1.pixel = base;
XQueryColor(XtDisplay($), DefaultColormapOfScreen(XtScreen($)), color1);
color2.red = min(65535, factor * color1.red);
color2.green = min(65535, factor * color1.green);
color2.blue = min(65535, factor * color1.blue);
if (! XAllocColor(XtDisplay($),
DefaultColormapOfScreen(XtScreen($)), color2))
return False;
if (base == color2.pixel) {
if (! XAllocNamedColor(XtDisplay($),
DefaultColormapOfScreen(XtScreen($)), "gray75",
color2, dummy))
return False;
}
*result = color2.pixel;
return True;
}
.fi
The method \fIlighter_color\fP uses \fIchoose_color\fP to compute a color
that is 1.5 times as bright as the color passed in as argument. The
function result is \fITrue\fP if a color was allocated, else \fIFalse\fP.
.nf
Boolean lighter_color($, Pixel base, Pixel * result)
{
return choose_color($, 1.5, base, result);
}
.fi
The method \fIdarker_color\fP uses \fIchoose_color\fP to compute a color
that is 2/3 times as bright as the color passed in as argument. The
function result is \fITrue\fP if a color was allocated, else \fIFalse\fP.
.nf
Boolean darker_color($, Pixel base, Pixel * result)
{
return choose_color($, 0.667, base, result);
}
.fi
.hi
.hi
.SH "Utilities"
The converter \fIcvtStringToAlignment\fP converts strings like `right',
`top left' and `bottom center' to values of type \fIAlignment\fP.
\fBdef\fP done(type, value) =
do {
if (to->addr != NULL) {
if (to->size < sizeof(type)) {
to->size = sizeof(type);
return False;
}
*(type*)(to->addr) = (value);
} else {
static type static_val;
static_val = (value);
to->addr = (XtPointer)static_val;
}
to->size = sizeof(type);
return True;
}while (0 )
.nf
Boolean cvtStringToAlignment(Display * display, XrmValuePtr args, Cardinal * num_args, XrmValuePtr from, XrmValuePtr to, XtPointer * converter_data)
{
Alignment a = 0;
char c, *t, *s = (char*) from->addr;
if (*num_args != 0)
XtAppErrorMsg(XtDisplayToApplicationContext(display),
"cvtStringToAlignment", "wrongParameters",
"XtToolkitError",
"String to Alignment conversion needs no arguments",
(String*) NULL, (Cardinal*) NULL);
while (*s) {
for (; isspace(*s); s++) ;
for (t = s; *t ! isspace(*t); t++) ;
c = *t;
*t = '\\0';
if (XmuCompareISOLatin1(s, "top") == 0) a |= XfwfTop;
else if (XmuCompareISOLatin1(s, "bottom") == 0) a |= XfwfBottom;
else if (XmuCompareISOLatin1(s, "center") == 0) ; /* skip */
else if (XmuCompareISOLatin1(s, "left") == 0) a |= XfwfLeft;
else if (XmuCompareISOLatin1(s, "right") == 0) a |= XfwfRight;
else {
XtDisplayStringConversionWarning(display, (char*) from->addr,
"Alignment");
break;
}
*t = c;
s = t;
}
done(Alignment, a);
}
.fi
The converter \fIcvtAlignmentToString\fP does the reverse: it convertes values of type \fIAlignment\fP (\fIint\fP's) to strings.
.nf
Boolean cvtAlignmentToString(Display * display, XrmValuePtr args, Cardinal * num_args, XrmValuePtr from, XrmValuePtr to, XtPointer * converter_data)
{
Alignment *a = (Alignment*) from->addr;
if (*num_args != 0)
XtAppErrorMsg(XtDisplayToApplicationContext(display),
"cvtAlignmentToString", "wrongParameters",
"XtToolkitError",
"Alignment to String conversion needs no arguments",
(String*) NULL, (Cardinal*) NULL);
switch (*a) {
case XfwfCenter: done(String, "center");
case XfwfBottom: done(String, "bottom");
case XfwfTop: done(String, "top");
case XfwfLeft: done(String, "left");
case XfwfRight: done(String, "right");
case XfwfBottom + XfwfLeft: done(String, "bottom left");
case XfwfBottom + XfwfRight: done(String, "bottom right");
case XfwfTop + XfwfLeft: done(String, "top left");
case XfwfTop + XfwfRight: done(String, "top right");
default: done(String, "unknown");
}
}
.fi
The following string is the set of translations that will be added
to any widget that has \fItraversalOn\fP set to \fITrue\fP. The string is
compiled into Xt's internal representation by the \fIclass_initialize\fP
method.
.nf
char extraTranslations[] = "\\
<FocusIn>: focusIn()\\n\\
<FocusOut>: focusOut()\\n\\
<Key>Up: traverseUp()\\n\\
<Key>Down: traverseDown()\\n\\
<Key>Left: traverseLeft()\\n\\
<Key>Right: traverseRight()\\n\\
<Key>Next: traverseNext()\\n\\
~Shift<Key>Tab: traverseNext()\\n\\
<Key>Prior: traversePrev()\\n\\
Shift<Key>Tab: traversePrev()\\n\\
<Key>KP_Enter: traverseNextTop()\\n\\
<Key>Home: traverseHome()"
.fi
The \fIcreate_bordergc\fP function creates a new GC for filling the
highlight border with.
.nf
create_bordergc($)
{
XtGCMask mask;
XGCValues values;
if ($bordergc) XtReleaseGC($, $bordergc);
if ($highlightPixmap != None) {
mask = GCFillStyle | GCTile;
values.fill_style = FillTiled;
values.tile = $highlightPixmap;
} else {
mask = GCFillStyle | GCForeground;
values.fill_style = FillSolid;
values.foreground = $highlightColor;
}
$bordergc = XtGetGC($, mask, values);
}
.fi
The \fItraverse_to_direction\fP function returns the nearest child,
grandchild, etc. in the indicated direction that is willing to accept
the focus. It returns \fIFalse\fP if no widget is found. The position is the
absolute coordinates, i.e., relative to the root window. The \fIdistance\fP
argument holds the distance from \fIx,y\fP of the best widget so far. If the
function finds a better one, it will return the new distance through
this parameter.
.nf
Boolean traverse_to_direction($, TraversalDirection dir, int x, int y, Widget * found, int * distance)
{
int i;
Widget child, w;
Position rx, ry;
int dist;
Boolean found_child = False;
if (! $traversalOn) return False;
/*
* First recurse to all descendants
*/
for (i = 0; i < $num_children; i++)
if (XtIsSubclass($children[i], xfwfCommonWidgetClass)
traverse_to_direction($children[i], dir, x, y, found, distance))
found_child = True;
if (found_child) return True;
/*
* No child found, now check own position and distance
*/
switch (dir) {
case TraverseHome: rx = 0; ry = 0; break;
case TraverseLeft: rx = $width; ry = $height/2; break;
case TraverseDown: rx = $width/2; ry = 0; break;
case TraverseRight: rx = 0; ry = $height/2; break;
case TraverseUp: rx = $width/2; ry = $height; break;
}
XtTranslateCoords($, rx, ry, rx, ry);
if ((dir == TraverseUp ry > y)
|| (dir == TraverseLeft rx > x)
|| (dir == TraverseDown ry < y)
|| (dir == TraverseRight rx < x)) return False;
dist = (rx - x)*(rx - x) + (ry - y)*(ry - y);
if (dist >= *distance) return False;
/*
* We are the best so far, but do we want the focus?
*/
if (! $would_accept_focus($)) return False;
*distance = dist;
*found = $;
return True;
}
.fi
The \fItraverse_to_next\fP routine looks for the \fIcurrent\fP widget among
its children. If it is found, all children following it will be tried
until one accepts the focus. If no child does, the routine will try to
ask the parent to find a sister widget instead.
.nf
traverse_to_next($, Widget current, Time * time)
{
int i = 0;
Widget parent = XtParent($);
while (i < $num_children $children[i] != current) i++;
for (i++; i < $num_children; i++)
if (XtCallAcceptFocus($children[i], time)) return;
if (XtIsSubclass(parent, xfwfCommonWidgetClass))
$parent$traverse(parent, TraverseNext, $, time);
}
.fi
\fItraverse_to_prev\fP looks for the \fIcurrent\fP widget among the children,
if it is found, all children before it will be asked in turn to accept
the focus. If none does, the parent is asked to set the focus to a
sister instead.
.nf
traverse_to_prev($, Widget current, Time * time)
{
int i = 0;
Widget parent = XtParent($);
while (i < $num_children $children[i] != current) i++;
for (i--; i >= 0; i--)
if (XtCallAcceptFocus($children[i], time)) return;
if (XtIsSubclass(parent, xfwfCommonWidgetClass))
$parent$traverse(parent, TraversePrev, $, time);
}
.fi
.nf
traverse_to_next_top($, Widget current, Time * time)
{
Widget parent = XtParent($);
if (XtIsSubclass(parent, xfwfCommonWidgetClass))
$parent$traverse(parent, TraverseNextTop, current, time);
else
XtCallCallbackList($, $nextTop, NULL);
}
.fi
.hi
|