In application-defined scrolling, the application is responsible for all aspects of the interactions among the scroll, the viewport, and the ScrollBars. The ScrolledWindow remains responsible for geometry and layout, but the application must adjust both the ScrollBars and the scroll position in response to the user's scrolling actions.
Because this model requires more work on the part of the application, it is most suitable for programs in which automatic scrolling is not adequate. For example, an application may contain a text editor or browser that reads only enough of a file to fill the viewport. This application must be informed of the user's scrolling actions so that it can read more of the file when necessary.
The application implements a scheme of its choosing for the relationship between the scroll and the viewport. Following are two common models:
In both models, the application must be notified when the viewport is resized. It may need to adjust the scroll with respect to the viewport, and it must recompute ScrollBar resources to reflect the new relation between the viewport and the scroll. If the viewport is a DrawingArea, the application can use the XmNresizeCallback callbacks for this purpose. Otherwise, the application can establish an event handler for ConfigureNotify events.
The application needs to take the following steps to use application-defined scrolling:
This section contains the scrolling-related portions of an example program that uses a ScrolledWindow with an application-defined scrolling policy. As in the example of automatic scrolling, the ScrolledWindow is a MainWindow, and the scroll widget is a DrawingArea. In this example, the scroll widget also serves as the viewport widget, and the scroll data is maintained in internal data structures.
The application is a simple file browser for C source code. The user selects a filename. The program reads the file and parses it (in the C locale) into an internal table of lines. The application displays in the DrawingArea as many lines as will fit into the current dimensions of the DrawingArea.
The application uses only a vertical ScrollBar, which allows the user to browse through the file. After reading the file, the program sets the ScrollBar's XmNminimum and XmNvalue to 0, its XmNmaximum to the number of lines in the file, and its XmNsliderSize to the lesser of the number of lines in the file and the number of lines that can be displayed in the viewport.
The program establishes a ScrollBar XmNvalueChangedCallback and a DrawingArea XmNexposeCallback that redisplay the lines in the viewport. The redisplay procedure fetches and displays lines from the internal data structure, starting with the line indicated by the ScrollBar's XmNvalue and proceeding to the last line that fits in the viewport. The program also establishes a DrawingArea XmNresizeCallback that recomputes the ScrollBar's XmNsliderSize and XmNvalue based on the number of lines that can be displayed in the viewport. The application does not resize the DrawingArea itself.
This section contains only the portions of the application that relate directly to creating and maintaining the ScrolledWindow. These include:
/*------------------------------------------------------------ ** Internal data structure to hold file info. */ typedef struct { Widget work_area; Widget v_scrb; String file_name; XFontStruct * font_struct; GC draw_gc; char ** lines; int num_lines; } FileData; /*------------------------------------------------------------ ** Create a MainWindow with a MenuBar to load a file. ** Add the vertical scrollbar and the workarea to filedata. */ void CreateApplication ( Widget parent, FileData * filedata) { Widget main_window, menu_bar, menu_pane, cascade, button; Arg args[5]; int n; /* Create app_defined MainWindow. * XmAPPLICATION_DEFINED is the default; however we set it here * to override any values specified by a user in a resource file */ n = 0; XtSetArg (args[n], XmNscrollingPolicy, XmAPPLICATION_DEFINED); n++; main_window = XmCreateMainWindow (parent, "main_window", args, n); XtManageChild (main_window); /* Create MenuBar in MainWindow. */ /* Create "File" PulldownMenu with Open and Quit buttons */ n = 0; menu_pane = XmCreatePulldownMenu (menu_bar, "menu_pane", args, n); n = 0; button = XmCreatePushButton (menu_pane, "Open...", args, n); XtManageChild (button); /* pass the file data to the Open callback */ XtAddCallback (button, XmNactivateCallback, OpenCB, (XtPointer)filedata); n = 0; button = XmCreatePushButton (menu_pane, "Quit", args, n); XtManageChild (button); XtAddCallback (button, XmNactivateCallback, QuitCB, NULL); n = 0; XtSetArg (args[n], XmNsubMenuId, menu_pane); n++; cascade = XmCreateCascadeButton (menu_bar, "File", args, n); XtManageChild (cascade); /* Create "Help" PulldownMenu with Help button. */ /* Create vertical scrollbar only */ n = 0; XtSetArg (args[n], XmNorientation, XmVERTICAL); n++; filedata->v_scrb = XmCreateScrollBar (main_window, "v_scrb", args, n); XtAddCallback (filedata->v_scrb, XmNvalueChangedCallback, ValueCB, (XtPointer)filedata); XtManageChild (filedata->v_scrb); /* Create work_area in MainWindow */ n = 0; filedata->work_area = XmCreateDrawingArea(main_window, "work_area", args, n); XtAddCallback (filedata->work_area, XmNexposeCallback, DrawCB, (XtPointer)filedata); XtAddCallback (filedata->work_area, XmNresizeCallback, DrawCB, (XtPointer)filedata); XtManageChild (filedata->work_area); /* Set MainWindow areas */ XmMainWindowSetAreas (main_window, menu_bar, NULL, NULL, filedata->v_scrb, filedata->work_area); } /*------------------------------------------------------------- ** OpenCB - callback for Open button */ void OpenCB ( Widget w, /* widget id */ XtPointer client_data, /* data from application */ XtPointer call_data) /* data from widget class */ { static Widget fsb_box = NULL; if (!fsb_box) { fsb_box = XmCreateFileSelectionDialog (w, "Load file", NULL, 0); /* just propagate the file information */ XtAddCallback (fsb_box, XmNokCallback, ReadCB, client_data); } XtManageChild (fsb_box); } /*------------------------------------------------------------- ** ReadCB - callback for fsb activate */ void ReadCB ( Widget w, /* widget id */ XtPointer client_data, /* data from application */ XtPointer call_data) /* data from widget class */ { FileData * filedata = (FileData *) client_data; String file_name; Arg args[5]; int n, slider_size; Dimension height; file_name = XmTextGetString( XmFileSelectionBoxGetChild(w, XmDIALOG_TEXT)); if (!BuildLineTable(filedata, file_name)) { WarnUser (w, "Cannot open %s\n", file_name); } else { filedata->file_name = file_name; /* ok, we have a new file, so reset some values */ n = 0; XtSetArg (args[n], XmNheight, &height); n++; XtGetValues (filedata->work_area, args, n); slider_size = (height - 4) / (filedata->font_struct->ascent + filedata->font_struct->descent); if (slider_size <= 0) slider_size = 1; if (slider_size > filedata->num_lines) slider_size = filedata->num_lines; n = 0; XtSetArg (args[n], XmNsliderSize, slider_size); n++; XtSetArg (args[n], XmNmaximum, filedata->num_lines); n++; XtSetArg (args[n], XmNvalue, 0); n++; XtSetValues (filedata->v_scrb, args, n); /* clear and redraw */ XClearWindow(XtDisplay(filedata->work_area), XtWindow(filedata->work_area)); ReDraw (filedata); } } /*------------------------------------------------------------- ** ValueCB - callback for scrollbar */ void ValueCB ( Widget w, /* widget id */ XtPointer client_data, /* data from application */ XtPointer call_data) /* data from widget class */ { FileData * filedata = (FileData *) client_data; /* clear and redraw, dumb dumb.. */ XClearWindow(XtDisplay(filedata->work_area), XtWindow(filedata->work_area)); ReDraw(filedata); } /*------------------------------------------------------------- ** DrawCB - callback for drawing area */ void DrawCB ( Widget w, /* widget id */ XtPointer client_data, /* data from application */ XtPointer call_data) /* data from widget class */ { XmDrawingAreaCallbackStruct * dacs = (XmDrawingAreaCallbackStruct *) call_data; FileData * filedata = (FileData *) client_data; XSetWindowAttributes xswa; static Boolean first_time = True; switch (dacs->reason) { case XmCR_EXPOSE: if (first_time) { /* Change once the bit gravity of the Drawing Area; default is north west and we want forget, so that resize always generates exposure events */ first_time = False; xswa.bit_gravity = ForgetGravity; XChangeWindowAttributes(XtDisplay(w), XtWindow(w), CWBitGravity, &xswa); } ReDraw(filedata); break; case XmCR_RESIZE: ReSize(filedata); break; } } void ReDraw( FileData * filedata) { /* Display as many line as slider_size actually shows, since slider_size is computed relative to the work_area height */ Cardinal i; int value, slider_size; Arg args[5]; int n; Position y; if (filedata->num_lines == 0) return; n = 0; XtSetArg (args[n], XmNvalue, &value); n++; XtSetArg (args[n], XmNsliderSize, &slider_size); n++; XtGetValues (filedata->v_scrb, args, n); for (i = value, y = 2 + filedata->font_struct->ascent; i < value + slider_size; i++, y += (filedata->font_struct->ascent + filedata->font_struct->descent)) { XDrawString(XtDisplay(filedata->work_area), XtWindow(filedata->work_area), filedata->draw_gc, 4, y, filedata->lines[i], strlen(filedata->lines[i])); } } void ReSize( FileData * filedata) { /* Just update the scrollbar internals here, don't bother to redisplay since the gravity is none */ Arg args[5]; int n; int value, slider_size; Dimension height; if (filedata->num_lines == 0) return; n = 0; XtSetArg (args[n], XmNheight, &height); n++; XtGetValues (filedata->work_area, args, n); /* sliderSize is the number of visible lines */ slider_size = (height - 4) / (filedata->font_struct->ascent + filedata->font_struct->descent); if (slider_size <= 0) slider_size = 1; if (slider_size > filedata->num_lines) slider_size = filedata->num_lines; n = 0; XtSetArg (args[n], XmNvalue, &value); n++; XtGetValues (filedata->v_scrb, args, n); /* value shouldn't change that often but there are cases where it matters */ if (value > filedata->num_lines - slider_size) value = filedata->num_lines - slider_size; n = 0; XtSetArg (args[n], XmNsliderSize, slider_size); n++; XtSetArg (args[n], XmNvalue, value); n++; XtSetArg (args[n], XmNmaximum, filedata->num_lines); n++; XtSetValues (filedata->v_scrb, args, n); }