Back to index

tetex-bin  3.0
statusline.c
Go to the documentation of this file.
00001 /*------------------------------------------------------------
00002   statusline for the xdvi(k) previewer
00003 
00004   written by S. Ulrich (ulrich@cis.uni-muenchen.de)  2000/02/25
00005 
00006   Permission is hereby granted, free of charge, to any person obtaining a copy
00007   of this software and associated documentation files (the "Software"), to
00008   deal in the Software without restriction, including without limitation the
00009   rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
00010   sell copies of the Software, and to permit persons to whom the Software is
00011   furnished to do so, subject to the following conditions:
00012 
00013   The above copyright notice and this permission notice shall be included in
00014   all copies or substantial portions of the Software.
00015 
00016   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
00017   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
00018   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
00019   IN NO EVENT SHALL PAUL VOJTA OR ANY OTHER AUTHOR OF THIS SOFTWARE BE
00020   LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
00021   OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
00022   WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00023   ------------------------------------------------------------*/
00024 
00025 
00026 #include "xdvi-config.h"
00027 #include "xdvi.h"
00028 #include "version.h"
00029 #include "statusline.h"
00030 #include "xm_menu.h"
00031 #include "x_util.h"
00032 #include "pagehist.h"
00033 #include "util.h"
00034 
00035 #include "kpathsea/c-vararg.h"
00036 #include "my-vsnprintf.h"
00037 
00038 #include <ctype.h>
00039 #include <X11/Xatom.h>
00040 #include <X11/StringDefs.h>
00041 
00042 # ifdef MOTIF
00043 #  include <Xm/Label.h>
00044 #  include <Xm/Frame.h>
00045 #  include <Xm/Text.h>
00046 #  include <Xm/TextF.h>
00047 # else
00048 #  include <X11/Xaw/Viewport.h>
00049 #  include <X11/Xaw/Label.h>
00050 # endif
00051 
00052 
00053 Widget statusline;
00054 
00055 static Boolean initialized = False;
00056 
00057 /*
00058  * only print MAX_LEN characters to the statusline
00059  * (it's only 1 line after all)
00060  */
00061 #define MAX_LEN 512
00062 
00063 /* for saving the statusline string if the statusline is
00064  * destroyed and recreated
00065  */
00066 static char g_string_savebuf[MAX_LEN + 2];
00067 
00068 int global_statusline_h = 20;
00069 
00070 #if MOTIF
00071 static void
00072 handle_statusline_event(Widget w, XtPointer client_data,
00073                      XEvent *ev, Boolean *cont)
00074 {
00075 /*      const char *text = (const char *)client_data; */
00076     UNUSED(w);
00077     UNUSED(client_data);
00078     UNUSED(cont);
00079 
00080 /*      fprintf(stderr, "text: |%s|; event: %p\n", text, ev); */
00081     /* only used to do this if page history was already active, but it's probably
00082        nicer to be able to get the history by clicking on the statusline ...
00083     */
00084     if (/* strncmp(text, "Page history:", sizeof "Page history:" - 1) == 0 && */ ev != NULL) {
00085        XmTextPosition pos = XmTextGetCursorPosition(statusline);
00086        char *ptr1, *ptr2;
00087        int diff = 0;
00088 /*     fprintf(stderr, "pos: %d\n", pos); */
00089        if (pos == 0) { /* just display the page history */
00090            page_history_move(0);
00091            return;
00092        }
00093        ptr1 = g_string_savebuf + pos;
00094        ptr2 = strchr(g_string_savebuf, '[');
00095        if (ptr2 == NULL) { /* some other string, also display the page history */
00096            page_history_move(0);
00097            return;
00098        }
00099 /*     fprintf(stderr, "ptr1: |%s|; ptr2: |%s|\n", ptr1, ptr2); */
00100        while (ptr1 < ptr2) {
00101            if (*ptr1 == ' ' && *(ptr1 + 1) != '-') /* separator */
00102               diff--;
00103            ptr1++;
00104        }
00105 
00106        while (ptr1 > ptr2) {
00107            if (*ptr1 == ' ' && *(ptr1 - 1) != '-') /* separator */
00108               diff++;
00109            ptr1--;
00110        }
00111 /*     fprintf(stderr, "diff: %d\n", diff); */
00112        page_history_move(diff);
00113     }
00114 }
00115 #endif /* MOTIF */
00116 
00117 /*
00118  * Create the statusline widget. To be called at the beginning
00119  * of the program, and when expert mode is switched off.
00120  *
00121  *  Side effects:
00122  *     sets <global_statusline_h> to the height of the statusline in pixels.
00123  */
00124 
00125 Widget
00126 create_statusline(
00127 #ifdef MOTIF
00128                 Widget parent
00129 #else
00130                 void
00131 #endif          
00132                 )
00133 {
00134 #ifndef MOTIF
00135     Position vport_h;
00136     Position clip_x;
00137     Position clip_w;
00138     static Position my_h = 0;
00139 #endif
00140 
00141     /*
00142      * FIXME: is there a better way to set the y position depending on
00143      * the height of the widget?
00144      * It doesn't work to change the value of XtNy *after* creating
00145      * the widget!
00146      */
00147 
00148     if (!initialized) {
00149 #ifndef MOTIF
00150        /*
00151         * determine height of statusline (depending on the font used).
00152         * This is not changeable at runtime, so it's determined once and
00153         * for all at program start.
00154         */
00155        statusline = XtVaCreateWidget("statusline", labelWidgetClass, globals.widgets.vport_widget,
00156                                   XtNlabel, (XtArgVal) "test",
00157                                   NULL);
00158        XtVaGetValues(statusline, XtNheight, &my_h, NULL);
00159        global_statusline_h = my_h;
00160        XtDestroyWidget(statusline);
00161 #endif
00162        initialized = True;
00163        /* initialize g_string_savebuf */
00164        sprintf(g_string_savebuf, "This is xdvik %s", XDVI_TERSE_VERSION_INFO);
00165     }
00166 #ifndef MOTIF
00167     /* determine position and width of statusline */
00168     XtVaGetValues(globals.widgets.clip_widget, XtNx, &clip_x, XtNwidth, &clip_w, NULL);
00169     XtVaGetValues(globals.widgets.vport_widget, XtNheight, &vport_h, NULL);
00170     if (vport_h - my_h <= 0) {
00171        XDVI_FATAL((stderr, "Window height too small for statusline (minimum value: %d).", my_h));
00172        return NULL;
00173     }
00174     statusline = XtVaCreateManagedWidget("statusline",
00175                                     labelWidgetClass, globals.widgets.vport_widget,
00176                                     XtNlabel, (XtArgVal) g_string_savebuf,
00177                                     XtNwidth, clip_w,
00178                                     XtNx, clip_x - 1,   /* so that left border becomes invisible */
00179                                     XtNy, vport_h - my_h,
00180                                     XtNjustify, XtJustifyLeft,
00181                                     /* same as for the buttons line */
00182                                     XtNborder, (XtArgVal) resource.fore_Pixel,
00183                                     NULL);
00184 #else
00185     statusline = XtVaCreateManagedWidget("statusline",
00186                                     xmTextFieldWidgetClass, parent,
00187                                     XmNalignment, XmALIGNMENT_END,
00188                                     XmNdepth, (XtArgVal) G_depth,
00189                                     XmNbottomAttachment, XmATTACH_FORM,
00190                                     XmNleftAttachment, XmATTACH_FORM,
00191                                     XmNrightAttachment, XmATTACH_FORM,
00192                                     XmNleftOffset, 1,
00193                                     XmNrightOffset, 1,
00194                                     XmNbottomOffset, 1,
00195                                     XmNtopOffset, 0,
00196                                     XmNcursorPositionVisible, False,
00197                                     XmNautoShowCursorPosition, False,
00198                                     XmNmarginWidth, 4,
00199                                     XmNmarginHeight, 1,
00200                                     XmNeditable, False,
00201                                     XmNtraversalOn, False,
00202                                     XmNvalue, g_string_savebuf,
00203                                     NULL);
00204 
00205     /* Block processing of most interactive events on this widget, except
00206      * for button events that should navigate the page history.
00207      */
00208     XtInsertEventHandler(statusline,
00209                       KeyPressMask | KeyReleaseMask |
00210                       PointerMotionMask| PointerMotionHintMask |
00211                       ButtonMotionMask |
00212 #if !MOTIF
00213                       ButtonPressMask | ButtonReleaseMask |
00214 #endif
00215                       FocusChangeMask,
00216                       /* ButtonPressMask | ButtonReleaseMask | */
00217                       /*                   PointerMotionMask| PointerMotionHintMask | */
00218                       /*                   ButtonMotionMask | */
00219                       True, block_event_callback,
00220                       (XtPointer)0, 0);
00221 #if MOTIF
00222     XtInsertEventHandler(statusline,
00223                       /* suboptimal, but needs to be release not press
00224                        * since we want to query the current cursor position,
00225                        * and that may not be set yet in the press event(?).
00226                        */
00227                       ButtonReleaseMask,
00228                       True, handle_statusline_event,
00229                       (XtPointer)g_string_savebuf, XtListTail);
00230 
00231 #endif /* MOTIF */
00232 
00233 #endif
00234 
00235     return statusline;
00236 }
00237 
00238 
00239 void
00240 toggle_statusline(void)
00241 {
00242 #ifdef MOTIF
00243     if ((resource.expert_mode & XPRT_SHOW_STATUSLINE) == 0)
00244        XtUnmanageChild(statusline);
00245     else
00246        XtManageChild(statusline);
00247 
00248 #if defined(NEW_MENU_CREATION) || defined(MOTIF)
00249     set_menu(&resource.expert_mode, Act_set_expert_mode, check_resource_expert);
00250 #else
00251     set_show_statusline_option();
00252 #endif
00253 #else
00254     static Boolean initialized = False;
00255     static Boolean statusline_mapped = False;
00256 
00257     Boolean make_statusline_visible = False;
00258     Boolean make_statusline_invisible = False;
00259     
00260     if (!initialized) {
00261        statusline_mapped = (resource.expert_mode & XPRT_SHOW_STATUSLINE) != 0;
00262        initialized = True;
00263     }
00264 
00265     if ((resource.expert_mode & XPRT_SHOW_STATUSLINE) == 0) {
00266        if (statusline_mapped)
00267            make_statusline_invisible = True;
00268     }
00269     else {
00270        if (!statusline_mapped)
00271            make_statusline_visible = True;
00272     }
00273 
00274     if (make_statusline_invisible) {
00275        XtDestroyWidget(statusline);
00276        statusline_mapped = False;
00277     }
00278     if (make_statusline_visible) {
00279        static Dimension window_w, window_h;
00280 
00281        static Arg arg_wh[] = {
00282            {XtNwidth, (XtArgVal) &window_w},
00283            {XtNheight, (XtArgVal) &window_h},
00284        };
00285 #ifdef MOTIF
00286        XtGetValues(globals.widgets.main_window, arg_wh, XtNumber(arg_wh));
00287 #else
00288        XtGetValues(globals.widgets.vport_widget, arg_wh, XtNumber(arg_wh));
00289 #endif
00290        XtVaSetValues(globals.widgets.vport_widget, XtNresizable, (XtArgVal)True, NULL);
00291        TRACE_GUI((stderr, "statusline: w %d, h %d", window_w, window_h));
00292        XtVaSetValues(globals.widgets.vport_widget, XtNwidth, (XtArgVal)window_w, XtNheight, (XtArgVal)window_h, NULL);
00293        TRACE_GUI((stderr, "after statusline"));
00294        create_statusline();
00295        statusline_mapped = True;
00296     }
00297 #endif /* MOTIF */
00298 }
00299 
00300 
00301 /*------------------------------------------------------------
00302  *  handle_statusline_resize
00303  *
00304  *  Arguments:
00305  *     void
00306  *
00307  *  Returns:
00308  *     void
00309  *
00310  *  Purpose:
00311  *     Resize the statusline when the total window size changes.
00312  *
00313  *------------------------------------------------------------*/
00314 
00315 void
00316 handle_statusline_resize(void)
00317 {
00318 #ifndef MOTIF
00319     if ((resource.expert_mode & XPRT_SHOW_STATUSLINE) == 0) {
00320        return;
00321     }
00322 
00323     if (!statusline)
00324        return;
00325 
00326     /* apparently the x,y values of a widget can only be set at creation time, so
00327      * the following won't work:
00328      */
00329 
00330 #if 0
00331     /*
00332     BROKEN  Position vport_h, clip_x, clip_w;
00333     BROKEN  static Position my_h = 0;
00334     BROKEN
00335     BROKEN  XtVaGetValues(globals.widgets.clip_widget,
00336     BROKEN                XtNx, &clip_x,
00337     BROKEN                XtNwidth, &clip_w,
00338     BROKEN                NULL);
00339     BROKEN  XtVaGetValues(globals.widgets.vport_widget,
00340     BROKEN                XtNheight, &vport_h,
00341     BROKEN                NULL);
00342     BROKEN
00343     BROKEN  XtUnmanageChild(statusline);
00344     BROKEN  XtVaSetValues(statusline,
00345     BROKEN                             XtNlabel, (XtArgVal) "",
00346     BROKEN                XtNwidth, clip_w,
00347     BROKEN                XtNx, clip_x - 1,
00348     BROKEN                XtNy, vport_h - my_h,
00349     BROKEN                XtNborderWidth, 1,
00350     BROKEN                XtNjustify, XtJustifyLeft,
00351     BROKEN                XtNborder, (XtArgVal) resource.fore_Pixel,
00352     BROKEN                NULL);
00353     BROKEN  XtManageChild(statusline);
00354     BROKEN  XFlush(DISP);
00355     */
00356 #endif
00357 
00358     /* only this will: */
00359     XtDestroyWidget(statusline);
00360     create_statusline();
00361 #endif
00362 }
00363 
00364 
00365 
00366 
00367 /*
00368  * clear statusline by printing an empty message to it.
00369  */
00370 
00371 
00372 static void
00373 clear_statusline(void)
00374 {
00375     if ((resource.expert_mode & XPRT_SHOW_STATUSLINE) != 0) {
00376 # ifdef MOTIF
00377        XmTextFieldSetString(statusline, " ");
00378 # else
00379        XtVaSetValues(statusline, XtNlabel, " ", NULL);
00380 # endif
00381        XFlush(DISP);
00382     }
00383     strcpy(g_string_savebuf, " ");
00384 }
00385 
00386 
00387 /* force a statusline update, no matter how busy the application is.
00388    Use this with care (only for important messages).
00389 */
00390 void
00391 force_statusline_update(void)
00392 {
00393 #ifdef MOTIF
00394     XmUpdateDisplay(globals.widgets.top_level);
00395 #else
00396     XEvent event;
00397     XSync(DISP, False);
00398     while (XCheckMaskEvent(DISP, ExposureMask, &event))
00399         XtDispatchEvent(&event);
00400 #endif /* MOTIF */
00401 }
00402 
00403 
00404 /*
00405  * timeout - if > 0, timeout in seconds after which the message will
00406  *          be deleted again. If < 0, message will remain (until
00407  *          another message overprints it)
00408  * fmt     - message, a C format string
00409  *
00410  * If expert mode is off, print <fmt> to the statusline; else, print
00411  * <fmt> to stdout, unless the `hushstdout' option is specified.
00412  */
00413 static XtIntervalId clear_timeout_id = 0;
00414 
00415 static void
00416 clear_statusline_timer_proc(XtPointer client_data, XtIntervalId *id)
00417 {
00418     UNUSED(client_data);
00419     UNUSED(id);
00420     
00421     if (clear_timeout_id) {
00422        clear_statusline();
00423        clear_timeout_id = 0;
00424     }
00425 }
00426 
00427 static void
00428 internal_print_statusline(statusTimerT timeout, const char *old_content, const char *fmt, va_list argp)
00429 {
00430     if (!XtIsRealized(globals.widgets.top_level) || !initialized || (resource.expert_mode & XPRT_SHOW_STATUSLINE) == 0) {
00431        if (!resource.hush_stdout && strlen(fmt) > 0) { /* check for strlen since sometimes we clear the statusline
00432                                                     by printing "" to it, and we don't want that on stdout */
00433            fprintf(stdout, "xdvi: ");
00434            if (old_content != NULL)
00435               (void)fputs(old_content, stdout);
00436            (void)vfprintf(stdout, fmt, argp);
00437            fputc('\n', stdout);
00438            fflush(stdout);
00439        }
00440     }
00441     else {
00442        char buf[MAX_LEN + 1];
00443        size_t offset = 0;
00444        
00445        if (old_content != NULL && old_content[0] != '\0') {
00446            offset += strlen(old_content);
00447            strncpy(buf, old_content, MAX_LEN);
00448            /* append separating space */
00449            if (strlen(old_content) < MAX_LEN - 1) {
00450               strcat(buf, " ");
00451               offset++;
00452            }
00453        }
00454        VSNPRINTF(buf + offset, MAX_LEN - offset, fmt, argp);   /* just throw away strings longer than MAX_LEN */
00455        buf[MAX_LEN] = '\0'; /* terminate buf */
00456        /*
00457         * save current contents of statusline so that toggling the statusline
00458         * on and off will display the same text again
00459         */
00460        strcpy(g_string_savebuf, buf);
00461 #ifdef MOTIF
00462        XmTextFieldSetString(statusline, buf);
00463 #else
00464        XtVaSetValues(statusline, XtNlabel, buf, NULL);
00465 #endif
00466 /*     fprintf(stderr, "timeout: %d, id: %ld\n", timeout, clear_timeout_id); */
00467        if (timeout > 0) {
00468            timeout *= 1000; /* convert to miliseconds */
00469 
00470            if (clear_timeout_id) {
00471 /*            fprintf(stderr, "clearing!\n"); */
00472               if (globals.debug & DBG_EVENT)
00473                   fprintf(stderr, "%s:%d: removing timeout %ld\n", __FILE__, __LINE__, clear_timeout_id);
00474               XtRemoveTimeOut(clear_timeout_id);
00475            }
00476            clear_timeout_id = XtAppAddTimeOut(app, timeout,
00477                                           clear_statusline_timer_proc, (XtPointer) NULL);
00478        }
00479     }
00480 }
00481 
00482 /*
00483  * Append the varargs-string `fmt' to the currnent contents of the statusline
00484  * erasing it after `timeout' seconds if timeout > 0, unless the current statusline
00485  * contents matches pattern - in that case, overwrite the contents.
00486  */
00487 void
00488 statusline_append(statusTimerT timeout, const char *pattern, const char *fmt, ...)
00489 {
00490     const char *buf = NULL;
00491     va_list argp;
00492 
00493     if (XtIsRealized(globals.widgets.top_level) && initialized && (resource.expert_mode & XPRT_SHOW_STATUSLINE) != 0) {
00494        /* get current statusline contents */
00495 #ifdef MOTIF
00496        XtVaGetValues(statusline, XmNvalue, &buf, NULL);
00497 #else
00498        XtVaGetValues(statusline, XtNlabel, &buf, NULL);
00499 #endif
00500     }
00501 
00502     while (buf != NULL && isspace((int)*buf)) /* skip spaces inserted by statusline appending */
00503        buf++;
00504     va_start(argp, fmt);
00505 
00506     if (buf != NULL && memcmp(buf, pattern, strlen(pattern)) == 0) {
00507        buf = NULL;
00508     }
00509     internal_print_statusline(timeout, buf, fmt, argp);
00510     va_end(argp);
00511 }
00512 
00513 /*
00514  * Print the varargs-string `fmt' to the currnent contents of the statusline,
00515  * erasing it after `timeout' seconds if timeout > 0.
00516  */
00517 
00518 void
00519 statusline_print(statusTimerT timeout, const char *fmt, ...)
00520 {
00521     va_list argp;
00522     va_start(argp, fmt);
00523     internal_print_statusline(timeout, NULL, fmt, argp);
00524     va_end(argp);
00525 }
00526 
00527 void
00528 statusline_clear(void)
00529 {
00530     statusline_print(STATUS_SHORT, "");
00531 }