📄 xmcombo.c
字号:
* in der die Drop-Down-Liste steckt. Und da nur Geschwisterfenster (sibling
* windows) im gleichen Stapel stecken, reicht das Shellfenster nicht aus.
* Alle gaengigen Fenstermanager sind solche "reparenting wm's", so dass ich
* hier zu diesem Trick greifen kann, um die Drop-Down-Liste immer ueber der
* ComboBox zu halten.
*
* Parameter:
* w Diejenige Combo-Box, fuer die wir dasjenige
* Fenster des Window-Managers ermitteln sollen,
* dass direkt unterhalb des Root-Fensters liegt.
* Ergebnis:
* besagtes zu suchendes Fenster, dass die Dekoration enthaelt (hoffentlich
* nur echte Bruesseler Spitze!)
*/
static Window GetDecorationWindow(XmComboBoxWidget w)
{
Window Root, Parent, AWindow;
Window *Children;
unsigned int NumChildren;
Parent = XtWindow((Widget) w);
/* Suche nach dem Dekorationsfenster des Window-Managers */
do {
AWindow = Parent;
XQueryTree(XtDisplay((Widget) w), AWindow,
&Root, &Parent, &Children, &NumChildren);
XFree((char *) Children);
} while ( Parent != Root );
return AWindow;
} /* GetDecorationWindow */
/* --------------------------------------------------------------------
* Eine Combo-Box aus dem Wege raeumen...
* Momentan muessen wir hier nur den Cursor wieder los werden sowie
* eventuell reservierte Pixmaps.
* Ups -- natuerlich muss auch wieder der Callback entfernt werden,
* der noch an der Shell haengt.
*/
static void Destroy(XmComboBoxWidget w)
{
/* fprintf(stderr, "Destroy: %08X\n", w->core.window);*/
if ( w->combobox.ConvertBitmapToPixmap )
XFreePixmap(XtDisplay((Widget) w),
w->combobox.LabelPixmap);
if ( w->combobox.ConvertBitmapToPixmapInsensitive )
XFreePixmap(XtDisplay((Widget) w),
w->combobox.LabelInsensitivePixmap);
if ( w->combobox.PendingFocusOut )
XtRemoveWorkProc(w->combobox.WorkProcID);
XtRemoveEventHandler(w->combobox.MyNextShell,
StructureNotifyMask | FocusChangeMask,
True, (XtEventHandler) ShellCallback,
(XtPointer) w);
} /* Destroy */
/* ---------------------------------------------------------------------------
* Ueberpruefe, ob fuer die Ressource "DropDownOffset" ein gueltiger Wert vom
* Benutzer angegeben wurde. Diese Ressource gibt an, wie weit die Drop-Down-
* Liste nach rechts gegenueber dem Eingabefeld eingerueckt sein soll. Wenn
* hierfuer ein negativer Wert angegeben ist, so berechne statt dessen einen
* Standardwert: dieser entspricht der Breite der Pfeilschaltflaeche, was
* optisch ganz gut wirkt (jedenfall nach meinem Dafuerhalten).
*/
static void CheckDropDownOffset(XmComboBoxWidget w)
{
if ( w->combobox.DropDownOffset < 0 ) {
XtWidgetGeometry ArrowGeom;
XtQueryGeometry(w->combobox.ArrowCtrl, NULL, &ArrowGeom);
w->combobox.DropDownOffset = ArrowGeom.width;
}
} /* CheckDropDownOffset */
/* --------------------------------------------------------------------
* Berechne die voreinzustellende Groesse, die diese Combo-Box be-
* sitzen muss, um ausreichenden Raum fuer das Eingabefeld und den
* Pfeil rechts daneben zur Verfuegung zu stellen. Bei einer
* editierbaren Combo-Box ist zwischen dem Eingabefeld und dem Pfeil
* noch ein Angst-Rasen von der halben Breite eines Pfeiles vorhanden.
* Wird das Listenfeld staendig dargestellt, so entfallen sowohl Pfeil
* als auch der Angstrasen, dafuer muss aber die Hoehe des Listenfelds
* beruecksichtigt werden.
*/
static void DefaultGeometry(XmComboBoxWidget w,
Dimension *TotalWidth,
Dimension *TotalHeight,
Dimension *EditCtrlWidth,
Dimension *LabelCtrlWidth)
{
XtWidgetGeometry EditGeom, ArrowGeom, LabelGeom, ListGeom;
XtQueryGeometry(w->combobox.EditCtrl, NULL, &EditGeom);
XtQueryGeometry(w->combobox.ArrowCtrl, NULL, &ArrowGeom);
XtQueryGeometry(w->combobox.LabelCtrl, NULL, &LabelGeom);
/*
* Soll die Pfeilschaltflaeche quadratisch, praktisch, gut sein?
*/
if ( w->combobox.SquareArrow )
ArrowGeom.width = ArrowGeom.height;
else
ArrowGeom.width = (ArrowGeom.height * 4) / 5;
/*
* Zuerst einmal ein paar einfache Werte ermitteln und zurueckgeben...
*/
*TotalHeight = EditGeom.height;
*EditCtrlWidth = EditGeom.width;
*LabelCtrlWidth = LabelGeom.width;
/*
* Ermittele nun die Breite, welche die Combobox benoetigt. Je nach-
* dem, ob das Eingabefeld oder die Liste breiter sind, wird der
* entsprechende Wert genommen. Diese Auswahl zwischen der Breite von
* Eingabefeld und Liste findet aber nur statt, wenn die Liste auch
* wirklich staendig sichtbar ist. Waehrend der Initialisierung hat
* allerdings XmNcolumns, so dass in diesem Moment die List nicht
* mehr die Breite kontrollieren kann!
*/
if ( w->combobox.StaticList ) {
/*
* Beachte: Frage nicht die Listbox, sondern das ScrolledWindow,
* in welchem die Liste eingebettet ist.
*/
CheckDropDownOffset(w);
XtQueryGeometry(XtParent(w->combobox.ListCtrl), NULL, &ListGeom);
if ( w->combobox.InInit ) {
*TotalWidth = EditGeom.width;
} else {
if ( EditGeom.width < (Dimension)
(ListGeom.width + w->combobox.DropDownOffset) )
*TotalWidth = ListGeom.width + w->combobox.DropDownOffset;
else
*TotalWidth = EditGeom.width;
}
*TotalHeight += ListGeom.height;
} else {
/*
* Das Listenfeld interessiert uns hier nicht. Degegen sollte noch
* die Breite fuer den Pfeil und ein evtl. Angstrasen beachtet
* werden.
*/
*TotalWidth = EditGeom.width + ArrowGeom.width;
if ( w->combobox.Editable && w->combobox.ArrowSpacingOn )
*TotalWidth += ArrowGeom.width/2;
}
/*
* Vergiss nicht, auch noch ein evtl. sichtbares Schriftfeld zu berueck-
* sichtigen!
*/
if ( w->combobox.ShowLabel )
*TotalWidth += LabelGeom.width;
} /* DefaultGeometry */
/* --------------------------------------------------------------------
* Anhand eines Widgets ermittele darueber die Screennummer desjenigen
* Screens, auf dem das Widget erscheint.
* Parameter:
* w betroffenes Widget.
* Ergebnis:
* Nummer desjenigen Screens, auf dem das Widget angezeigt wird.
*/
static int WidgetToScreen(Widget w)
{
Screen *screen;
Display *display;
int NumScreens, i;
screen = XtScreen(w); NumScreens = ScreenCount(XtDisplay(w));
display = DisplayOfScreen(screen);
for ( i = 0; i < NumScreens; ++i )
if ( ScreenOfDisplay(display, i) == screen )
return i;
XtError("WidgetToScreen: data structures are destroyed.");
return 0; /* to avoid a compiler warning */
} /* WidgetToScreen */
/* --------------------------------------------------------------------
* Positioniere die DropDown-Liste (soweit sie natuerlich auch momentan
* sichtbar ist) so auf dem Bildschirm, dass sie sich unterhalb des
* Eingabefeldes anschliesst.
*/
static void DoDropDownLayout(XmComboBoxWidget w)
{
Position abs_x, abs_y;
Dimension ArrowWidth, ListWidth, ListHeight;
Dimension ScreenHeight, LabelWidth;
XWindowChanges WindowChanges;
/*
* etwa nicht sichtbar ?!! Oder etwa immer sichtbar ?!!
* Dann sind wir jetzt sofort fertig.
*/
if ( !w->combobox.ListVisible || w->combobox.StaticList ) return;
/*
* Finde zuerst einmal heraus, wo wir uns denn auf dem Bildschirm be-
* finden sollen... Beachte dabei auch, dass eventuell die Liste zu schmal
* werden koennte und gib' ihr dann ggf. eine Mindestbreite, damit es
* keinen core-Dump gibt.
*/
XtVaGetValues(w->combobox.ArrowCtrl, XmNwidth, &ArrowWidth, NULL);
XtTranslateCoords((Widget) w, 0, w->core.height, &abs_x, &abs_y);
CheckDropDownOffset(w);
ListWidth = w->core.width - w->combobox.DropDownOffset - 2;
abs_x += w->combobox.DropDownOffset;
if ( w->combobox.ShowLabel ) {
XtVaGetValues(w->combobox.LabelCtrl, XmNwidth, &LabelWidth, NULL);
ListWidth -= LabelWidth;
abs_x += LabelWidth;
}
if ( ListWidth < 20 ) ListWidth = 20;
XtVaGetValues(XtParent(w->combobox.ListCtrl), XmNheight, &ListHeight, NULL);
/*
* Hier ueberpruefen wir noch, ob die Liste unten aus dem Bildschirm
* herausfallen wuerde. In dem Fall klappen wir die Liste oberhalb des
* Eingabefeldes auf.
*/
ScreenHeight = DisplayHeight(XtDisplay((Widget) w),
WidgetToScreen((Widget) w));
if ( abs_y + ListHeight + 2 > ScreenHeight ) {
int y;
y = ((int) abs_y) - ListHeight - w->core.height - 1;
if ( y < 0 ) y = 0;
abs_y = (Position) y;
}
XtConfigureWidget(w->combobox.PopupShell,
abs_x, abs_y, ListWidth, ListHeight, 1);
/*
* So...das hier dient der Kosmetik: hier sorgen wir dafuer, dass die
* Liste auch wirklich immer direkt ueber der ComboBox innerhalb des
* Fensterstapels schwebt. Siehe dazu auch die Erlaeuterungen und An-
* merkungen in GetDecorationWindow().
*/
if ( XtIsRealized((Widget) w) ) {
WindowChanges.sibling = GetDecorationWindow(w);
WindowChanges.stack_mode = Above;
XReconfigureWMWindow(XtDisplay((Widget) w),
XtWindow(w->combobox.PopupShell),
WidgetToScreen(w->combobox.PopupShell),
CWSibling | CWStackMode, &WindowChanges);
}
} /* DoDropDownLayout */
/* --------------------------------------------------------------------
* Naja... diese Routine scheint ja bereits zu einer Institution beim
* Schreiben von Composite-Widgets geworden zu sein.
*
* Hier beim ComboBox-Widget ist die Aufgabe ziemlich einfach: es
* genuegt, die Eingabezeile und den Pfeil-Button entsprechend inner-
* halb des ComboBox-Widgets zu plazieren. Seit allerdings noch das
* Textlabel hinzukommt, wird's langsam aufwendiger. Nun ja - da sich
* das Listenfeld wahlweise auch statisch einblenden laesst, ist nun
* noch mehr zu beruecksichtigen, wenn die Kinder-Widgets an ihre
* Plaetze geschoben werden.
*/
static void DoLayout(XmComboBoxWidget w)
{
Dimension EditCtrlWidth, ArrowCtrlWidth, LabelCtrlWidth;
Dimension ComboBoxHeight;
Dimension BorderWidth;
Dimension HighlightThickness;
Position EditX;
XtVaGetValues(w->combobox.ArrowCtrl,
XmNheight, &ArrowCtrlWidth, NULL);
if ( !w->combobox.SquareArrow )
ArrowCtrlWidth = (ArrowCtrlWidth * 4) / 5;
XtVaGetValues(w->combobox.LabelCtrl,
XmNwidth, &LabelCtrlWidth, NULL);
/*
* In Abhaengigkeit davon, ob die ComboBox editierbar ist und ob das
* Listenfeld staendig sichtbar sein soll, hier die Breite einzelner
* Widgets bestimmen.
*/
if ( w->combobox.StaticList ) {
ComboBoxHeight = w->combobox.EditCtrl->core.height;
EditCtrlWidth = w->core.width;
} else {
ComboBoxHeight = w->core.height;
EditCtrlWidth = w->core.width - ArrowCtrlWidth;
if ( w->combobox.Editable && w->combobox.ArrowSpacingOn )
EditCtrlWidth -= ArrowCtrlWidth/2;
}
/* Beruecksichtige noch ein evtl. ebenfalls anzuzeigendes Schriftfeld
* neben dem Eingabefeld.
*/
if ( w->combobox.ShowLabel ) {
EditX = LabelCtrlWidth;
EditCtrlWidth -= LabelCtrlWidth;
} else
EditX = 0;
if ( EditCtrlWidth < 20 ) EditCtrlWidth = 20;
/* Plaziere nun das Eingabefeld... */
XtVaGetValues(w->combobox.EditCtrl,
XmNborderWidth, &BorderWidth,
XmNhighlightThickness, &HighlightThickness,
NULL);
XtConfigureWidget(w->combobox.EditCtrl,
EditX, 0,
EditCtrlWidth, ComboBoxHeight, BorderWidth);
/* ...und nun den Pfeil... */
XtVaGetValues(w->combobox.ArrowCtrl,
XtNborderWidth, &BorderWidth, NULL);
XtConfigureWidget(w->combobox.ArrowCtrl,
w->core.width-ArrowCtrlWidth, HighlightThickness,
ArrowCtrlWidth,
ComboBoxHeight - 2 * HighlightThickness,
BorderWidth);
/* ...und ggf. das Textlabel. */
if ( w->combobox.ShowLabel ) {
XtVaGetValues(w->combobox.LabelCtrl,
XmNborderWidth, &BorderWidth,
NULL);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -