/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public License * Version 1.0 (the "NPL"); you may not use this file except in * compliance with the NPL. You may obtain a copy of the NPL at * http://www.mozilla.org/NPL/ * * Software distributed under the NPL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL * for the specific language governing rights and limitations under the * NPL. * * The Initial Developer of this code under the NPL is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ /* forms.c --- creating and displaying forms widgets Created: Jamie Zawinski , 21-Jul-94. Whacked into a completely new form: Chris Toshok , 29-Oct-1997. */ #include "mozilla.h" #include "xfe.h" #include "fonts.h" #include "felocale.h" #include "xpform.h" #include "layers.h" #include /* for mocha */ #include #include #include #include "intl_csi.h" #include "fonts.h" #ifndef NO_WEB_FONTS #include "nf.h" #include "Mnfrc.h" #include "Mnfrf.h" #endif #include "DtWidgets/ComboBoxP.h" #include /* for xfe widgets and utilities */ /* XXX */ /* #define NO_NEW_DEFAULT_SIZES 1 */ /* for XP_GetString() */ #include extern int XFE_ERROR_OPENING_FILE; /* Kludge around conflicts between Motif and xp_core.h... */ #undef Bool #define Bool char /* #define FORMS_ARE_FONTIFIED */ /* #define FORMS_ARE_COLORED */ /* Kludge kludge kludge, should be in resources... */ #define fe_FORM_LEFT_MARGIN 0 #define fe_FORM_RIGHT_MARGIN 0 #define fe_FORM_TOP_MARGIN 4 #define fe_FORM_BOTTOM_MARGIN 4 static void fe_form_file_browse_cb (Widget, XtPointer, XtPointer); static void fe_activate_submit_cb (Widget, XtPointer, XtPointer); static void fe_submit_form_cb (Widget, XtPointer, XtPointer); static void fe_reset_form_cb (Widget, XtPointer, XtPointer); static void fe_radio_form_cb (Widget, XtPointer, XtPointer); static void fe_check_form_cb (Widget, XtPointer, XtPointer); static void fe_button_form_cb (Widget, XtPointer, XtPointer); static void fe_combo_form_cb(Widget, XtPointer, XtPointer); static void fe_got_focus_cb (Widget, XtPointer, XtPointer); static void fe_list_form_cb (Widget, XtPointer, XtPointer); static void fe_lost_focus_cb (Widget, XtPointer, XtPointer); static void fe_mocha_focus_notify_eh (Widget w, XtPointer closure, XEvent *ev, Boolean *cont); static void fe_key_handler(Widget w, XtPointer client_data, XEvent * xevent, Boolean * bcontinue); static void fe_complete_js_event(JSEvent *js_event, XEvent *x_event, XP_Bool do_geometry); #ifdef DEBUG static char * private_precondition_format = "debug: precondition violation at %s:%d\n"; static char * private_check_format = "debug: check violation at %s:%d\n"; #endif typedef struct fe_form_vtable FEFormVtable; typedef struct fe_form_data FEFormData; struct fe_form_vtable { void (*create_widget_func)(FEFormData *, LO_FormElementStruct *); void (*get_size_func)(FEFormData *, LO_FormElementStruct *); void (*element_is_submit_func)(FEFormData *, LO_FormElementStruct *); void (*display_element_func)(FEFormData *, LO_FormElementStruct *); void (*get_element_value)(FEFormData *, LO_FormElementStruct *, XP_Bool); void (*free_element_func)(FEFormData *, LO_FormElementData *); void (*reset_element)(FEFormData *, LO_FormElementStruct *); void (*select_element_func)(FEFormData *, LO_FormElementStruct *); void (*change_element_func)(FEFormData *, LO_FormElementStruct *); void (*focus_element_func)(FEFormData *, LO_FormElementStruct *); void (*lost_focus_func)(FEFormData *); }; struct fe_form_data { FEFormVtable vtbl; LO_FormElementStruct *form; Widget widget; MWContext *context; }; typedef struct { FEFormData form_data; Widget rowcolumn; Widget browse_button; Widget file_widget; } FEFileFormData; typedef struct { FEFormData form_data; Widget text_widget; /* form_data.widget is the scrolled window. */ } FETextAreaFormData; typedef struct { FEFormData form_data; Widget list_widget; /* form_data.widget is the scrolled window. */ int nkids; char *selected_p; } FESelectMultFormData; typedef FESelectMultFormData FESelectOneFormData; static FEFormData* alloc_form_data(int32 type); static void text_create_widget(FEFormData *, LO_FormElementStruct *); static void text_display(FEFormData *, LO_FormElementStruct *); static void text_get_value(FEFormData *, LO_FormElementStruct *, XP_Bool); static void text_reset(FEFormData *, LO_FormElementStruct *); static void text_change(FEFormData *, LO_FormElementStruct *); static void text_focus(FEFormData *, LO_FormElementStruct *); static void text_lost_focus(FEFormData *); static void text_select(FEFormData *, LO_FormElementStruct *); static void file_create_widget(FEFormData *, LO_FormElementStruct *); static void file_get_value(FEFormData *, LO_FormElementStruct *, XP_Bool); static void file_free(FEFormData *, LO_FormElementData *); static void button_create_widget(FEFormData *, LO_FormElementStruct *); static void checkbox_create_widget(FEFormData *, LO_FormElementStruct *); static void checkbox_get_value(FEFormData *, LO_FormElementStruct *, XP_Bool); static void checkbox_change(FEFormData *, LO_FormElementStruct *); static void select_create_widget(FEFormData *, LO_FormElementStruct *); static void select_get_value(FEFormData *, LO_FormElementStruct *, XP_Bool); static void select_free(FEFormData *, LO_FormElementData *); static void select_reset(FEFormData *, LO_FormElementStruct *); static void select_change(FEFormData *, LO_FormElementStruct *); static void textarea_create_widget(FEFormData *, LO_FormElementStruct *); static void textarea_display(FEFormData *, LO_FormElementStruct *); static void textarea_get_value(FEFormData *, LO_FormElementStruct *, XP_Bool); static void textarea_reset(FEFormData *, LO_FormElementStruct *); static void textarea_lost_focus(FEFormData *); static void form_element_display(FEFormData *, LO_FormElementStruct *); static void form_element_get_size(FEFormData *, LO_FormElementStruct *); static void form_element_is_submit(FEFormData *, LO_FormElementStruct *); static void form_element_get_value(FEFormData *, LO_FormElementStruct *, XP_Bool); static void form_element_free(FEFormData *, LO_FormElementData *); /* doubles as both the text and password vtable */ static FEFormVtable text_form_vtable = { text_create_widget, form_element_get_size, form_element_is_submit, text_display, text_get_value, form_element_free, text_reset, text_select, text_change, text_focus, text_lost_focus }; static FEFormVtable file_form_vtable = { file_create_widget, form_element_get_size, form_element_is_submit, text_display, file_get_value, file_free, text_reset, text_select, text_change, text_focus, text_lost_focus }; static FEFormVtable button_form_vtable = { button_create_widget, form_element_get_size, NULL, form_element_display, form_element_get_value, form_element_free, NULL, NULL, NULL, NULL, NULL }; static FEFormVtable checkbox_form_vtable = { checkbox_create_widget, form_element_get_size, NULL, form_element_display, checkbox_get_value, form_element_free, NULL, NULL, checkbox_change, NULL, NULL }; static FEFormVtable selectone_form_vtable = { select_create_widget, form_element_get_size, NULL, form_element_display, select_get_value, select_free, select_reset, NULL, select_change, NULL, NULL }; static FEFormVtable selectmult_form_vtable = { select_create_widget, form_element_get_size, NULL, form_element_display, select_get_value, select_free, select_reset, NULL, select_change, NULL, NULL }; static FEFormVtable textarea_form_vtable = { textarea_create_widget, form_element_get_size, NULL, textarea_display, textarea_get_value, form_element_free, textarea_reset, text_select, text_change, text_focus, textarea_lost_focus }; /* ** Why are these next two functions in this file? */ /* * Raise the window to the top of the view order */ void FE_RaiseWindow(MWContext *context) { #ifdef DEBUG_username printf ("RaiseWindow: context 0x%x\n", context); #endif XtRealizeWidget (CONTEXT_WIDGET (context)); XtManageChild (CONTEXT_WIDGET (context)); /* XXX Uniconify the window if it was iconified */ XMapRaised(XtDisplay(CONTEXT_WIDGET(context)), XtWindow(CONTEXT_WIDGET(context))); XtPopup(CONTEXT_WIDGET(context),XtGrabNone); } /* * Lower the window to the bottom of the view order */ void FE_LowerWindow(MWContext *context) { #ifdef DEBUG_username printf ("LowerWindow: context 0x%x\n", context); #endif if (!XtIsRealized(CONTEXT_WIDGET(context))) return; XLowerWindow(XtDisplay(CONTEXT_WIDGET(context)), XtWindow(CONTEXT_WIDGET(context))); } static void fe_font_list_metrics(MWContext *context, LO_FormElementStruct *form, XmFontList font_list, int *ascentp, int *descentp) { fe_Font font; LO_TextAttr *text = XP_GetFormTextAttr(form); font = fe_LoadFontFromFace(context,text, &text->charset, text->font_face, text->size, text->fontmask); if (!font) { *ascentp = 14; *descentp = 3; return; } FE_FONT_EXTENTS(text->charset, font, ascentp, descentp); } static fe_Font fe_LoadFontForWidgetUnicodePseudoFont (MWContext *context, Widget parent, LO_TextAttr *text_attr, XmFontList *fontlist, XmFontType *type, int mask ) { fe_Font fe_font; fe_font = fe_LoadUnicodeFont(NULL, "", 0, text_attr->size, mask, 0, 0, 0, XtDisplay(parent)); /* This looks very weired , but it looks like this before I split the code */ *fontlist = NULL; return fe_font; } static fe_Font fe_LoadFontForWidgetLocale (MWContext *context, Widget parent, LO_TextAttr *text_attr, XmFontList *fontlist, XmFontType *type, int mask ) { fe_Font fe_font; XmFontListEntry flentry; XmFontList locale_font_list = NULL; fe_font = fe_GetFontOrFontSetFromFace(context, NULL, text_attr->font_face, text_attr->size, mask, type); XP_ASSERT(NULL != fe_font); if (!fe_font) return NULL; flentry = XmFontListEntryCreate(XmFONTLIST_DEFAULT_TAG, *type, fe_font); XP_ASSERT(NULL != flentry); if (!flentry) return NULL; locale_font_list = XmFontListAppendEntry(NULL, flentry); XP_ASSERT(NULL != locale_font_list); if (!locale_font_list) return NULL; XmFontListEntryFree(&flentry); *fontlist = locale_font_list; return fe_font; } static void fe_FontlistAndXmStringForOptions( MWContext *context, LO_TextAttr *text_attr, LO_FormElementData *form_data, int nitems, XmString *xmstrings, XmFontList *fontlist) { Widget parent = CONTEXT_DATA(context)->drawing_area; XP_Bool use_UnicodePseudoFont = IS_UNICODE_CSID(text_attr->charset); fe_Font fe_font; XmFontType type = 0; /* keep purify happy */ if(use_UnicodePseudoFont) { fe_font = fe_LoadFontForWidgetUnicodePseudoFont(context, parent, text_attr, fontlist, &type, text_attr->fontmask); if(*fontlist) *fontlist = XmFontListCopy(*fontlist); } else { fe_font = fe_LoadFontFromFace( context, text_attr, &text_attr->charset, text_attr->font_face, text_attr->size, text_attr->fontmask ); FE_NONEDIT_FONTLIST(text_attr->charset, fe_font, *fontlist); } if (xmstrings) { int i; for(i = 0 ; i < nitems; i++) { unsigned char* text = (unsigned char*) XP_FormSelectGetOption(form_data, i)->text_value; if(NULL == text) text = (unsigned char*) "---\?\?\?---"; if(use_UnicodePseudoFont) { xmstrings[i] = fe_ConvertToXmString( text, text_attr->charset, fe_font, type, fontlist); } else { xmstrings[i] = FE_NONEDIT_TO_XMSTRING(text_attr->charset, fe_font, text, XP_STRLEN(text) ); } } } } static void fe_FontlistAndXmStringForButton(MWContext *context, int32 form_type, char *string, LO_TextAttr *text_attr, XmString *xmstring, XmFontList *fontlist) { Widget parent = CONTEXT_DATA(context)->drawing_area; XP_Bool use_UnicodePseudoFont; fe_Font fe_font; XmFontType type = 0; /* keep purify happy */ use_UnicodePseudoFont = IS_UNICODE_CSID(text_attr->charset); if (xmstring) *xmstring = NULL; if( use_UnicodePseudoFont ) { fe_font = fe_LoadFontForWidgetUnicodePseudoFont(context, parent, text_attr, fontlist, &type, text_attr->fontmask); XP_ASSERT(NULL != fe_font); if (string && xmstring) *xmstring = fe_ConvertToXmString((unsigned char*)string, text_attr->charset, fe_font, type, fontlist); if(*fontlist) *fontlist = XmFontListCopy(*fontlist); } else { fe_font = fe_LoadFontFromFace( context, text_attr, &text_attr->charset, text_attr->font_face, text_attr->size, text_attr->fontmask ); FE_NONEDIT_FONTLIST(text_attr->charset, fe_font, *fontlist); if (string && xmstring) { *xmstring = FE_NONEDIT_TO_XMSTRING(text_attr->charset, fe_font, string, XP_STRLEN(string) ); } } } static void fe_FontlistAndXmStringForFormElement(MWContext *context, int32 form_type, char *string, LO_TextAttr *text_attr, XmString *xmstring, XmFontList *fontlist) { Widget parent = CONTEXT_DATA(context)->drawing_area; XP_Bool use_UnicodePseudoFont; fe_Font fe_font; XmFontType type = 0; /* keep purify happy */ int mask = text_attr->fontmask; use_UnicodePseudoFont = IS_UNICODE_CSID(text_attr->charset); #ifdef DEBUG_username printf ("fe_FontlistAndXmStringForFormElement(%s)\n", string); #endif switch (form_type) { case FORM_TYPE_FILE: case FORM_TYPE_TEXT: case FORM_TYPE_PASSWORD: case FORM_TYPE_TEXTAREA: case FORM_TYPE_READONLY: /* Make sure text fields are non-bold, non-italic, fixed. */ mask &= (~ (LO_FONT_BOLD|LO_FONT_ITALIC)); mask |= LO_FONT_FIXED; use_UnicodePseudoFont = FALSE; /* XmText doesn't use XmString */ break; default: break; } if (xmstring) *xmstring = NULL; if(use_UnicodePseudoFont) fe_font = fe_LoadFontForWidgetUnicodePseudoFont(context, parent, text_attr, fontlist, &type, mask); else fe_font = fe_LoadFontForWidgetLocale(context, parent, text_attr, fontlist, &type, mask); XP_ASSERT(NULL != fe_font); if (string && xmstring) *xmstring = fe_ConvertToXmString((unsigned char*)string, text_attr->charset, fe_font, type, fontlist); } static void set_form_colors(FEFormData *fed, LO_Color *lo_fg, LO_Color *lo_bg) { Widget parent = CONTEXT_DATA (fed->context)->drawing_area; Pixel fg, bg; fg = fe_GetPixel (fed->context, lo_fg->red, lo_fg->green, lo_fg->blue); bg = fe_GetPixel (fed->context, lo_bg->red, lo_bg->green, lo_bg->blue); if (fg == bg) { /* the foreground and background are the same. Try to choose something that will make things readable (if a little ugly) */ XmGetColors(XtScreen(parent), XfeColormap(parent), bg, &fg, NULL, NULL, NULL); } XtVaSetValues(fed->widget, XmNforeground, fg, XmNbackground, bg, NULL); } static void get_form_colors(FEFormData *fed, LO_Color *lo_fg, LO_Color *lo_bg, Pixel *fg_r, Pixel *bg_r) { Widget parent = CONTEXT_DATA (fed->context)->drawing_area; Pixel fg, bg; fg = fe_GetPixel (fed->context, lo_fg->red, lo_fg->green, lo_fg->blue); bg = fe_GetPixel (fed->context, lo_bg->red, lo_bg->green, lo_bg->blue); if (fg == bg) { /* the foreground and background are the same. Try to choose something that will make things readable (if a little ugly) */ XmGetColors(XtScreen(parent), XfeColormap(parent), bg, &fg, NULL, NULL, NULL); } *fg_r = fg; *bg_r = bg; } /* Motif likes to complain when there are binary characters in text strings. */ void fe_forms_clean_text (MWContext *context, int charset, char *text, Boolean newlines_too_p) { unsigned char *t = (unsigned char *) text; for (; *t; t++) { unsigned char c = *t; if ((newlines_too_p && (c == '\n' || c == '\r')) || (c < ' ' && c != '\t' && c != '\n' && c != '\r') || (c == 0x7f) || ((INTL_CharSetType(charset) == SINGLEBYTE) && (0x7f <= c) && (c <= 0xa0))) *t = ' '; } } static void fe_HackTextTranslations_addCallbacks(Widget widget) { #if 0 /* make sure it gets added only once */ XtRemoveCallback(widget, XmNmodifyVerifyCallback, fe_textModifyVerifyCallback, NULL); XtRemoveCallback(widget, XmNmotionVerifyCallback, fe_textMotionVerifyCallback, NULL); #endif XtAddCallback(widget, XmNmotionVerifyCallback, fe_textMotionVerifyCallback, NULL); XtAddCallback(widget, XmNmodifyVerifyCallback, fe_textModifyVerifyCallback, NULL); XtVaSetValues(widget, XmNverifyBell,False, NULL); } /* * A list key event handler. * This will search for a key in the XmList and shift the location cursor * to a match. */ static void fe_list_find_eh(Widget widget, XtPointer closure, XEvent* event, Boolean* continue_to_dispatch) { MWContext *context = (MWContext *) closure; Boolean usedEvent_p = False; Modifiers mods; KeySym k; char *s; int i; int index; /* index is 1 based */ XmString *items; int nitems; #define MILLI_500_SECS 500 #define _LIST_MAX_CHARS 100 static char input[_LIST_MAX_CHARS] = {0}; static Time prev_time; static Widget w = 0; static Boolean is_prev_found = False; if (!context || !event) return; fe_UserActivity (context); if (event->type == KeyPress) { if (!(k = XtGetActionKeysym (event, &mods))) return; if (!(s = XKeysymToString (k))) return; if (!s || !*s || s[1]) return; /* no or more than 1 char */ if (!isprint(*s)) return; /* unprintable */ if ((widget == w) && (event->xkey.time - prev_time < MILLI_500_SECS)) { /* Continue search */ if (is_prev_found) strcat(input, s); else return; } else { /* Fresh search */ is_prev_found = False; strcpy(input, s); } w = widget; prev_time = event->xkey.time; index = XmListGetKbdItemPos(widget); if (!index) return; /* List is empty */ /* Wrap search for input[] in List */ i = index; i = (i == nitems) ? 1 : i+1; /* Increment i */ XtVaGetValues(widget, XmNitems, &items, XmNitemCount, &nitems, 0); while (i != index) { if (!XmStringGetLtoR(items[i-1], XmSTRING_DEFAULT_CHARSET, &s)) continue; if (!strncmp(s, input, strlen(input))) { /* Found */ XP_FREE(s); XmListSetKbdItemPos(widget, i); is_prev_found = True; break; } XP_FREE(s); i = (i == nitems) ? 1 : i+1; /* Increment i */ } usedEvent_p = True; } if (usedEvent_p) *continue_to_dispatch = False; } /* ** ** VTABLE for "superclass", form_element. ** */ static void form_element_display(FEFormData *fed, LO_FormElementStruct *form) { MWContext *context = fed->context; Arg av[10]; int ac; uint16 attrmask = XP_GetFormEleAttrmask(form); Position x; Position y; x = (form->x + form->x_offset - CONTEXT_DATA (context)->document_x); y = (form->y + form->y_offset - CONTEXT_DATA (context)->document_y); x += fe_FORM_LEFT_MARGIN; y += fe_FORM_TOP_MARGIN; if (!XtIsManaged(fed->widget)) { Dimension bw = 0; Dimension width = form->width; Dimension height = form->height; XtVaGetValues (fed->widget, XmNborderWidth, &bw, 0); /* Since I told layout that width was width+bw+bw (for its sizing and positioning purposes), subtract it out again when setting the size of the text area. X thinks the borders are outside the WxH rectangle, not inside, but thinks that the borders are below and to the right of the X,Y origin, instead of up and to the left as you would expect given the WxH behavior!! */ width -= (bw * 2); height -= (bw * 2); /* x += bw; */ /* y += bw;*/ width -= fe_FORM_LEFT_MARGIN + fe_FORM_RIGHT_MARGIN; height -= fe_FORM_TOP_MARGIN + fe_FORM_BOTTOM_MARGIN; /* * Move to 0 before moving to where we want so it ends up * where we want. */ ac = 0; XtSetArg (av [ac], XmNx, 0); ac++; XtSetArg (av [ac], XmNy, 0); ac++; XtSetValues (fed->widget, av, ac); #ifdef DEBUG_username printf ("DisplayFormElement: width = %d; height = %d\n", width, height); #endif ac = 0; XtSetArg (av [ac], XmNx, (Position)x); ac++; XtSetArg (av [ac], XmNy, (Position)y); ac++; XtSetArg (av [ac], XmNwidth, (Dimension)width); ac++; XtSetArg (av [ac], XmNheight, (Dimension)height); ac++; XtSetValues (fed->widget, av, ac); XtSetMappedWhenManaged(fed->widget, !(attrmask & LO_ELE_INVISIBLE)); XtManageChild (fed->widget); } else { XtSetMappedWhenManaged(fed->widget, !(attrmask & LO_ELE_INVISIBLE)); XtMoveWidget(fed->widget, x, y); } } static void form_element_get_size(FEFormData *fed, LO_FormElementStruct *form) { MWContext *context = fed->context; Dimension width = 0; Dimension height = 0; Dimension bw = 0; Dimension st = 0; Dimension ht = 0; Dimension margin_height = 0; Dimension bottom = 0; int ascent, descent; XmFontList font_list; LO_FormElementData *form_data = XP_GetFormElementData(form); int32 form_type = XP_FormGetType(form_data); Widget element_widget; if (form_type == FORM_TYPE_FILE) element_widget = ((FEFileFormData*)fed)->rowcolumn; else element_widget = fed->widget; font_list = 0; XtVaGetValues (element_widget, XmNfontList, &font_list, 0); fe_HackTranslations (context, element_widget); fe_font_list_metrics (context, form, font_list, &ascent, &descent); XtRealizeWidget(element_widget); if (fe_globalData.fe_guffaw_scroll == 1) { XSetWindowAttributes attr; unsigned long valuemask; valuemask = CWBitGravity | CWWinGravity; attr.win_gravity = StaticGravity; attr.bit_gravity = StaticGravity; XChangeWindowAttributes (XtDisplay (element_widget), XtWindow (element_widget), valuemask, &attr); } XtVaGetValues (element_widget, XmNwidth, &width, XmNheight, &height, XmNborderWidth, &bw, XmNshadowThickness, &st, XmNhighlightThickness, &ht, XmNmarginHeight, &margin_height, XmNmarginBottom, &bottom, 0); #ifdef DEBUG_username printf ("form before: width = %d; height = %d\n", width, height); #endif width += fe_FORM_LEFT_MARGIN + fe_FORM_RIGHT_MARGIN; height += fe_FORM_TOP_MARGIN + fe_FORM_BOTTOM_MARGIN; /* The WxH doesn't take into account the border width; so add that in for layout's benefit. We subtract it again later. */ width += (bw * 2); height += (bw * 2); #ifdef DEBUG_username printf ("form after: width = %d; height = %d\n", width, height); #endif form->width = width; form->height = height; /* Yeah. Uh huh. */ if (form_type == FORM_TYPE_RADIO || form_type == FORM_TYPE_CHECKBOX) { bottom = 0; descent = bw; } form->baseline = height - (bottom + margin_height + st + bw + ht + descent + fe_FORM_BOTTOM_MARGIN); } static void form_element_is_submit(FEFormData *fed, LO_FormElementStruct *form) { XtRemoveCallback (fed->widget, XmNactivateCallback, fe_activate_submit_cb, fed); XtAddCallback (fed->widget, XmNactivateCallback, fe_activate_submit_cb, fed); if (fe_globalData.terminal_text_translations) XtOverrideTranslations (fed->widget, fe_globalData.terminal_text_translations); } static void form_element_get_value(FEFormData *fed, LO_FormElementStruct *form, XP_Bool delete_p) { if (delete_p) { XtDestroyWidget(fed->widget); fed->widget = 0; } } static void form_element_free(FEFormData *fed, LO_FormElementData *form) { if (fed->widget) XtDestroyWidget(fed->widget); fed->widget = 0; XP_FREE(fed); } /* ** ** VTABLE for Text like form elements (TEXT, TEXTAREA, and FILE) ** */ static void text_create_widget(FEFormData *fed, LO_FormElementStruct *form) { MWContext *context = fed->context; Widget parent; int16 charset; LO_FormElementData *form_data; int32 form_type; int32 text_size; int32 max_size; int columns; char *tmp_text = 0; char *text; unsigned char *loc; XmFontList font_list; Arg av [50]; int ac; LO_TextAttr *text_attr; parent = CONTEXT_DATA (context)->drawing_area; form_data = XP_GetFormElementData(form); form_type = XP_FormGetType(form_data); text_size = XP_FormTextGetSize(form_data); max_size = XP_FormTextGetMaxSize(form_data); text_attr = XP_GetFormTextAttr(form); text = (char*)XP_FormGetDefaultText(form_data); charset = text_attr->charset; /* from bstell: in multibyte (asian) locales, characters tend to be twice as wide as in non-multibyte locales. To make things look correct, we divide the column number in half in those cases. */ columns = ((fe_LocaleCharSetID & MULTIBYTE) ? (text_size + 1) / 2 : text_size); /* the default size for text fields is 20 columns. XX Shouldn't the backend just fill it in with 20? */ if (columns == 0) columns = 20; if (!text) text = ""; if (max_size > 0 && (int) XP_STRLEN (text) >= max_size) { tmp_text = XP_STRDUP (text); tmp_text [max_size] = '\0'; text = tmp_text; } fe_forms_clean_text (context, charset, text, True); fe_FontlistAndXmStringForFormElement(context, form_type, text, text_attr, NULL, &font_list); if (!font_list) return; ac = 0; /* Ok, let's try using the fixed font for text areas only. */ /*#ifdef FORMS_ARE_FONTIFIED*/ XtSetArg (av [ac], XmNfontList, font_list); ac++; /*#endif*/ XtSetArg (av [ac], XmNcolumns, columns); ac++; if (form_type == FORM_TYPE_READONLY) { XtSetArg (av [ac], XmNeditable, False); ac++; XtSetArg (av [ac], XmNcursorPositionVisible, False); ac++; } if (max_size > 0) { XtSetArg (av [ac], XmNmaxLength, max_size); ac++; } fed->widget = fe_CreateTextField (parent, "textForm", av, ac); /*#ifdef FORMS_ARE_COLORED*/ if (form_type == FORM_TYPE_READONLY) set_form_colors(fed, &text_attr->fg, &text_attr->bg); /*#endif*/ /* Install in javascript form elemement text field translation */ fe_HackFormTextTranslations(fed->widget); XtAddCallback(fed->widget, XmNlosingFocusCallback, fe_lost_focus_cb, fed); XtAddCallback(fed->widget, XmNfocusCallback, fe_got_focus_cb, fed); /* This is changed later for self-submitting fields. */ if (fe_globalData.nonterminal_text_translations) XtOverrideTranslations (fed->widget, fe_globalData. nonterminal_text_translations); if (form_type == FORM_TYPE_PASSWORD) { fe_SetupPasswdText (fed->widget, (max_size > 0 ? max_size : 1024)); fe_MarkPasswdTextAsFormElement(fed->widget); } /* Must be after passwd setup. */ XtVaSetValues (fed->widget, XmNcursorPosition, 0, 0); loc = fe_ConvertToLocaleEncoding (charset, (unsigned char *)text); /* Add verify callbacks to implement JS key events */ fe_HackTextTranslations_addCallbacks(fed->widget); /* * I don't know whether it is SGI or some version of Motif,but * doing a XtVaSetValues() here will execute the * valueChangedCallback but ignore its changes. * XmTextFieldSetString() works as expected. */ XmTextFieldSetString (fed->widget, loc); if (((char *) loc) != text) { XP_FREE (loc); } if (tmp_text) XP_FREE (tmp_text); } static void text_display(FEFormData *fed, LO_FormElementStruct *form) { MWContext *context = fed->context; Widget widget_to_manage; uint16 attrmask = XP_GetFormEleAttrmask(form); LO_FormElementData *form_data = XP_GetFormElementData(form); Position x; Position y; Arg av[10]; int ac = 0; unsigned char *loc = 0; char *current_text = (char*)XP_FormGetCurrentText(form_data); int32 max_size = XP_FormTextGetMaxSize(form_data); int32 form_type = XP_FormGetType(form_data); LO_TextAttr *text_attr = XP_GetFormTextAttr(form); uint16 charset = text_attr->charset; x = (form->x + form->x_offset - CONTEXT_DATA (context)->document_x); y = (form->y + form->y_offset - CONTEXT_DATA (context)->document_y); x += fe_FORM_LEFT_MARGIN; y += fe_FORM_TOP_MARGIN; if (form_type == FORM_TYPE_FILE) widget_to_manage = ((FEFileFormData*)fed)->rowcolumn; else widget_to_manage = fed->widget; if (!XtIsManaged(widget_to_manage)) { Dimension bw = 0; Dimension width = form->width; Dimension height = form->height; XtVaGetValues (widget_to_manage, XmNborderWidth, &bw, 0); /* Since I told layout that width was width+bw+bw (for its sizing and positioning purposes), subtract it out again when setting the size of the text area. X thinks the borders are outside the WxH rectangle, not inside, but thinks that the borders are below and to the right of the X,Y origin, instead of up and to the left as you would expect given the WxH behavior!! */ width -= (bw * 2); height -= (bw * 2); /* x += bw; */ /* y += bw;*/ width -= fe_FORM_LEFT_MARGIN + fe_FORM_RIGHT_MARGIN; height -= fe_FORM_TOP_MARGIN + fe_FORM_BOTTOM_MARGIN; if (max_size > 0) { XtSetArg (av [ac], XmNmaxLength, max_size); ac++; } if (form_type == FORM_TYPE_TEXT) { if (current_text) { loc = fe_ConvertToLocaleEncoding (charset, (unsigned char *)current_text); XtSetArg (av[ac], XmNvalue, loc); ac++; } } if (ac) XtSetValues(widget_to_manage, av, ac); ac = 0; XtSetArg (av [ac], XmNx, x); ac++; XtSetArg (av [ac], XmNy, y); ac++; XtSetArg (av [ac], XmNwidth, width); ac++; XtSetArg (av [ac], XmNheight, height); ac++; XtSetValues (widget_to_manage, av, ac); XtSetMappedWhenManaged(widget_to_manage, !(attrmask & LO_ELE_INVISIBLE)); XtManageChild(widget_to_manage); } else { XtSetMappedWhenManaged(widget_to_manage, !(attrmask & LO_ELE_INVISIBLE)); XtMoveWidget(widget_to_manage, x, y); } } static void text_get_value(FEFormData *fed, LO_FormElementStruct *form, XP_Bool delete_p) { MWContext *context = fed->context; INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(context); LO_FormElementData *form_data = XP_GetFormElementData(form); char *text = 0; int32 form_type; PA_Block new_current; PA_Block cur_current; PA_Block default_txt; form_type = XP_FormGetType(form_data); if (form_type == FORM_TYPE_TEXT) { /* gross, since for password type form elements, we get back a malloc'ed area of memory, and for text form elements, we get XtMalloc'ed. */ char *non_motif_text = NULL; XtVaGetValues (fed->widget, XmNvalue, &text, 0); if (text != NULL) { non_motif_text = XP_STRDUP(text); XtFree(text); } text = non_motif_text; } else if (form_type == FORM_TYPE_PASSWORD) text = fe_GetPasswdText (fed->widget); cur_current = XP_FormGetCurrentText(form_data); default_txt = XP_FormGetDefaultText(form_data); if (cur_current && cur_current != default_txt) free ((char*)cur_current); new_current = (PA_Block)fe_ConvertFromLocaleEncoding(INTL_GetCSIWinCSID(c), (unsigned char *)text); XP_FormSetCurrentText(form_data, new_current); if ((char*)new_current != text) free (text); form_element_get_value(fed, form, delete_p); } static void text_reset(FEFormData *fed, LO_FormElementStruct *form) { LO_FormElementData *form_data = XP_GetFormElementData(form); char *default_text = (char*)XP_FormGetDefaultText(form_data); LO_TextAttr *text_attr = XP_GetFormTextAttr(form); char *tmp_text = 0; unsigned char *loc; int16 charset = text_attr->charset; Widget w = fed->widget; int32 max_size = XP_FormTextGetMaxSize(form_data); int32 form_type; form_type = XP_FormGetType(form_data); if (!default_text) default_text = ""; if (max_size > 0 && (int) XP_STRLEN (default_text) >= max_size) { tmp_text = XP_STRDUP (default_text); tmp_text [max_size] = '\0'; default_text = tmp_text; } fe_forms_clean_text (fed->context, charset, default_text, True); XtVaSetValues (w, XmNcursorPosition, 0, 0); loc = fe_ConvertToLocaleEncoding (charset, (unsigned char*)default_text); /* * I don't know whether it is SGI or some version of Motif, * but doing a XtVaSetValues() here will execute the * valueChangedCallback but ignore its changes. * XmTextFieldSetString() works as expected. */ XmTextFieldSetString (w, loc); if (((char *) loc) != default_text) { XP_FREE (loc); } if (tmp_text) XP_FREE (tmp_text); } static void text_change(FEFormData *fed, LO_FormElementStruct *form) { LO_FormElementData *form_data = XP_GetFormElementData(form); int32 form_type = XP_FormGetType(form_data); char *text = (char*)XP_FormGetCurrentText(form_data); LO_TextAttr *text_attr = XP_GetFormTextAttr(form); unsigned char *loc; int16 charset = text_attr->charset; if (!text) text = (char*)XP_FormGetDefaultText(form_data); loc = fe_ConvertToLocaleEncoding(charset, (unsigned char*)text); if (form_type == FORM_TYPE_TEXTAREA) { FETextAreaFormData *ta_fed = (FETextAreaFormData*)fed; XmTextSetString(ta_fed->text_widget, loc); } else { XmTextFieldSetString(fed->widget, loc); } if (((char *) loc) != text) { XP_FREE (loc); } } static void text_select(FEFormData *fed, LO_FormElementStruct *form) { XmTextSetSelection (fed->widget, 0, XmTextGetMaxLength (fed->widget), CurrentTime); } static void text_focus(FEFormData *fed, LO_FormElementStruct *form) { XmProcessTraversal(fed->widget, XmTRAVERSE_CURRENT); } static void text_lost_focus(FEFormData *fed) { LO_FormElementData *form_data; char *text; XP_Bool text_changed = False; int32 form_type; PA_Block current_text; JSEvent *event; form_data = XP_GetFormElementData(fed->form); form_type = XP_FormGetType(form_data); if (form_type == FORM_TYPE_READONLY) return; current_text = XP_FormGetCurrentText(form_data); if (form_type == FORM_TYPE_PASSWORD) text = fe_GetPasswdText (fed->widget); else XtVaGetValues (fed->widget, XmNvalue, &text, 0); if (!current_text || XP_STRCMP((char*)current_text, text)) text_changed = True; if (((char*) current_text) != text) { XtFree (text); } /* if the text has changed, call get_element_value to copy it into the form element, and send a CHANGE event to the javascript thread. */ if (text_changed) { (*fed->vtbl.get_element_value)(fed, fed->form, FALSE); event = XP_NEW_ZAP(JSEvent); event->type = EVENT_CHANGE; ET_SendEvent (fed->context, (LO_Element *) fed->form, event, NULL, NULL); } } /* ** ** VTABLE for the FILE form element. ** */ static void file_create_widget(FEFormData *fed, LO_FormElementStruct *form) { FEFileFormData *filefed = (FEFileFormData*)fed; MWContext *context = fed->context; Widget parent; int16 charset; LO_FormElementData *form_data; int32 form_type; int32 text_size; int32 max_size; int columns; char *tmp_text = 0; char *text; unsigned char *loc; XmFontList font_list; Arg av [50]; int ac; LO_TextAttr *text_attr; Pixel fg, bg; parent = CONTEXT_DATA (context)->drawing_area; form_data = XP_GetFormElementData(form); form_type = XP_FormGetType(form_data); text_size = XP_FormTextGetSize(form_data); max_size = XP_FormTextGetMaxSize(form_data); text_attr = XP_GetFormTextAttr(form); text = (char*)XP_FormGetDefaultText(form_data); charset = text_attr->charset; /* from bstell: in multibyte (asian) locales, characters tend to be twice as wide as in non-multibyte locales. To make things look correct, we divide the column number in half in those cases. */ columns = ((fe_LocaleCharSetID & MULTIBYTE) ? (text_size + 1) / 2 : text_size); /* the default size for text fields is 20 columns. XX Shouldn't the backend just fill it in with 20? */ if (columns == 0) columns = 20; if (!text) text = ""; if (max_size > 0 && (int) XP_STRLEN (text) >= max_size) { tmp_text = XP_STRDUP (text); tmp_text [max_size] = '\0'; text = tmp_text; } fe_forms_clean_text (context, charset, text, True); fe_FontlistAndXmStringForFormElement(context, form_type, text, text_attr, NULL, &font_list); if (!font_list) return; get_form_colors(fed, &text_attr->fg, &text_attr->bg, &fg, &bg); /* Create a RowColumn widget to handle both the text and its associated browseButton */ ac = 0; XtSetArg (av [ac], XmNpacking, XmPACK_TIGHT); ac++; XtSetArg (av [ac], XmNorientation, XmHORIZONTAL); ac++; XtSetArg (av [ac], XmNnumColumns, 2); ac++; XtSetArg (av [ac], XmNspacing, 5); ac++; XtSetArg (av [ac], XmNmarginWidth, 0); ac++; XtSetArg (av [ac], XmNmarginHeight, 0); ac++; filefed->rowcolumn = XmCreateRowColumn(parent, "formRowCol", av, ac); ac = 0; /* Ok, let's try using the fixed font for text areas only. */ /*#ifdef FORMS_ARE_FONTIFIED*/ XtSetArg (av [ac], XmNfontList, font_list); ac++; /*#endif*/ XtSetArg (av [ac], XmNcolumns, columns); ac++; if (max_size > 0) { XtSetArg (av [ac], XmNmaxLength, max_size); ac++; } #ifdef FORMS_ARE_COLORED XtSetArg (av [ac], XmNforeground, fg); ac++; XtSetArg (av [ac], XmNbackground, bg); ac++; #endif fed->widget = fe_CreateTextField (filefed->rowcolumn, "textForm", av, ac); /* Install in javascript form elemement text field translation */ fe_HackFormTextTranslations(fed->widget); XtAddCallback(fed->widget, XmNlosingFocusCallback, fe_lost_focus_cb, fed); XtAddCallback(fed->widget, XmNfocusCallback, fe_got_focus_cb, fed); XtManageChild(fed->widget); ac = 0; XtSetArg (av [ac], XmNfontList, font_list); ac++; XtSetArg (av [ac], XmNforeground, fg); ac++; XtSetArg (av [ac], XmNbackground, bg); ac++; filefed->browse_button = XmCreatePushButton (filefed->rowcolumn, "formFileBrowseButton", av, ac); XtAddCallback(filefed->browse_button, XmNactivateCallback, fe_form_file_browse_cb, fed); XtManageChild(filefed->browse_button); /* This is changed later for self-submitting fields. */ if (fe_globalData.nonterminal_text_translations) XtOverrideTranslations (fed->widget, fe_globalData. nonterminal_text_translations); XtVaSetValues (fed->widget, XmNcursorPosition, 0, 0); loc = fe_ConvertToLocaleEncoding (charset, (unsigned char *)text); /* Add verify callbacks to implement JS key events */ fe_HackTextTranslations_addCallbacks(fed->widget); /* * I don't know whether it is SGI or some version of Motif * but doing a XtVaSetValues() here will execute the * valueChangedCallback but ignore its changes. * XmTextFieldSetString() works as expected. */ XmTextFieldSetString (fed->widget, loc); if (((char *) loc) != text) { XP_FREE (loc); } if (tmp_text) XP_FREE (tmp_text); } static void file_get_value(FEFormData *fed, LO_FormElementStruct *form, XP_Bool delete_p) { FEFileFormData *filefed = (FEFileFormData*)fed; text_get_value(fed, form, delete_p); /* if delete_p is true, then form_element_get_value will have destroyed fed->widget (our text field.) We need to finish the job here. */ if (delete_p) { XtDestroyWidget(filefed->rowcolumn); filefed->rowcolumn = 0; } } static void file_free(FEFormData *fed, LO_FormElementData *form) { FEFileFormData *filefed = (FEFileFormData*)fed; if (filefed->rowcolumn) XtDestroyWidget(filefed->rowcolumn); filefed->rowcolumn = 0; fed->widget = 0; XP_FREE(fed); } /* ** ** VTABLE for Text like button elements (BUTTON, RESET, SUBMIT). ** */ static void button_create_widget(FEFormData *fed, LO_FormElementStruct *form) { MWContext *context = fed->context; LO_FormElementData *form_data; Widget parent; int32 form_type; unsigned char *string; char *name; LO_TextAttr *text_attr; XmFontList font_list = NULL; XmString xmstring = NULL; Arg av [50]; int ac; text_attr = XP_GetFormTextAttr(form); parent = CONTEXT_DATA (context)->drawing_area; form_data = XP_GetFormElementData(form); form_type = XP_FormGetType(form_data); string = (unsigned char*)XP_FormGetValue(form_data); name = (form_type == FORM_TYPE_SUBMIT ? "formSubmitButton" : (form_type == FORM_TYPE_RESET ? "formResetButton" : "formButton")); fe_FontlistAndXmStringForButton(context, form_type, (char*)string, text_attr, &xmstring, &font_list); #ifdef DEBUG_username if (xmstring == NULL) printf ("xmstring is NULL\n"); #endif ac = 0; if (xmstring) { XtSetArg (av [ac], XmNlabelString, xmstring); ac++; } /*#ifdef FORMS_ARE_FONTIFIED*/ if (font_list) { XtSetArg (av [ac], XmNfontList, font_list); ac++; } /*#endif*/ /* Ok, buttons are always text-colored, because OptionMenu buttons must be, and making buttons be a different color looks inconsistent. */ if (form_type == FORM_TYPE_SUBMIT) { XtSetArg (av [ac], XmNshowAsDefault, 1); ac++; } fed->widget = XmCreatePushButton (parent, name, av, ac); if (xmstring) XmStringFree (xmstring); /*#ifdef FORMS_ARE_COLORED*/ set_form_colors(fed, &text_attr->fg, &text_attr->bg); /*#endif*/ #ifndef NO_NEW_DEFAULT_SIZES /* added so the security people (or anyone needing an HTML area with form elements in it) could specify a wider default width than what motif computes based on the font */ if (form->width > 0 || form->height > 0) { Dimension width; Dimension height; Dimension bw; XtVaGetValues(fed->widget, XmNheight, &height, XmNwidth, &width, XmNborderWidth, &bw, NULL); /* take into account our padding */ width += fe_FORM_LEFT_MARGIN + fe_FORM_RIGHT_MARGIN; height += fe_FORM_TOP_MARGIN + fe_FORM_BOTTOM_MARGIN; width += (bw * 2); height += (bw * 2); if (form->width > width) width = form->width; if (form->height > height) height = form->height; XtVaSetValues(fed->widget, XmNheight, height, XmNwidth, width, NULL); } #endif switch (form_type) { case FORM_TYPE_SUBMIT: XtAddCallback (fed->widget, XmNactivateCallback, fe_submit_form_cb, fed); break; case FORM_TYPE_RESET: XtAddCallback (fed->widget, XmNactivateCallback, fe_reset_form_cb, fed); break; case FORM_TYPE_BUTTON: XtAddCallback (fed->widget, XmNactivateCallback, fe_button_form_cb, fed); break; } if (font_list) XmFontListFree(font_list); } /* ** ** Vtable routines for the radio and checkbox form elements. ** */ static void checkbox_create_widget(FEFormData *fed, LO_FormElementStruct *form) { MWContext *context = fed->context; Widget parent = CONTEXT_DATA (context)->drawing_area; LO_FormElementData *form_data; int32 form_type; XmString xmstring; int margin = 2; /* leave some slack... */ int size; int descent; XmFontList locale_font_list = NULL; Arg av[10]; int ac; LO_TextAttr *text_attr; form_data = XP_GetFormElementData(form); form_type = XP_FormGetType(form_data); text_attr = XP_GetFormTextAttr(form); fe_font_list_metrics (context, form, locale_font_list, &size, &descent); size += (margin * 2); XP_FormSetElementToggled(form_data, XP_FormGetDefaultToggled(form_data)); ac = 0; xmstring = XmStringCreate (" ", XmFONTLIST_DEFAULT_TAG); /* These always use our font list, for proper sizing. */ XtSetArg (av [ac], XmNfontList, locale_font_list); ac++; XtSetArg (av [ac], XmNwidth, size); ac++; XtSetArg (av [ac], XmNheight, size); ac++; XtSetArg (av [ac], XmNresize, False); ac++; XtSetArg (av [ac], XmNspacing, 2); ac++; XtSetArg (av [ac], XmNlabelString, xmstring); ac++; XtSetArg (av [ac], XmNvisibleWhenOff, True); ac++; XtSetArg (av [ac], XmNset, XP_FormGetDefaultToggled(form_data)); ac++; XtSetArg (av [ac], XmNindicatorType, (form_type == FORM_TYPE_RADIO ? XmONE_OF_MANY : XmN_OF_MANY)); ac++; fed->widget = XmCreateToggleButton (parent, "toggleForm", av, ac); /* These must always be the prevailing color of the text, or they look silly. */ #ifdef FORMS_ARE_COLORED set_form_colors(fed, &text_attr->fg, &text_attr->bg); #endif if (form_type == FORM_TYPE_RADIO) XtAddCallback (fed->widget, XmNvalueChangedCallback, fe_radio_form_cb, fed); else XtAddCallback (fed->widget, XmNvalueChangedCallback, fe_check_form_cb, fed); XmStringFree (xmstring); } static void checkbox_get_value(FEFormData *fed, LO_FormElementStruct *form, XP_Bool delete_p) { LO_FormElementData *form_data = XP_GetFormElementData(form); Boolean set = False; XtVaGetValues(fed->widget, XmNset, &set, NULL); XP_FormSetElementToggled(form_data, set); form_element_get_value(fed, form, delete_p); } static void checkbox_change(FEFormData *fed, LO_FormElementStruct *form) { LO_FormElementData *form_data = XP_GetFormElementData(form); int32 form_type = XP_FormGetType(form_data); XP_Bool toggled = XP_FormGetElementToggled(form_data); switch (form_type) { case FORM_TYPE_RADIO: if (toggled) LO_FormRadioSet (fed->context, form); /* SPECIAL: If this was the only radio button in the radio group, LO_FormRadioSet() will turn it back ON again if Mocha tried to turn it OFF. We want to let mocha be able to turn ON/OFF radio buttons. So we are going to override that and let mocha have its way. */ XFE_SetFormElementToggle(fed->context, form, toggled); break; case FORM_TYPE_CHECKBOX: XtVaSetValues (fed->widget, XmNset, toggled, 0); break; } } /* ** ** Vtable entries for FORM_TYPE_SELECT_MULT ** */ static void select_create_widget(FEFormData *fed, LO_FormElementStruct *form) { FESelectMultFormData *sel_fed = (FESelectMultFormData*)fed; MWContext *context = fed->context; Widget parent = CONTEXT_DATA(context)->drawing_area; LO_FormElementData *form_data = XP_GetFormElementData(form); int32 form_type = XP_FormGetType(form_data); LO_TextAttr *text_attr; int nitems = XP_FormSelectGetOptionsCount(form_data); int vlines; XmString *items = NULL; char *selected_p = NULL; int i; Arg av[30]; int ac; XmFontList font_list = NULL; text_attr = XP_GetFormTextAttr(form); vlines = XP_FormSelectGetSize(form_data); if (vlines <= 0) vlines = nitems; if (nitems > 0) { items = (XmString *) malloc (sizeof (XmString) * nitems); selected_p = (char *) calloc (nitems, sizeof (char)); } for (i = 0; i < nitems; i++) { lo_FormElementOptionData *d2 = XP_FormSelectGetOption(form_data, i); selected_p [i] = !!d2->def_selected; d2->selected = d2->def_selected; } fe_FontlistAndXmStringForOptions(context, text_attr, form_data, nitems, items, &font_list); ac = 0; /* #ifdef FORMS_ARE_FONTIFIED */ if(NULL != font_list) { XtSetArg (av [ac], XmNfontList, font_list); ac++; if (form_type == FORM_TYPE_SELECT_ONE) { /* Need to do this untill Ramiro implement fe_ComboBoxSetFontLists */ XtSetArg (av [ac], XmNlistFontList, font_list); ac++; } } /* #endif */ XtSetArg (av [ac], XmNspacing, 0); ac++; if (nitems) { XtSetArg (av [ac], XmNitems, items); ac++; XtSetArg (av [ac], XmNitemCount, nitems); ac++; } if (form_type == FORM_TYPE_SELECT_MULT) { XtSetArg (av [ac], XmNvisibleItemCount, vlines); ac++; XtSetArg (av [ac], XmNlistSizePolicy, XmRESIZE_IF_POSSIBLE); ac++; XtSetArg (av [ac], XmNselectionPolicy, (XP_FormSelectGetMultiple(form_data) ? XmMULTIPLE_SELECT : XmSINGLE_SELECT)); ac++; XtSetArg(av[ac], XmNx, -15000); ac++; } else /* FORM_TYPE_SELECT_ONE */ { Visual *v = 0; Colormap cmap = 0; Cardinal depth = 0; fe_getVisualOfContext (context, &v, &cmap, &depth); XtSetArg (av [ac], XmNshadowThickness, 1); ac++; XtSetArg (av [ac], XmNarrowType, XmMOTIF); ac++; XtSetArg (av [ac], XmNtype, XmDROP_DOWN_LIST_BOX); ac++; XtSetArg (av [ac], XmNvisual, v); ac++; XtSetArg (av [ac], XmNcolormap, cmap); ac++; XtSetArg (av [ac], XmNdepth, depth); ac++; } if (form_type == FORM_TYPE_SELECT_MULT) { sel_fed->list_widget = XmCreateScrolledList (parent, "list", av, ac); fed->widget = XtParent(sel_fed->list_widget); /* * We need this Unmanage because otherwise XtIsManaged on the * parent fails later. It seems XmCreateScrolledList() creates * an unmanaged list with a managed scrolled window parent. * How stupid. */ XtUnmanageChild (fed->widget); XtManageChild (sel_fed->list_widget); } else /* FORM_TYPE_SELECT_ONE */ { fed->widget = DtCreateComboBox(parent, "list", av, ac); XtVaGetValues(fed->widget, XmNlist, &sel_fed->list_widget, NULL); /* we also need to set the fontlist explicitly on the label, for some reason... */ if((NULL != font_list) && (NULL != fed->widget) ) { ac = 0; XtSetArg (av [ac], XmNfontList, font_list); ac++; /* Still need to do this untill Ramiro implement fe_ComboBoxSetFontLists */ if(NULL != ((DtComboBoxWidget)(fed->widget))->combo_box.label) XtSetValues((Widget)((DtComboBoxWidget)(fed->widget))->combo_box.label , av, ac); } } if(NULL != font_list) XmFontListFree(font_list); #ifdef FORMS_ARE_COLORED set_form_colors(fed, &text_attr->fg, &text_attr->bg); #endif #ifndef NO_NEW_DEFAULT_SIZES /* added so the security people (or anyone needing an HTML area with form elements in it) could specify a wider default width (or higher default height) than what motif computes based on the font */ if (form->width > 0) { Dimension width; Dimension height; Dimension bw; XtVaGetValues(sel_fed->list_widget, XmNwidth, &width, XmNheight, &height, XmNborderWidth, &bw, NULL); /* take into account our padding */ width += fe_FORM_LEFT_MARGIN + fe_FORM_RIGHT_MARGIN; height += fe_FORM_TOP_MARGIN + fe_FORM_BOTTOM_MARGIN; width += (bw * 2); height += (bw * 2); if (form->width > width) width = form->width; if (form->height > height) height = form->height; XtVaSetValues(sel_fed->list_widget, XmNwidth, width, XmNheight, height, NULL); } #endif for (i = 0; i < nitems; i++) if (selected_p[i]) if (form_type == FORM_TYPE_SELECT_MULT) XmListSelectPos(sel_fed->list_widget, i+1, False); else /* FORM_TYPE_SELECT_ONE */ DtComboBoxSelectItem(fed->widget, items[i]); if (form_type == FORM_TYPE_SELECT_MULT) { XtAddCallback (sel_fed->list_widget, XmNdefaultActionCallback, fe_list_form_cb, fed); XtAddCallback (sel_fed->list_widget, XmNsingleSelectionCallback, fe_list_form_cb, fed); XtAddCallback (sel_fed->list_widget, XmNmultipleSelectionCallback, fe_list_form_cb, fed); XtAddCallback (sel_fed->list_widget, XmNextendedSelectionCallback, fe_list_form_cb, fed); XtInsertEventHandler(sel_fed->list_widget, KeyPressMask, False, fe_list_find_eh, context, XtListHead); XtAddEventHandler(sel_fed->list_widget, FocusChangeMask, FALSE, (XtEventHandler)fe_mocha_focus_notify_eh, fed); } else { XtAddCallback(fed->widget, XmNselectionCallback, fe_combo_form_cb, sel_fed); } sel_fed->nkids = nitems; sel_fed->selected_p = selected_p; } static void select_get_value(FEFormData *fed, LO_FormElementStruct *form, XP_Bool delete_p) { FESelectMultFormData *sel_fed = (FESelectMultFormData*)fed; LO_FormElementData *form_data = XP_GetFormElementData(form); int i; for (i = 0; i < sel_fed->nkids; i ++) { lo_FormElementOptionData *option_data = XP_FormSelectGetOption(form_data, i); XP_FormOptionSetSelected(option_data, sel_fed->selected_p[i]); } form_element_get_value(fed, form, delete_p); } static void select_free(FEFormData *fed, LO_FormElementData *form_data) { FESelectMultFormData *sel_fed = (FESelectMultFormData*)fed; if (sel_fed->selected_p) free(sel_fed->selected_p); form_element_free(fed, form_data); } static void select_reset(FEFormData *fed, LO_FormElementStruct *form) { FESelectMultFormData *sel_fed = (FESelectMultFormData*)fed; LO_FormElementData *form_data = XP_GetFormElementData(form); int32 form_type = XP_FormGetType(form_data); int nitems = XP_FormSelectGetOptionsCount(form_data); int i; XmString *items; XtVaGetValues(sel_fed->list_widget, XmNitems, &items, NULL); for (i = 0; i < nitems; i++) { lo_FormElementOptionData *option = XP_FormSelectGetOption(form_data, i); XP_Bool selected; XP_FormOptionSetSelected(option, XP_FormOptionGetDefaultSelected(option)); selected = XP_FormOptionGetSelected(option); if (selected) { /* Highlight the item at pos i+1 if it is not already */ if (!sel_fed->selected_p[i]) if (form_type == FORM_TYPE_SELECT_MULT) XmListSelectPos(sel_fed->list_widget, i+1, False); else /* FORM_TYPE_SELECT_ONE */ DtComboBoxSelectItem(fed->widget, items[i]); } else XmListDeselectPos(sel_fed->list_widget, i+1); sel_fed->selected_p [i] = selected; } } static void select_change(FEFormData *fed, LO_FormElementStruct *form) { FESelectMultFormData *sel_fed = (FESelectMultFormData*)fed; LO_FormElementData *form_data = XP_GetFormElementData(form); int32 form_type = XP_FormGetType(form_data); fe_Font fe_font; LO_TextAttr *text_attr; XmString *new_items; char* new_selected_p; int nitems; int i; XmFontList fontlist = NULL; XP_Bool item_selected = FALSE; XmFontType type; XP_Bool use_UnicodePseudoFont; MWContext *context = fed->context; nitems = XP_FormSelectGetOptionsCount(form_data); text_attr = XP_GetFormTextAttr(form); use_UnicodePseudoFont = IS_UNICODE_CSID(text_attr->charset); if(use_UnicodePseudoFont) { Widget parent = CONTEXT_DATA(context)->drawing_area; fe_font = fe_LoadFontForWidgetUnicodePseudoFont(context, parent, text_attr, &fontlist, &type, text_attr->fontmask); if(fontlist) fontlist = XmFontListCopy(fontlist); } else { fe_font = fe_LoadFontFromFace( context, text_attr, &text_attr->charset, text_attr->font_face, text_attr->size, text_attr->fontmask ); FE_NONEDIT_FONTLIST(text_attr->charset, fe_font, fontlist); } if (nitems > 0) { new_items = (XmString *) malloc (sizeof (XmString) * nitems); new_selected_p = (char *) calloc (nitems, sizeof (char)); } #ifdef DEBUG_username printf ("nitems = %d\n", nitems); #endif for (i = 0; i < nitems; i ++) { char *str; lo_FormElementOptionData *option_data = XP_FormSelectGetOption(form_data, i); str = (option_data->text_value ? ((*((char *) option_data->text_value)) ? ((char *) option_data->text_value) : " ") : "---\?\?\?---"); if(use_UnicodePseudoFont) { new_items[i] = fe_ConvertToXmString( str, text_attr->charset, fe_font, type, &fontlist); } else { new_items[i] = FE_NONEDIT_TO_XMSTRING(text_attr->charset, fe_font, str, XP_STRLEN(str) ); } new_selected_p [i] = !!XP_FormOptionGetSelected(option_data); #ifdef DEBUG_username printf ("items[%d] = %s\n", i, str); #endif } XmListDeselectAllItems(sel_fed->list_widget); XtVaSetValues(sel_fed->list_widget, XmNitems, new_items, XmNitemCount, nitems, NULL); for (i = 0; i < nitems; i++) if (new_selected_p[i]) if (form_type == FORM_TYPE_SELECT_MULT) XmListSelectPos(sel_fed->list_widget, i+1, False); else /* FORM_TYPE_SELECT_ONE */ { DtComboBoxSelectItem(fed->widget, new_items[i]); item_selected = TRUE; } if (sel_fed->selected_p) free(sel_fed->selected_p); sel_fed->selected_p = new_selected_p; sel_fed->nkids = nitems; } /* ** ** Vtable entries for FORM_TYPE_TEXTAREA ** */ static void textarea_create_widget(FEFormData *fed, LO_FormElementStruct *form) { FETextAreaFormData *ta_fed = (FETextAreaFormData*)fed; MWContext *context = fed->context; Widget parent; int16 charset; LO_FormElementData *form_data; int32 form_type; int32 rows, cols; char *text; unsigned char *loc; XmFontList locale_font_list; Arg av [50]; int ac; LO_TextAttr *text_attr; parent = CONTEXT_DATA (context)->drawing_area; form_data = XP_GetFormElementData(form); form_type = XP_FormGetType(form_data); text_attr = XP_GetFormTextAttr(form); text = (char*)XP_FormGetDefaultText(form_data); charset = text_attr->charset; XP_FormTextAreaGetDimensions(form_data, &rows, &cols); /* the default size for text fields is 20 columns. XX Shouldn't the backend just fill it in with 20? */ if (cols == 0) cols = 20; if (!text) text = ""; fe_forms_clean_text (context, charset, text, False); loc = fe_ConvertToLocaleEncoding (charset, text); fe_FontlistAndXmStringForFormElement(context, form_type, loc, text_attr, NULL, &locale_font_list); if (!locale_font_list) return; ac = 0; XtSetArg (av[ac], XmNscrollingPolicy, XmAUTOMATIC); ac++; XtSetArg (av[ac], XmNvisualPolicy, XmCONSTANT); ac++; XtSetArg (av[ac], XmNscrollBarDisplayPolicy, XmSTATIC); ac++; XtSetArg (av[ac], XmNvalue, loc); ac++; XtSetArg (av[ac], XmNcursorPosition, 0); ac++; /* from bstell: in multibyte (asian) locales, characters tend to be twice as wide as in non-multibyte locales. To make things look correct, we divide the column number in half in those cases. */ XtSetArg (av[ac], XmNcolumns, ((fe_LocaleCharSetID & MULTIBYTE) ? (cols + 1) / 2 : cols)); ac++; XtSetArg (av[ac], XmNrows, rows); ac++; /* * Gotta love Motif. No matter what wordWrap is set to, if * there is a horizontal scrollbar present it won't wrap. * Of course the documentation mentions this nowhere. * "Use the source Luke!" */ if (XP_FormTextAreaGetAutowrap(form_data) != TEXTAREA_WRAP_OFF) { XtSetArg (av[ac], XmNscrollHorizontal, FALSE); ac++; XtSetArg (av[ac], XmNwordWrap, TRUE); ac++; } XtSetArg (av[ac], XmNeditMode, XmMULTI_LINE_EDIT); ac++; /* Ok, let's try using the fixed font for text areas only. */ /*#ifdef FORMS_ARE_FONTIFIED*/ XtSetArg (av[ac], XmNfontList, locale_font_list); ac++; /*#endif*/ XtSetArg(av[ac], XmNx, -15000); ac++; ta_fed->text_widget = XmCreateScrolledText (parent, "formText", av, ac); ta_fed->form_data.widget = XtParent(ta_fed->text_widget); /* The scroller must have the prevailing color of the text, or it looks funny.*/ #ifdef FORMS_ARE_COLORED set_form_colors(fed, &text_attr->fg, &text_attr->bg); #endif if (((char *) loc) != text) { XP_FREE (loc); } XtAddCallback(ta_fed->text_widget, XmNlosingFocusCallback, fe_lost_focus_cb, fed); XtAddCallback(ta_fed->text_widget, XmNfocusCallback, fe_got_focus_cb, fed); XtAddEventHandler(ta_fed->text_widget, KeyPressMask | KeyReleaseMask, FALSE, /* don't care about nonmaskable events */ fe_key_handler, ta_fed); /* Add verify callbacks to implement JS key events */ fe_HackTextTranslations_addCallbacks(ta_fed->text_widget); XtUnmanageChild (ta_fed->form_data.widget); XtManageChild (ta_fed->text_widget); } static void textarea_display(FEFormData *fed, LO_FormElementStruct *form) { form_element_display(fed, form); } static void textarea_get_value(FEFormData *fed, LO_FormElementStruct *form, XP_Bool delete_p) { MWContext *context = fed->context; LO_FormElementData *form_data = XP_GetFormElementData(form); FETextAreaFormData *ta_fed = (FETextAreaFormData*)fed; INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(context); char *text = 0; int32 cols; PA_Block current_text, new_current_text; PA_Block default_text; XP_FormTextAreaGetDimensions(form_data, NULL, &cols); XtVaGetValues (ta_fed->text_widget, XmNvalue, &text, 0); if (! text) return; if (XP_FormTextAreaGetAutowrap(form_data) == TEXTAREA_WRAP_HARD) { char *tmp = XP_WordWrap(fe_LocaleCharSetID, text, cols, 0); if (text) XtFree(text); if (!tmp) return; text = tmp; } current_text = XP_FormGetCurrentText(form_data); default_text = XP_FormGetDefaultText(form_data); if (current_text && current_text != default_text) free (current_text); new_current_text = (PA_Block)fe_ConvertFromLocaleEncoding (INTL_GetCSIWinCSID(c), (unsigned char *) text); XP_FormSetCurrentText(form_data, new_current_text); if (((char *) new_current_text) != text) { free (text); } form_element_get_value(fed, form, delete_p); } static void textarea_reset(FEFormData *fed, LO_FormElementStruct *form) { LO_FormElementData *form_data = XP_GetFormElementData(form); FETextAreaFormData *ta_fed = (FETextAreaFormData*)fed; char *default_text = (char*)XP_FormGetDefaultText(form_data); LO_TextAttr *text_attr = XP_GetFormTextAttr(form); int16 charset = text_attr->charset; unsigned char *loc; if (!default_text) default_text = ""; fe_forms_clean_text (fed->context, charset, default_text, False); XtVaSetValues (ta_fed->text_widget, XmNcursorPosition, 0, 0); loc = fe_ConvertToLocaleEncoding (charset, (unsigned char*)default_text); XtVaSetValues (ta_fed->text_widget, XmNvalue, loc, 0); if (((char *) loc) != default_text) { XP_FREE (loc); } } static void textarea_lost_focus(FEFormData *fed) { FETextAreaFormData *ta_fed = (FETextAreaFormData*)fed; LO_FormElementData *form_data; char *text; XP_Bool text_changed = False; PA_Block current_text; JSEvent *event; form_data = XP_GetFormElementData(fed->form); current_text = XP_FormGetCurrentText(form_data); XtVaGetValues (ta_fed->text_widget, XmNvalue, &text, 0); if (!current_text || XP_STRCMP((char*)current_text, text)) text_changed = True; if (((char*) current_text) != text) { XtFree (text); } /* if the text has changed, call get_element_value to copy it into the form element, and send a CHANGE event to the javascript thread. */ if (text_changed) { (*fed->vtbl.get_element_value)(fed, fed->form, FALSE); event = XP_NEW_ZAP(JSEvent); event->type = EVENT_CHANGE; ET_SendEvent (fed->context, (LO_Element *) fed->form, event, NULL, NULL); } } static FEFormData * alloc_form_data(int32 form_type) { FEFormData *data; switch (form_type) { case FORM_TYPE_TEXT: case FORM_TYPE_PASSWORD: case FORM_TYPE_READONLY: data = (FEFormData*)XP_NEW_ZAP(FEFormData); data->vtbl = text_form_vtable; return data; case FORM_TYPE_FILE: data = (FEFormData*)XP_NEW_ZAP(FEFileFormData); data->vtbl = file_form_vtable; return data; case FORM_TYPE_SUBMIT: case FORM_TYPE_RESET: case FORM_TYPE_BUTTON: data = (FEFormData*)XP_NEW_ZAP(FEFormData); data->vtbl = button_form_vtable; return data; case FORM_TYPE_RADIO: case FORM_TYPE_CHECKBOX: data = (FEFormData*)XP_NEW_ZAP(FEFormData); data->vtbl = checkbox_form_vtable; return data; case FORM_TYPE_SELECT_ONE: data = (FEFormData*)XP_NEW_ZAP(FESelectOneFormData); data->vtbl = selectone_form_vtable; return data; case FORM_TYPE_TEXTAREA: data = (FEFormData*)XP_NEW_ZAP(FETextAreaFormData); data->vtbl = textarea_form_vtable; return data; case FORM_TYPE_SELECT_MULT: data = (FEFormData*)XP_NEW_ZAP(FESelectMultFormData); data->vtbl = selectmult_form_vtable; return data; case FORM_TYPE_HIDDEN: case FORM_TYPE_JOT: case FORM_TYPE_ISINDEX: case FORM_TYPE_IMAGE: case FORM_TYPE_KEYGEN: case FORM_TYPE_OBJECT: default: XP_ASSERT(0); return NULL; } } /* ** This method can be called more than once for the same front end form element. ** ** In those cases we don't reallocate the FEData*, and also don't recreate the widget, ** but we do set the form and context pointers in FEData, and we call the get_size ** method. ** ** The two cases I know of where layout calls this function more than once are: ** when laying out a table with form elements in it -- layout moves our fe data ** over to the new LO_FormElementStruct, but we have to sync up our pointer to ** the new. ** when reloading a page with frames in it. In this case, the context is different, ** so need to always set that as well. ** ** Also, layout could have told us to delete the widget without freeing our FEData* ** (this happens with delete_p is TRUE in XFE_GetFormElementValue.) In this case, ** we need to recreate the widget. ** ** yuck. */ void XFE_GetFormElementInfo(MWContext *context, LO_FormElementStruct *form) { LO_FormElementData *form_data = XP_GetFormElementData(form); FEFormData *fed; #ifdef DEBUG_username printf ("XFE_GetFormElementInfo\n"); #endif if (!form_data) return; fed = (FEFormData*)XP_FormGetFEData(form_data); if (!fed) { int32 form_type = XP_FormGetType(form_data); fed = alloc_form_data(form_type); XP_ASSERT(fed); if (!fed) return; XP_FormSetFEData(form_data, fed); } fed->form = form; fed->context = context; if (!fed->widget) { XP_ASSERT(fed->vtbl.create_widget_func); (*fed->vtbl.create_widget_func)(fed, form); } XP_ASSERT(fed->vtbl.get_size_func); (*fed->vtbl.get_size_func)(fed, form); } void XFE_DisplayFormElement(MWContext *context, int iLocation, LO_FormElementStruct *form) { LO_FormElementData *form_data = XP_GetFormElementData(form); FEFormData *fed; #ifdef DEBUG_username printf ("XFE_DisplayFormElement\n"); #endif if (!form_data) return; fed = (FEFormData*)XP_FormGetFEData(form_data); XP_ASSERT(fed); if (!fed) return; if (fed->vtbl.display_element_func) (*fed->vtbl.display_element_func)(fed, form); } void XFE_FormTextIsSubmit (MWContext *context, LO_FormElementStruct *form) { LO_FormElementData *form_data = XP_GetFormElementData(form); FEFormData *fed; if (!form_data) return; fed = (FEFormData*)XP_FormGetFEData(form_data); XP_ASSERT(fed); if (!fed) return; if (fed->vtbl.element_is_submit_func) (*fed->vtbl.element_is_submit_func)(fed, form); } void XFE_GetFormElementValue (MWContext *context, LO_FormElementStruct *form, XP_Bool delete_p) { LO_FormElementData *form_data = XP_GetFormElementData(form); FEFormData *fed; #ifdef DEBUG_username printf ("XFE_GetFormElementValue (delete_p = %d)\n", delete_p); #endif if (!form_data) return; fed = (FEFormData*)XP_FormGetFEData(form_data); XP_ASSERT(fed); if (!fed) return; if (fed->vtbl.get_element_value) (*fed->vtbl.get_element_value)(fed, form, delete_p); } void FE_FreeFormElement(MWContext *context, LO_FormElementData *form_data) { FEFormData *fed; #ifdef DEBUG_username printf ("XFE_FreeFormElement. \n"); #endif if (!form_data) return; fed = (FEFormData*)XP_FormGetFEData(form_data); /* this assert gets tripped by HIDDEN form elements, since we have no FE data associated with them. */ /*XP_ASSERT(fed);*/ if (!fed) return; if (fed->vtbl.free_element_func) (*fed->vtbl.free_element_func)(fed, form_data); /* clear out the FE data, so we don't try anything funny from now on. */ XP_FormSetFEData(form_data, NULL); } void FE_FocusInputElement(MWContext *context, LO_Element *element) { if (element) { LO_FormElementData *form_data = XP_GetFormElementData((LO_FormElementStruct*)element); FEFormData* fed; if (!form_data) return; fed = (FEFormData*)XP_FormGetFEData(form_data); if (fed->vtbl.focus_element_func) (*fed->vtbl.focus_element_func)(fed, (LO_FormElementStruct*)element); } else { JSEvent *event; if (context->is_grid_cell) fe_SetGridFocus (context); XmProcessTraversal (CONTEXT_WIDGET(context), XmTRAVERSE_CURRENT); FE_RaiseWindow(context); event = XP_NEW_ZAP(JSEvent); event->type = EVENT_FOCUS; ET_SendEvent (context, element, event, NULL, NULL); } } /* * Force input to be defocused from an element in the given window. * It's ok if the input didn't have input focus. */ void FE_BlurInputElement(MWContext *context, LO_Element *element) { /* Just blow away focus */ TRACEMSG(("XFE_BlurInputElement: element->type = %d\n")); if (!context) return; /* I was told by brendan that this is unnecessary - ramiro */ #if 0 if (form == NULL || form->element_data == NULL) { #ifdef DEBUG_spence printf ("BlurInputElement: form is invalid\n"); #endif return; } #endif fe_NeutralizeFocus (context); if (!element) { FE_LowerWindow(context); fe_MochaBlurNotify(context, element); } } void FE_SelectInputElement(MWContext *context, LO_Element *element) { LO_FormElementStruct *form = (LO_FormElementStruct*)element; LO_FormElementData *form_data = XP_GetFormElementData(form); FEFormData *fed; if (!form_data) return; fed = (FEFormData*)XP_FormGetFEData(form_data); XP_ASSERT(fed); if (!fed) return; if (fed->vtbl.select_element_func) (*fed->vtbl.select_element_func)(fed, form); } void FE_ChangeInputElement(MWContext *context, LO_Element *element) { LO_FormElementStruct *form = (LO_FormElementStruct*)element; LO_FormElementData *form_data = XP_GetFormElementData(form); FEFormData *fed; int32 type; if (!form_data) return; fed = (FEFormData*)XP_FormGetFEData(form_data); type = XP_FormGetType(form_data); /* this assert gets tripped by HIDDEN form elements, since we have no FE data associated with them. */ /*XP_ASSERT(fed);*/ if (!fed) return; if (fed->vtbl.change_element_func) { LO_LockLayout(); (*fed->vtbl.change_element_func)(fed, form); LO_UnlockLayout(); } } /* ** Tell the FE that a form is being submitted without a UI gesture indicating ** that fact, i.e., in a Mocha-automated fashion ("document.myform.submit()"). ** The FE is responsible for emulating whatever happens when the user hits the ** submit button, or auto-submits by typing Enter in a single-field form. */ void FE_SubmitInputElement(MWContext *context, LO_Element *element) { LO_FormSubmitData *submit; URL_Struct *url; History_entry *he = NULL; TRACEMSG(("XFE_SubmitInputElement: element->type = %d\n")); if (!context) return; if (element == NULL) { #ifdef DEBUG_username printf ("SubmitInputElement: form is invalid\n"); #endif return; } submit = LO_SubmitForm (context, (LO_FormElementStruct *) element); if (!submit) return; /* Create the URL to load */ url = NET_CreateURLStruct((char *)submit->action, NET_DONT_RELOAD); /* ... add the form data */ NET_AddLOSubmitDataToURLStruct(submit, url); /* referrer field if there is one */ he = SHIST_GetCurrent (&context->hist); url->referer = fe_GetURLForReferral(he); fe_GetURL (context, url, FALSE); LO_FreeSubmitData (submit); } /* * Emulate a button or HREF anchor click for element. */ void FE_ClickInputElement(MWContext *context, LO_Element *xref) { LO_FormElementStruct *form = (LO_FormElementStruct *) xref; LO_FormElementData *form_data; FEFormData *fed; if (!form) return; form_data = XP_GetFormElementData(form); if (!form_data) return; fed = XP_FormGetFEData(form_data); switch (xref->type) { case LO_FORM_ELE: { XmPushButtonCallbackStruct cb; cb.reason = XmCR_ACTIVATE; cb.event = 0; cb.click_count = 1; XtCallCallbacks (fed->widget, XmNactivateCallback, &cb); } break; case LO_IMAGE: case LO_TEXT: { CL_Event layer_event; fe_EventStruct fe_event; XEvent event; event.type = ButtonPress; event.xbutton.time = XtLastTimestampProcessed (XtDisplay(CONTEXT_WIDGET (context))); /* We only fill in the fields that we need. */ #ifdef LAYERS_FULL_FE_EVENT fe_event.event = &event; #else fe_event_stuff(context,&fe_event,&event,0,0,FE_INVALID_MOUSE_ACTION); layer_event.fe_event_size = sizeof(fe_event); #endif layer_event.fe_event = (void*)&fe_event; layer_event.x = xref->lo_image.x; layer_event.y = xref->lo_image.y; (void) fe_HandleHREF (context, xref, False, False, &layer_event, 0); } break; } } static void fe_form_file_browse_cb (Widget widget, XtPointer closure, XtPointer call_data) { FEFormData *fed = (FEFormData*) closure; char *filename; XmString xm_title = 0; char *title = 0; XtVaGetValues (widget, XmNlabelString, &xm_title, 0); if (xm_title) XmStringGetLtoR (xm_title, XmFONTLIST_DEFAULT_TAG, &title); /* title - XtMalloc*/ XmStringFree(xm_title); filename = fe_ReadFileName( fed->context, title, 0, FALSE, 0); XtFree(title); if (filename) { XP_StatStruct st; char buf [2048]; if (stat(filename, &st) < 0) { /* Error: Cant stat */ PR_snprintf (buf, sizeof (buf), XP_GetString(XFE_ERROR_OPENING_FILE), filename); FE_Alert(fed->context, buf); } else { /* The file was found. Stick the name into the text field. */ XmTextFieldSetString (fed->widget, filename); /* resync up the backend with the current value in the text field. */ XFE_GetFormElementValue(fed->context, fed->form, False); } XP_FREE(filename); } } /* This happens for self submiting forms. */ static void fe_activate_submit_cb (Widget widget, XtPointer closure, XtPointer call_data) { FEFormData *fed = (FEFormData*)closure; /* Move focus out of the current focus widget. This will make any OnChange * calls to mocha. */ fe_NeutralizeFocus(fed->context); fe_submit_form_cb(widget, closure, call_data); } static void fe_mocha_focus_notify_eh (Widget w, XtPointer closure, XEvent *ev, Boolean *cont) { FEFormData *fed = (FEFormData *) closure; MWContext *context = (MWContext *) fed->context; TRACEMSG (("fe_focus_notify_eh\n")); switch (ev->type) { case FocusIn: TRACEMSG (("focus in\n")); fe_MochaFocusNotify(context, (LO_Element*)fed->form); break; case FocusOut: TRACEMSG (("focus out\n")); fe_MochaBlurNotify(context, (LO_Element*)fed->form); break; } } static void fe_mocha_submit_form_cb (MWContext *context, LO_Element *element, int32 event, void *closure, ETEventStatus status) { LO_FormSubmitData *data; URL_Struct *url; if (status != EVENT_OK) return; data = LO_SubmitForm (context, (LO_FormElementStruct *) element); if (! data) return; url = NET_CreateURLStruct ((char *) data->action, FALSE); /* Add the referer to the URL. */ { History_entry *he = SHIST_GetCurrent (&context->hist); if (url->referer) free (url->referer); url->referer = fe_GetURLForReferral(he); } NET_AddLOSubmitDataToURLStruct (data, url); if (data->window_target) { MWContext *tempContext; tempContext = XP_FindNamedContextInList(context, data->window_target); /* Might be null if we're looking at _new. */ if(tempContext) { context = tempContext; data->window_target = NULL; url->window_target = NULL; } } fe_GetURL (context, url, FALSE); LO_FreeSubmitData (data); } static void fe_mocha_submit_click_form_cb(MWContext *context, LO_Element *element, int32 event, void *closure, ETEventStatus status) { FEFormData *fed = (FEFormData *) closure; if (status != EVENT_OK) return; /* Load the element value before telling mocha about the submit. * This needs to be done if we came here not because of the user * clicking on the submit button, like hitting RETURN on a text field. */ XFE_GetFormElementValue(fed->context, fed->form, False); { JSEvent *event = XP_NEW_ZAP(JSEvent); event->type = EVENT_SUBMIT; ET_SendEvent (fed->context, (LO_Element *) fed->form, event, fe_mocha_submit_form_cb, NULL); } } static void fe_submit_form_cb (Widget widget, XtPointer closure, XtPointer call_data) /* * description: * This function was registered as an XmNactivateCallback for * a Motif PushButton widget created in XFE_GetFormElementInfo(). * * preconditions: * widget != 0 && closure != 0 && call_data != 0 * * returns: * not applicable * **********************************************************************/ { FEFormData *fed = (FEFormData *) closure; /*LO_FormSubmitData *data;*/ /*URL_Struct *url;*/ #ifdef DEBUG /* check preconditions in debug mode */ if (widget == 0 || closure == 0 || call_data == 0) { fprintf(stderr, private_precondition_format, __FILE__, __LINE__); fflush(stderr); } #endif { XEvent *x_event; JSEvent *js_event; XP_Bool do_geometry = FALSE; /* * The Motif documentation for XmPushButton informs that call_data * is a pointer to XmPushButtonCallbackStruct, declared in Xm/Xm.h. */ x_event = ((XmPushButtonCallbackStruct*)call_data)->event; js_event = XP_NEW_ZAP(JSEvent); js_event->type = EVENT_CLICK; fe_complete_js_event(js_event, x_event, do_geometry); /* Lord Whorfin says: send me a click, dammit! */ ET_SendEvent (fed->context, (LO_Element *) fed->form, js_event, fe_mocha_submit_click_form_cb, closure); } } static void fe_mocha_button_form_cb (MWContext *context, LO_Element *element, int32 event, void *closure, ETEventStatus status) { if (status != EVENT_OK) return; XFE_GetFormElementValue (context, (LO_FormElementStruct *) element, False); } /********************************************************************** */ static void fe_button_form_cb (Widget widget, /* in: a non-NULL valid widget */ XtPointer closure, /* in: registered by us in XtAddCallback() */ XtPointer call_data) /* in: from Xt */ /* * description: * Called when a form button is clicked. * This function was registered as an XmNactivateCallback for * a Motif PushButton widget created in XFE_GetFormElementInfo(). * * preconditions: * widget != 0 && closure != 0 && call_data != 0 * * returns: * not applicable * **********************************************************************/ { FEFormData *form_data = (FEFormData *) closure; XEvent *x_event; /* X Windows event structure */ JSEvent *js_event; /* Javascript event */ XP_Bool do_geometry = FALSE; #ifdef DEBUG /* check preconditions in debug mode */ if (widget == 0 || closure == 0 || call_data == 0) { fprintf(stderr, private_precondition_format, __FILE__, __LINE__); fflush(stderr); } #endif /* * The Motif documentation for XmPushButton informs that call_data * is a pointer to XmPushButtonCallbackStruct, declared in Xm/Xm.h. */ x_event = ((XmPushButtonCallbackStruct*)call_data)->event; js_event = XP_NEW_ZAP(JSEvent); js_event->type = EVENT_CLICK; fe_complete_js_event(js_event, x_event, do_geometry); ET_SendEvent (form_data->context, (LO_Element *) form_data->form, js_event, fe_mocha_button_form_cb, NULL); } static void fe_mocha_reset_form_cb (MWContext *context, LO_Element *element, int32 event, void *closure, ETEventStatus status) { if (status != EVENT_OK) return; LO_ResetForm (context, (LO_FormElementStruct *) element); } /********************************************************************** */ static void fe_reset_form_cb (Widget widget, XtPointer closure, XtPointer call_data) /* * description: * This function was registered as an XmNactivateCallback for * a Motif PushButton widget created in XFE_GetFormElementInfo(). * * preconditions: * widget != 0 && closure != 0 && call_data != 0 * * returns: * not applicable * **********************************************************************/ { FEFormData *fed = (FEFormData *) closure; #ifdef DEBUG /* check preconditions in debug mode */ if (widget == 0 || closure == 0 || call_data == 0) { fprintf(stderr, private_precondition_format, __FILE__, __LINE__); fflush(stderr); } #endif { XEvent *x_event; JSEvent *js_event; XP_Bool do_geometry = FALSE; /* * The Motif documentation for XmPushButton informs that call_data * is a pointer to XmPushButtonCallbackStruct, declared in Xm/Xm.h. */ x_event = ((XmPushButtonCallbackStruct*)call_data)->event; js_event = XP_NEW_ZAP(JSEvent); js_event->type = EVENT_CLICK; fe_complete_js_event(js_event, x_event, do_geometry); ET_SendEvent (fed->context, (LO_Element *) fed->form, js_event, fe_mocha_reset_form_cb, NULL); } } static void fe_got_focus_cb (Widget widget, XtPointer closure, XtPointer call_data) { FEFormData *fed = (FEFormData *) closure; JSEvent *event; TRACEMSG(("fe_got_focus_c:\n")); event = XP_NEW_ZAP(JSEvent); event->type = EVENT_FOCUS; ET_SendEvent (fed->context, (LO_Element *) fed->form, event, NULL, NULL); } static void fe_lost_focus_cb (Widget widget, XtPointer closure, XtPointer call_data) { FEFormData *fed = (FEFormData *) closure; if (!fed) return; if (fed->vtbl.lost_focus_func) (*fed->vtbl.lost_focus_func)(fed); /* always send a blur event. */ fe_MochaBlurNotify(fed->context, (LO_Element*)fed->form); } static void fe_complete_js_event(JSEvent *js_event, /* inout */ XEvent *x_event, /* in */ XP_Bool do_geometry) /* in */ /* * description: * Updates js_event fields from an X event. * Consolidates common code inside various callback * functions within this module. * * preconditions: * js_event != 0 && x_event != 0 * The js_event must already have an assigned type. * * returns: * not applicable * ****************************************/ { unsigned int state_of_buttons_and_modifiers = 0; #ifdef DEBUG /* check preconditions in debug mode */ if (js_event == 0 || x_event == 0 || js_event->type == 0) { fprintf(stderr, private_precondition_format, __FILE__, __LINE__); fflush(stderr); } #endif state_of_buttons_and_modifiers = x_event->xbutton.state; switch(js_event->type) { case EVENT_CLICK: #ifdef DEBUG_rodt /* check in debug mode */ /* Expect to be processing ButtonRelease */ if (x_event->type != ButtonRelease) { fprintf(stderr, private_check_format, __FILE__, __LINE__); fflush(stderr); } #endif { unsigned int button; button = x_event->xbutton.button; /* * MOUSE BUTTON * * Only verified that left button is 1. 2 should be * middle button and 3 should be right button. */ js_event->which = button; } break; case EVENT_KEYDOWN: case EVENT_KEYUP: case EVENT_KEYPRESS: #ifdef DEBUG /* check in debug mode */ /* Expect to be processing KeyPress or KeyRelease */ if (x_event->type != KeyPress && x_event->type != KeyRelease) { fprintf(stderr, private_check_format, __FILE__, __LINE__); fflush(stderr); } #endif { int buffer_length; char buffer[32]; KeySym keysym; XComposeStatus * compose_status; /* * DETERMINE ASCII VALUE OF KEY PRESSED FOR JAVASCRIPT * * The following code needs to be checked on corner cases. * */ /* * Ignore compose status. * This may need to be changed. */ compose_status = 0; /* * The XEvent structure is converted to a keysym as well as a * string (possibly more than one character) which represents * the keysym. * */ buffer_length = XLookupString(&(x_event->xkey), buffer, 31, &keysym, compose_status); if (buffer_length == 1) { js_event->which = buffer[0]; } else { /* * Force all other cases to return a zero to * expose bugs which indicate that this code needs * to be modified. */ js_event->which = 0; } if (do_geometry) { js_event->x = x_event->xkey.x; js_event->y = x_event->xkey.y; js_event->screenx = x_event->xkey.x_root; js_event->screeny = x_event->xkey.y_root; } } break; } /* * ASSIGN MODIFIERS */ if (state_of_buttons_and_modifiers) { /* * MODIFIER KEYS (ALT, CONTROL, SHIFT, META) * EVENT_xxx_MASK's are from ns/include/libevent.h */ if (state_of_buttons_and_modifiers & ControlMask) js_event->modifiers |= EVENT_CONTROL_MASK; if (state_of_buttons_and_modifiers & ShiftMask) js_event->modifiers |= EVENT_SHIFT_MASK; /* * Only verified meta on Solaris 2.5 dtwm (mwm workalike) */ if (state_of_buttons_and_modifiers & Mod4Mask) js_event->modifiers |= EVENT_META_MASK; } } static void fe_key_handler(Widget widget, /* in */ XtPointer closure, /* in */ XEvent * x_event, /* in */ Boolean * bcontinue) /* out */ /* * description: * Handles XEvents of type KeyPress and KeyRelease in order * to pass these along to JavaScript. JavaScript recognizes * three key events: * EVENT_KEYDOWN corresponds to the X Event KeyPress * EVENT_KEYUP corresponds to the X Event KeyRelease * EVENT_KEYPRESS corresponds to the X Event KeyPress followed by KeyRelease * * Do not confuse EVENT_KEYPRESS with the X Event KeyPress! * * returns: * Not applicable. Note that currently bcontinue is not modified. * * end: ****************************************/ { FEFormData *form_data; JSEvent *js_event = 0; /* Javascript event */ static int32 previous_js_event_type = 0; static int32 previous_js_event_which = 0; #ifdef DEBUG /* check preconditions in debug mode */ if (widget == 0 || closure == 0 || x_event == 0 || bcontinue == 0) { fprintf(stderr, private_precondition_format, __FILE__, __LINE__); fflush(stderr); } #endif form_data = (FEFormData *) closure; if (x_event->type == KeyPress) { js_event = XP_NEW_ZAP(JSEvent); js_event->type = EVENT_KEYDOWN; } else if (x_event->type == KeyRelease) { js_event = XP_NEW_ZAP(JSEvent); js_event->type = EVENT_KEYUP; } /* * Do further work only on KeyPress and KeyRelease */ if (js_event) { XP_Bool do_geometry = TRUE; fe_complete_js_event(js_event, x_event, do_geometry); /* * SYNTHESIZE AND SEND EVENT_KEYPRESS */ if (previous_js_event_type == EVENT_KEYDOWN && js_event->type == EVENT_KEYUP && previous_js_event_which == js_event->which) { JSEvent *synthesized_js_event; synthesized_js_event = XP_NEW_ZAP(JSEvent); XP_MEMCPY(synthesized_js_event, js_event, sizeof(JSEvent)); synthesized_js_event->type = EVENT_KEYPRESS; ET_SendEvent(form_data->context, (LO_Element *) form_data->form, synthesized_js_event, NULL, NULL); } /* * SEND EVENT_KEYDOWN/UP */ previous_js_event_type = js_event->type; previous_js_event_which = js_event->which; ET_SendEvent(form_data->context, (LO_Element *) form_data->form, js_event, NULL, NULL); } } static void fe_mocha_radio_form_cb (MWContext *context, LO_Element *element, int32 event, void *closure, ETEventStatus status) { XmToggleButtonCallbackStruct *cb = (XmToggleButtonCallbackStruct *)closure; LO_FormElementStruct *save; if (status == EVENT_OK) { if (cb->set) save = LO_FormRadioSet (context, (LO_FormElementStruct *) element); /* it's possible for save to be null here. it's a legal return value from LO_FormRadioSet */ if (cb->set && save && save != (LO_FormElementStruct *) element) { XFE_SetFormElementToggle (context, save, TRUE); LO_FormRadioSet (context, save); } XFE_GetFormElementValue (context, (LO_FormElementStruct *) element, False); } XP_FREE (cb); } /********************************************************************** */ static void fe_radio_form_cb (Widget widget, XtPointer closure, XtPointer call_data) /* * description: * This function was registered as an XmNvalueChangedCallback for * a Motif ToggleButton widget created in XFE_GetFormElementInfo(). * * preconditions: * widget != 0 && closure != 0 && call_data != 0 * * returns: * not applicable * **********************************************************************/ { FEFormData *fed = (FEFormData *) closure; XmToggleButtonCallbackStruct *cb = (XmToggleButtonCallbackStruct *)call_data; LO_FormElementStruct *save; #ifdef DEBUG /* check preconditions in debug mode */ if (widget == 0 || closure == 0 || call_data == 0) { fprintf(stderr, private_precondition_format, __FILE__, __LINE__); fflush(stderr); } #endif if (cb->set) save = LO_FormRadioSet (fed->context, fed->form); else /* Don't allow the user to ever toggle a button off - exactly one must be selected at all times. */ XtVaSetValues (widget, XmNset, True, 0); XFE_GetFormElementValue (fed->context, fed->form, False); { XEvent *x_event; JSEvent *js_event; XP_Bool do_geometry = FALSE; XmToggleButtonCallbackStruct *cb_closure; cb_closure = XP_NEW_ZAP (XmToggleButtonCallbackStruct); XP_MEMCPY (cb_closure, cb, sizeof (XmToggleButtonCallbackStruct)); x_event = cb_closure->event; js_event = XP_NEW_ZAP(JSEvent); js_event->type = EVENT_CLICK; fe_complete_js_event(js_event, x_event, do_geometry); ET_SendEvent (fed->context, (LO_Element *) fed->form, js_event, fe_mocha_radio_form_cb, cb_closure); } } static void fe_check_form_cb (Widget widget, XtPointer closure, XtPointer call_data) /* * description: * This function was registered as an XmNvalueChangedCallback for * a Motif ToggleButton widget created in XFE_GetFormElementInfo(). * * preconditions: * widget != 0 && closure != 0 && call_data != 0 * * returns: * not applicable * **********************************************************************/ { FEFormData *form_data = (FEFormData *) closure; XEvent *x_event; /* X Windows event structure */ JSEvent *js_event; XP_Bool do_geometry = FALSE; lo_FormElementToggleData *data; Bool save; #ifdef DEBUG /* check preconditions in debug mode */ if (widget == 0 || closure == 0 || call_data == 0) { fprintf(stderr, private_precondition_format, __FILE__, __LINE__); fflush(stderr); } #endif data = &form_data->form->element_data->ele_toggle; save = data->toggled; XFE_GetFormElementValue (form_data->context, form_data->form, False); /* * The Motif documentation for XmToggleButton informs that call_data * is a pointer to XmToggleButtonCallbackStruct, declared in Xm/Xm.h. */ x_event = ((XmToggleButtonCallbackStruct*)call_data)->event; js_event = XP_NEW_ZAP(JSEvent); js_event->type = EVENT_CLICK; fe_complete_js_event(js_event, x_event, do_geometry); ET_SendEvent (form_data->context, (LO_Element *) form_data->form, js_event, NULL, NULL); } static void fe_combo_form_cb(Widget widget, XtPointer closure, XtPointer call_data) { FESelectOneFormData *sel_fed = (FESelectOneFormData *) closure; DtComboBoxCallbackStruct *cb = (DtComboBoxCallbackStruct *) call_data; LO_FormElementData *form_data; lo_FormElementOptionData *option_data; int i; form_data = XP_GetFormElementData(sel_fed->form_data.form); for (i = 0; i < sel_fed->nkids; i++) { option_data = XP_FormSelectGetOption(form_data, i); /* combo boxes start the item ordering at 0, not 1. */ sel_fed->selected_p [i] = (i == cb->item_position); XP_FormOptionSetSelected(option_data, sel_fed->selected_p[i]); } { JSEvent *event = XP_NEW_ZAP(JSEvent); event->type = EVENT_CHANGE; ET_SendEvent (sel_fed->form_data.context, (LO_Element *) sel_fed->form_data.form, event, NULL, NULL); } } static void fe_list_form_cb (Widget widget, XtPointer closure, XtPointer call_data) { FESelectMultFormData *sel_fed = (FESelectMultFormData *) closure; XmListCallbackStruct *cb = (XmListCallbackStruct *) call_data; LO_FormElementData *form_data; lo_FormElementOptionData *option_data; int i, j; form_data = XP_GetFormElementData(sel_fed->form_data.form); switch (cb->reason) { case XmCR_SINGLE_SELECT: case XmCR_BROWSE_SELECT: for (i = 0; i < sel_fed->nkids; i++) { option_data = XP_FormSelectGetOption(form_data, i); /* Note that the item_position starts at 1, not 0!!!! */ sel_fed->selected_p [i] = (cb->selected_item_count && i == (cb->item_position - 1)); XP_FormOptionSetSelected(option_data, sel_fed->selected_p[i]); } break; case XmCR_MULTIPLE_SELECT: case XmCR_EXTENDED_SELECT: for (i = 0; i < sel_fed->nkids; i++) { /* ** ** toshok -- this is extremely gross and inefficient. must ** revisit it. XXXXX ** */ option_data = XP_FormSelectGetOption(form_data, i); sel_fed->selected_p [i] = 0; XP_FormOptionSetSelected(option_data, sel_fed->selected_p[i]); for (j = 0; j < cb->selected_item_count; j++) if (i == (cb->selected_item_positions [j] - 1)) { sel_fed->selected_p [i] = 1; XP_FormOptionSetSelected(option_data, sel_fed->selected_p[i]); } } break; case XmCR_DEFAULT_ACTION: break; default: assert (0); return; } { JSEvent *event = XP_NEW_ZAP(JSEvent); event->type = EVENT_CHANGE; ET_SendEvent (sel_fed->form_data.context, (LO_Element *) sel_fed->form_data.form, event, NULL, NULL); } } void XFE_ResetFormElement (MWContext *context, LO_FormElementStruct *form) { LO_FormElementData *form_data = XP_GetFormElementData(form); FEFormData *fed = (FEFormData*)XP_FormGetFEData(form_data); XP_ASSERT(fed); if (!fed) return; if (fed->vtbl.reset_element) (*fed->vtbl.reset_element)(fed, form); } /* don't *really* need a vtable entry for this one... or maybe we do. */ void XFE_SetFormElementToggle (MWContext *context, LO_FormElementStruct *form, XP_Bool state) { LO_FormElementData *form_data = XP_GetFormElementData(form); FEFormData *fed; int32 form_type; if (form_data == NULL) { #ifdef DEBUG_username printf ("forms.c:%d; form_data == NULL\n", __LINE__); #endif return; } fed = (FEFormData*)XP_FormGetFEData(form_data); form_type = XP_FormGetType(form_data); XP_ASSERT(form_type == FORM_TYPE_CHECKBOX || form_type == FORM_TYPE_RADIO); if (form_type != FORM_TYPE_CHECKBOX && form_type != FORM_TYPE_RADIO) return; XtVaSetValues (fed->widget, XmNset, state, 0); } void fe_SetFormsGravity (MWContext *context, int gravity) { Widget *kids; Widget area; Cardinal nkids = 0; XSetWindowAttributes attr; unsigned long valuemask; XtVaGetValues (CONTEXT_DATA (context)->drawing_area, XmNchildren, &kids, XmNnumChildren, &nkids, 0); valuemask = CWBitGravity | CWWinGravity; attr.win_gravity = gravity; attr.bit_gravity = gravity; area = CONTEXT_DATA (context)->drawing_area; XChangeWindowAttributes (XtDisplay(area), XtWindow(area), valuemask, &attr); while (nkids--) { if (XtIsManaged (kids[nkids])) XChangeWindowAttributes (XtDisplay(kids[nkids]), XtWindow(kids[nkids]), valuemask, &attr); } } void fe_ScrollForms (MWContext *context, int x_off, int y_off) { Widget *kids; Cardinal nkids = 0; if (x_off == 0 && y_off == 0) return; XtVaGetValues (CONTEXT_DATA (context)->drawing_area, XmNchildren, &kids, XmNnumChildren, &nkids, 0); while (nkids--) XtMoveWidget (kids [nkids], _XfeX(kids [nkids]) + x_off, _XfeY(kids [nkids]) + y_off); } void fe_GravityCorrectForms (MWContext *context, int x_off, int y_off) { Widget *kids; Cardinal nkids = 0; if (x_off == 0 && y_off == 0) return; XtVaGetValues (CONTEXT_DATA (context)->drawing_area, XmNchildren, &kids, XmNnumChildren, &nkids, 0); while (nkids--) { if (XtIsManaged (kids[nkids])) { _XfeX(kids[nkids]) = _XfeX(kids[nkids]) + x_off; _XfeY(kids[nkids]) = _XfeY(kids[nkids]) + y_off; } } }