Back to index

tetex-bin  3.0
xm_toolbar.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2001 Marcin Dalecki
00003  * Copyright (c) 2002-2004 the xdvik development team
00004  *
00005  * Permission is hereby granted, free of charge, to any person obtaining a copy
00006  * of this software and associated documentation files (the "Software"), to
00007  * deal in the Software without restriction, including without limitation the
00008  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
00009  * sell copies of the Software, and to permit persons to whom the Software is
00010  * furnished to do so, subject to the following conditions:
00011  *
00012  * The above copyright notice and this permission notice shall be included in
00013  * all copies or substantial portions of the Software.
00014  *
00015  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00016  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00017  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
00018  * PAUL VOJTA OR ANY OTHER AUTHOR OF THIS SOFTWARE BE LIABLE FOR ANY CLAIM,
00019  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
00020  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
00021  * OTHER DEALINGS IN THE SOFTWARE.
00022  *
00023  */
00024 
00025 /*
00026  * Tool bar implementation for the Motif widget set.
00027  */
00028 
00029 #include "xdvi-config.h"
00030 #include "xdvi.h"
00031 
00032 #include "pagesel.h"
00033 #include "help-window.h"
00034 #include "message-window.h"
00035 #include "kpathsea/tex-file.h"
00036 #include "kpathsea/expand.h"
00037 #include "statusline.h"
00038 #include "dvi-init.h"
00039 #include "events.h"
00040 #include "dvi-draw.h"
00041 #include "xm_menu.h"
00042 #include "xm_toolbar.h"
00043 #include "util.h"
00044 #include "x_util.h"
00045 #include "Tip.h"
00046 
00047 #include <stdlib.h>
00048 
00049 #include "xdvi-config.h"
00050 #include "c-openmx.h"
00051 
00052 /* default toolbar pixmap file */
00053 #include "pixmaps/toolbar.xpm"
00054 
00055 #ifdef MOTIF
00056 #include <X11/Intrinsic.h>
00057 #include <X11/StringDefs.h>
00058 #include <X11/Shell.h>
00059 
00060 #include <Xm/Xm.h>
00061 #include <Xm/RowColumn.h>
00062 #include <Xm/Frame.h>
00063 #include <Xm/PushB.h>
00064 #include <Xm/Form.h>
00065 #include <Xm/Separator.h>
00066 #include <Xm/CascadeB.h>
00067 #include <Xm/MainW.h>
00068 #include <Xm/DialogS.h>
00069 #include <Xm/MessageB.h>
00070 #include <Xm/Label.h>
00071 
00072 #if defined(HAVE_X11_XPM_H)
00073 # include <X11/xpm.h>
00074 #elif defined(HAVE_XPM_H)
00075 # include <xpm.h>
00076 #elif defined(HAVE_XM_XPMP_H)
00077 # include <Xm/XpmP.h>
00078 #endif
00079 
00080 #include <sys/stat.h>
00081 
00082 /*
00083  * The following needs to be keept in sync with the actual pixmap sizes in
00084  * tools.xpm. When editing the pixmaps, take care that the symbolic names
00085  * are not messed up (e.g. it works with "pixmap" from ftp.x11.org, but
00086  * not with xv).
00087  */
00088 
00089 #define PIXMAP_WIDTH 18
00090 #define PIXMAP_HEIGHT       18
00091 /*  #define PIXMAP_COUNT    13 */
00092 
00093 
00094 static Widget tool_bar_frame;
00095 
00096 #define SEP_CHAR ':' /* character separating entries in translations lines */
00097 
00098 void
00099 toggle_scrollbars(void)
00100 {
00101     Widget x_bar = XtNameToWidget(globals.widgets.main_window, "HorScrollBar");
00102     Widget y_bar = XtNameToWidget(globals.widgets.main_window, "VertScrollBar");
00103     
00104     if ((resource.expert_mode & XPRT_SHOW_SCROLLBARS) == 0) {
00105        XtUnmanageChild(x_bar);
00106        XtUnmanageChild(y_bar);
00107     }
00108     else {
00109        XtManageChild(x_bar);
00110        XtManageChild(y_bar);
00111     }
00112 
00113     set_menu(&resource.expert_mode, Act_set_expert_mode, check_resource_expert);
00114 }
00115 
00116 void
00117 toggle_toolbar(void)
00118 {
00119 #if !HAVE_XPM
00120     statusline_print(STATUS_LONG,
00121                    "Compiled without XPM support; no toolbar available.");
00122 #else
00123     if (resource.toolbar_unusable) {
00124        statusline_print(STATUS_LONG,
00125                       "Toolbar pixmap file not found; toolbar is disabled.");
00126        return;
00127     }
00128 
00129     if ((resource.expert_mode & XPRT_SHOW_TOOLBAR) == 0)
00130        XtUnmanageChild(tool_bar_frame);
00131     else
00132        XtManageChild(tool_bar_frame);
00133 
00134     set_menu(&resource.expert_mode, Act_set_expert_mode, check_resource_expert);
00135 
00136 #endif /* not HAVE_XPM */
00137 }
00138 
00139 typedef enum { TB_BUTTON, TB_SEPARATOR } toolbarButtonT;
00140 
00141 /* global array of button infos */
00142 static struct toolbar_button_info {
00143     Widget button;
00144     toolbarButtonT type;
00145     char *tip;
00146 } *toolbar_buttons = NULL;
00147 
00148 #if HAVE_XPM  /* remainder of file: toolbar is disabled without XPM support */
00149 
00150 /* to save current values to */
00151 static Pixel m_background = 0, m_top_shadow_color = 0, m_bottom_shadow_color = 0;
00152 
00153 /* to save resource value of XmNshadowThickness before overriding it if
00154    toolbar_buttons_raised is false; it will be used to raise the button on enter:
00155 */
00156 static int m_shadow_thickness; 
00157 extern Boolean get_int_arg(String *param, Cardinal *num_params, int *res);
00158 
00159 /* indexes for named colors */
00160 enum {
00161     BACKGROUND,
00162     FOREGROUND,
00163     BOTTOM_SHADOW,
00164     TOP_SHADOW,
00165     HIGHLIGHT
00166 };
00167 
00168 /* save info about buttons that need to change state */
00169 static struct state_buttons_info {
00170     Widget back_button;                   /* insensitive when current page is last page */
00171     Widget zoom_in_button;         /* insensitive when shrinkfactor is 1 */
00172     Widget forward_button;         /* insensitive when current page is first page */
00173     Widget hyperref_back_button;   /* insensitive when at end of href history */
00174     Widget hyperref_forward_button;       /* insensitive when at begin of href history */
00175 } m_button_info;
00176 
00177 static XtCallbackRec command_call[] = {
00178     { handle_command, NULL },
00179     { NULL, NULL },
00180 };
00181 
00182 static void
00183 set_button_sensitivity(Widget w, Boolean sensitive)
00184 {
00185     if (w == NULL) { /* if button hadn't been initialized properly */
00186        return;
00187     }
00188     XtVaSetValues(w, XmNsensitive, sensitive, NULL);
00189     /* also remove `raised' property on mouse over */
00190     if (!sensitive && !resource.toolbar_buttons_raised) {
00191        Dimension x, y, wd, ht;
00192        Pixel foreground;
00193        static GC backgroundGC = NULL;
00194        if (!XtIsRealized(w))
00195            return;
00196        XtVaGetValues(w,
00197                     XmNx, &x,
00198                     XmNy, &y,
00199                     XmNwidth, &wd,
00200                     XmNheight, &ht,
00201                     NULL);
00202        if (backgroundGC == NULL) {
00203            XtVaGetValues(w, XmNforeground, &foreground, NULL);
00204            backgroundGC = set_or_make_gc(NULL, GXcopy, m_background, foreground);
00205        }
00206        XtVaSetValues(w,
00207                     XmNtopShadowColor, m_background,
00208                     XmNbottomShadowColor, m_background,
00209                     NULL);
00210 #if 0
00211        fprintf(stderr, "drawing on %d,%d\n", x, y);
00212        XFillRectangle(XtDisplay(w), XtWindow(w), globals.gc.high, /* backgroundGC, */
00213                      x, y, wd, ht);
00214 #endif
00215     }
00216     XSync(DISP, False);
00217 }
00218 
00219 void
00220 tb_check_navigation_sensitivity(int pageno)
00221 {
00222     if (!XtIsRealized(globals.widgets.top_level) || (resource.expert_mode & XPRT_SHOW_TOOLBAR) == 0)
00223        return;
00224 
00225     set_button_sensitivity(m_button_info.forward_button, pageno == total_pages - 1 ? False : True);
00226     set_button_sensitivity(m_button_info.back_button, pageno == 0 ? False : True);
00227 }
00228 
00229 void
00230 tb_set_htex_back_sensitivity(Boolean sensitive)
00231 {
00232     if (!XtIsRealized(globals.widgets.top_level) || (resource.expert_mode & XPRT_SHOW_TOOLBAR) == 0)
00233        return;
00234     set_button_sensitivity(m_button_info.hyperref_back_button, sensitive);
00235 }
00236 
00237 void
00238 tb_set_htex_forward_sensitivity(Boolean sensitive)
00239 {
00240     if (!XtIsRealized(globals.widgets.top_level) || (resource.expert_mode & XPRT_SHOW_TOOLBAR) == 0)
00241        return;
00242     
00243     set_button_sensitivity(m_button_info.hyperref_forward_button, sensitive);
00244 }
00245 
00246 void
00247 tb_set_pagehist_back_sensitivity(Boolean sensitive)
00248 {
00249     if (!XtIsRealized(globals.widgets.top_level) || (resource.expert_mode & XPRT_SHOW_TOOLBAR) == 0)
00250        return;
00251     set_button_sensitivity(m_button_info.hyperref_back_button, sensitive);
00252 }
00253 
00254 void
00255 tb_set_pagehist_forward_sensitivity(Boolean sensitive)
00256 {
00257     if (!XtIsRealized(globals.widgets.top_level) || (resource.expert_mode & XPRT_SHOW_TOOLBAR) == 0)
00258        return;
00259     
00260     set_button_sensitivity(m_button_info.hyperref_forward_button, sensitive);
00261 }
00262 
00263 void
00264 tb_set_zoom_sensitivity(Boolean sensitive)
00265 {
00266     if (!XtIsRealized(globals.widgets.top_level) || (resource.expert_mode & XPRT_SHOW_TOOLBAR) == 0)
00267        return;
00268     
00269     set_button_sensitivity(m_button_info.zoom_in_button, sensitive);
00270 }
00271 
00272 #if 0
00273 static void
00274 search_callback(Widget w,
00275               XtPointer client_data,
00276               XmAnyCallbackStruct *call_data)
00277 {
00278     UNUSED(w);
00279     UNUSED(client_data);
00280     UNUSED(call_data);
00281     
00282     popup_message(globals.widgets.top_level,
00283                 MSG_ERR, NULL, "Sorry, not yet implemented");
00284 }
00285 #endif /* 0 */
00286 
00287 
00288 
00289 /*
00290  * If successful, this extracts a square pixmap from resource.toolbar_pixmap_file
00291  * and returns True. 
00292  * In this case, the pixmap `sen' will contain the actual
00293  * pixmap and `insen' will contain a drawn pixmap for the insensitive state of
00294  * the button. We are emulating a shaded state derived from the monochrome 'm'
00295  * color attributes of the XPM.
00296  *
00297  * If unsuccessful, it returns False, and the Pixmap pointers are undefined.
00298  */
00299 
00300 static Boolean
00301 create_pixmap(Widget parent, int iconidx, Pixmap *sen, Pixmap *insen)
00302 {
00303     static Boolean first_time = True;
00304     static Window rootWindow;
00305     static XpmColorSymbol color[5] = {
00306        {"none", "none", 0},
00307        {"iconColor1", NULL, 0},
00308        {"bottomShadowColor", NULL, 0},
00309        {"topShadowColor", NULL, 0},
00310        {"selectColor", NULL, 0}
00311     };
00312     static int screenNum;
00313     static Pixmap tools_map;
00314     static Pixmap tools_mask;
00315     static Pixmap shade_map;
00316     static Pixmap shade_mask;
00317     
00318     int                  status = 0;
00319     XpmAttributes   attr;
00320     Pixmap        map;
00321     Pixmap        mask;
00322     static String pixmap_file_path = NULL; /* note: never free()d */
00323        
00324     ASSERT(iconidx >= 0, "index must be positive");
00325     
00326     if (first_time) {
00327        /* FIXME: We use a dummy window here to get the correct depth/visual for the
00328           pixmap creation (cant use globals.widgets.top_level since it's not realized
00329           yet and realizing it now would result in wrong dimensions) ... */
00330        Widget dummy = XtVaAppCreateShell("xdvi", "Xdvi",
00331                                      transientShellWidgetClass, DISP,
00332                                      XtNdepth, G_depth,
00333                                      XtNvisual, G_visual,
00334                                      XtNcolormap, G_colormap,
00335                                      XtNmappedWhenManaged, False,
00336                                      NULL);
00337        XtRealizeWidget(dummy); /* note: doesn't pop it up */
00338        rootWindow = XtWindow(dummy);
00339        screenNum = DefaultScreen(XtDisplay(globals.widgets.top_level));
00340        ASSERT(rootWindow != 0, "");
00341        XtVaGetValues(parent,
00342                     XmNbackground, &color[BACKGROUND].pixel,
00343                     XmNforeground, &color[FOREGROUND].pixel,
00344                     XmNbottomShadowColor, &color[BOTTOM_SHADOW].pixel,
00345                     XmNtopShadowColor, &color[TOP_SHADOW].pixel,
00346                     XmNhighlightColor, &color[HIGHLIGHT].pixel,
00347                     NULL);
00348        /* try to locate the XPM file with the toolbar pixmaps */
00349        pixmap_file_path = XtResolvePathname(DISP,
00350                                         "pixmaps",
00351                                         resource.toolbar_pixmap_file,
00352                                         (String)NULL,          /* suffix */
00353                                         (String)NULL,          /* use default path */
00354                                         (Substitution)NULL,    /* substitutions */
00355                                         0,                            /* number of substitutions */
00356                                         (XtFilePredicate)NULL);       /* return True iff file exists */
00357 
00358        TRACE_GUI((stderr, "pixmap file search via XtResolvePathname: %s => %s",
00359                  resource.toolbar_pixmap_file, pixmap_file_path ? (char*)pixmap_file_path : "<NULL>"));
00360        if (pixmap_file_path == NULL) {
00361            pixmap_file_path = (String)kpse_find_file(resource.toolbar_pixmap_file,
00362                                                 kpse_program_text_format,
00363                                                 0);
00364            TRACE_GUI((stderr,
00365                      "pixmap file search via kpse_find_file: %s => %s",
00366                      resource.toolbar_pixmap_file,
00367                      pixmap_file_path ? (char*)pixmap_file_path : "<NULL>"));
00368            if (pixmap_file_path == NULL) {
00369               TRACE_GUI((stderr, "No installed toolbar pixmap found, using built-in pixmap."));
00370            }
00371        }
00372     }
00373     
00374     /* Setup the color subsititution table */
00375     attr.valuemask = XpmColorSymbols | XpmCloseness | XpmColormap | XpmDepth | XpmVisual;
00376     attr.closeness = 65535; /* accuracy isn't crucial */
00377     attr.colorsymbols = color;
00378     attr.numsymbols = XtNumber(color);
00379     attr.visual = G_visual;
00380     attr.colormap = G_colormap;
00381     attr.depth = G_depth;
00382     
00383     /* Create the "sensitive" pixmap */
00384     if (!tools_map) {
00385        if (pixmap_file_path != NULL) {
00386            status = XpmReadFileToPixmap(XtDisplay(globals.widgets.top_level), rootWindow,
00387                                     pixmap_file_path, &tools_map, &tools_mask, &attr);
00388        }
00389        else {
00390            status = XpmCreatePixmapFromData(XtDisplay(globals.widgets.top_level), rootWindow,
00391                                         (char **)toolbar_xpm, &tools_map, &tools_mask, &attr);
00392        }
00393     }
00394     else
00395        status = XpmSuccess;
00396 
00397     map = tools_map;
00398     mask = tools_mask;
00399 
00400     if (status == XpmSuccess) {
00401        static Pixmap tmp_mask;
00402        static GC gc;
00403 
00404        if (first_time) {
00405            tmp_mask = XCreatePixmap(XtDisplay(globals.widgets.top_level), rootWindow, PIXMAP_WIDTH, PIXMAP_HEIGHT, 1);
00406            gc = XCreateGC(XtDisplay(globals.widgets.top_level), tmp_mask, 0, NULL);
00407        }
00408        XCopyArea(XtDisplay(globals.widgets.top_level),
00409                 mask, tmp_mask, gc, iconidx * PIXMAP_WIDTH, 0, PIXMAP_WIDTH, PIXMAP_HEIGHT, 0, 0);
00410 
00411        mask = tmp_mask;
00412     }
00413     else { /* something went wrong */
00414        popup_message(globals.widgets.top_level,
00415                     MSG_ERR,
00416                     "Something's wrong with your XPM file - "
00417                     "try to load it into an image editor and fix the problem.",
00418                     "Xpm error: %s - switching toolbar off.",
00419                     XpmGetErrorString(status));
00420        sen = insen = NULL;
00421        resource.expert_mode ^= XPRT_SHOW_TOOLBAR;
00422        return False;
00423     }
00424 
00425     XpmFreeAttributes(&attr);
00426     
00427     if (map != 0) {
00428        static GC back_gc, bots_gc;
00429 
00430        if (first_time) {
00431            XGCValues   gcvalues;
00432 
00433            gcvalues.foreground = color[BACKGROUND].pixel;
00434            back_gc = XCreateGC(XtDisplay(globals.widgets.top_level), rootWindow, GCForeground, &gcvalues);
00435 
00436            gcvalues.foreground = color[BOTTOM_SHADOW].pixel;
00437            bots_gc = XCreateGC(XtDisplay(globals.widgets.top_level), rootWindow, GCForeground, &gcvalues);
00438        }
00439 
00440        /* Need to create new Pixmaps with the mask applied. */
00441        XSetClipMask(XtDisplay(globals.widgets.top_level), bots_gc, mask);
00442 
00443        /* Create the "sensitive" pixmap. */
00444        *sen = XCreatePixmap(XtDisplay(globals.widgets.top_level), rootWindow, PIXMAP_WIDTH, PIXMAP_HEIGHT,
00445                           G_depth);
00446        XFillRectangle(XtDisplay(globals.widgets.top_level), *sen, back_gc, 0, 0, PIXMAP_WIDTH, PIXMAP_HEIGHT);
00447        if (iconidx != -1)
00448            XCopyArea(XtDisplay(globals.widgets.top_level), map, *sen, bots_gc,
00449                     iconidx * PIXMAP_WIDTH, 0, PIXMAP_WIDTH, PIXMAP_HEIGHT, 0, 0);
00450        else
00451            XCopyArea(XtDisplay(globals.widgets.top_level), map, *sen, bots_gc,
00452                     0, 0, PIXMAP_WIDTH, PIXMAP_HEIGHT, 0, 0);
00453 
00454        if (iconidx == -1)
00455            XFreePixmap(XtDisplay(globals.widgets.top_level), map);
00456 
00457        /* Create the "insensitive" pixmap. */
00458        if (insen != NULL) {
00459            Pixmap map;
00460            Pixmap mask;
00461 
00462            attr.valuemask = XpmColorSymbols | XpmCloseness | XpmColorKey | XpmColormap | XpmDepth | XpmVisual;
00463            attr.closeness = 65535; /* accuracy isn't crucial */
00464            attr.colorsymbols = color;
00465            attr.numsymbols = XtNumber(color);
00466            attr.color_key = XPM_MONO;
00467            attr.visual = G_visual;
00468            attr.colormap = G_colormap;
00469            attr.depth = G_depth;
00470 
00471 
00472            if (!shade_map) {
00473               if (pixmap_file_path != NULL) {
00474                   status = XpmReadFileToPixmap(XtDisplay(globals.widgets.top_level), rootWindow,
00475                                            pixmap_file_path, &shade_map, &shade_mask, &attr);
00476               }
00477               else {
00478                   status = XpmCreatePixmapFromData(XtDisplay(globals.widgets.top_level), rootWindow,
00479                                                (char **)toolbar_xpm, &shade_map, &shade_mask, &attr);
00480               }
00481            }
00482            else
00483               status = XpmSuccess;
00484 
00485            map = shade_map;
00486            mask = shade_mask;
00487 
00488            if (status == XpmSuccess) {
00489               static Pixmap tmp_mask;
00490               static GC gc;
00491 
00492               if (first_time) {
00493                   tmp_mask = XCreatePixmap(XtDisplay(globals.widgets.top_level), rootWindow,
00494                                         PIXMAP_WIDTH, PIXMAP_HEIGHT, 1);
00495                   gc = XCreateGC(XtDisplay(globals.widgets.top_level), tmp_mask, 0, NULL);
00496               }
00497 
00498               XCopyArea(XtDisplay(globals.widgets.top_level), mask, tmp_mask, gc,
00499                        iconidx * PIXMAP_WIDTH, 0,
00500                        PIXMAP_WIDTH, PIXMAP_HEIGHT,
00501                        0, 0);
00502 
00503               mask = tmp_mask;
00504            }
00505            else { /* something went wrong */
00506               popup_message(globals.widgets.top_level,
00507                            MSG_ERR,
00508                            "Something's wrong with your XPM file - "
00509                            "try to load it into an image editor and fix the problem.",
00510                            "Xpm error: %s - switching toolbar off.",
00511                            XpmGetErrorString(status));
00512               sen = insen = NULL;
00513               resource.expert_mode ^= XPRT_SHOW_TOOLBAR;
00514               return False;
00515            }
00516 
00517            if (mask != 0) {
00518               static GC   tops_gc;
00519 
00520               if (first_time) {
00521                   XGCValues   gcvalues;
00522 
00523                   gcvalues.foreground = color[TOP_SHADOW].pixel;
00524                   tops_gc = XCreateGC(XtDisplay(globals.widgets.top_level), rootWindow, GCForeground, &gcvalues);
00525               }
00526 
00527               /* Need to create new Pixmaps with the mask applied. */
00528               XSetClipMask(XtDisplay(globals.widgets.top_level), bots_gc, mask);
00529               XSetClipMask(XtDisplay(globals.widgets.top_level), tops_gc, mask);
00530               XSetClipOrigin(XtDisplay(globals.widgets.top_level), tops_gc, 1, 1);
00531 
00532               *insen = XCreatePixmap(XtDisplay(globals.widgets.top_level), rootWindow, PIXMAP_WIDTH, PIXMAP_HEIGHT,
00533                                    G_depth);
00534 
00535               XFillRectangle(XtDisplay(globals.widgets.top_level), *insen, back_gc, 0, 0,
00536                             PIXMAP_WIDTH, PIXMAP_HEIGHT);
00537               XFillRectangle(XtDisplay(globals.widgets.top_level), *insen, tops_gc, 1, 1,
00538                             PIXMAP_WIDTH - 1, PIXMAP_HEIGHT - 1);
00539               XFillRectangle(XtDisplay(globals.widgets.top_level), *insen, bots_gc, 0, 0, PIXMAP_WIDTH, PIXMAP_HEIGHT);
00540 
00541               if (iconidx == -1)
00542                   XFreePixmap(XtDisplay(globals.widgets.top_level), map);
00543            }
00544 
00545            XpmFreeAttributes(&attr);
00546        }
00547     }
00548     
00549     first_time = False;
00550     return True;
00551 }
00552 
00553 /*
00554  * If successful, this returns True, and sets *button to a button with
00555  * a square pixmap on it (which is cut out form `index' position in
00556  * resource.toolbar_pixmap_file).  If unsuccessful, it returns false,
00557  * and *button is undefined.
00558  */
00559 static Boolean
00560 create_toolbar_button(Widget parent, Widget *button,
00561                     const Pixmap *image_sens, const Pixmap *image_insens)
00562 {
00563     Boolean sensitive = True; /* dummy */
00564     *button = XtVaCreateManagedWidget("button", xmPushButtonWidgetClass, parent,
00565                                   XmNhighlightOnEnter, True,
00566                                   XmNlabelPixmap, *image_sens,
00567                                   XmNlabelInsensitivePixmap, *image_insens,
00568                                   XmNsensitive, sensitive,
00569                                   XmNlabelType, XmPIXMAP,
00570                                   NULL);
00571     XtVaGetValues(*button, XmNshadowThickness, &m_shadow_thickness, NULL);
00572     if (!resource.toolbar_buttons_raised) {
00573        if (m_background == 0) { /* initialize values */
00574            XtVaGetValues(*button,
00575                        /* should we rather get background of parent widget? */
00576                        XmNbackground, &m_background,
00577                        XmNtopShadowColor, &m_top_shadow_color,
00578                        XmNbottomShadowColor, &m_bottom_shadow_color,
00579                        NULL);
00580        }
00581        /* remove shadows, setting them later when mouse enters button */
00582        XtVaSetValues(*button,
00583                     XmNtopShadowColor, m_background,
00584                     XmNbottomShadowColor, m_background,
00585                     NULL);
00586     }
00587     return True;
00588 }
00589 
00590 static void
00591 create_toolbar_separator(Widget parent, Widget *separator, int width)
00592 {
00593     *separator = XtVaCreateManagedWidget("toolbarSeparator",
00594                                     xmSeparatorWidgetClass, parent,
00595                                     XmNseparatorType, XmNO_LINE,
00596                                     XmNminWidth, width,
00597                                     XmNwidth, width,
00598                                     XmNorientation, XmVERTICAL,
00599                                     XmNleftAttachment, XmATTACH_WIDGET,
00600                                     XmNrightAttachment, XmATTACH_WIDGET,
00601                                     XmNtopAttachment, XmATTACH_FORM,
00602                                     XmNbottomAttachment, XmATTACH_FORM,
00603                                     NULL);
00604 }
00605 
00606 /* taken from shadow_trick in WlToolBar.c, mgv-3.1.5:
00607    Get a raised behaviour for armed buttons, by explicitly setting
00608    a shadow on entry and removing it on leave.
00609 */
00610 static void
00611 enter_leave(Widget w, XtPointer closure, XEvent *event, Boolean *cont)
00612 {
00613     char *tooltip = (char *)closure;
00614     XCrossingEvent *ev = (XCrossingEvent *)event;
00615     static Boolean entered = False; /* to skip double leaves */
00616 
00617     UNUSED(cont);
00618 
00619     if (ev->type == EnterNotify) {
00620        if (!resource.toolbar_buttons_raised) {
00621            /* draw shadows */
00622            XtVaSetValues(w,
00623                        XmNtopShadowColor, m_top_shadow_color,
00624                        XmNbottomShadowColor, m_bottom_shadow_color,
00625                        NULL);
00626        }
00627        entered = True;
00628        if (resource.tooltips_in_statusline) {
00629            statusline_print(STATUS_SHORT, tooltip);
00630        }
00631     }
00632     else if (ev->type == LeaveNotify && !resource.toolbar_buttons_raised) {
00633        /* remove shadows */
00634        if (!entered)
00635            return;
00636 
00637        XtVaSetValues(w,
00638                     XmNtopShadowColor, m_background,
00639                     XmNbottomShadowColor, m_background,
00640                     NULL);
00641        entered = False;
00642     }
00643 }
00644 
00645 /*
00646   save info about special buttons in m_button_info
00647   (see definition of that for details)
00648 */
00649 static void
00650 button_info_save(struct xdvi_action *action, Widget w)
00651 {
00652     if (action->proc == Act_back_page && strcmp(action->param, "1") == 0) {
00653        m_button_info.back_button = w;
00654     }
00655     else if (action->proc == Act_forward_page && strcmp(action->param, "1") == 0) {
00656        m_button_info.forward_button = w;
00657     }
00658     else if (action->proc == Act_pagehistory_back) {
00659        set_button_sensitivity(w, False);
00660        m_button_info.hyperref_back_button = w;
00661     }
00662     else if (action->proc == Act_pagehistory_forward) {
00663        set_button_sensitivity(w, False);
00664        m_button_info.hyperref_forward_button = w;
00665     }
00666     else if (action->proc == Act_set_shrink_factor && action->param[0] == '+') {
00667        m_button_info.zoom_in_button = w;
00668     }
00669 }
00670 
00671 #endif /* HAVE_XPM */
00672 
00673 /*
00674  * Create a toolbar with buttons, return toolbar widget.
00675  */
00676 Widget
00677 create_toolbar(Widget parent, Widget menu_bar)
00678 {
00679 #if HAVE_XPM
00680     size_t alloc_len = 0, n;
00681     size_t alloc_step = 16;
00682     const char *c_ptr, *e_ptr;
00683 #endif /* HAVE_XPM */
00684     Widget tool_bar;
00685     resource.toolbar_unusable = False;
00686     
00687     tool_bar_frame = XtVaCreateWidget("toolBarFrame",
00688                                   xmFrameWidgetClass, parent,
00689                                   XmNshadowType, XmSHADOW_OUT,
00690                                   XmNleftAttachment, XmATTACH_FORM,
00691                                   XmNrightAttachment, XmATTACH_FORM,
00692                                   XmNtopAttachment, XmATTACH_WIDGET,
00693                                   XmNtopWidget, menu_bar,
00694                                   NULL);
00695 
00696     tool_bar = XtVaCreateManagedWidget("toolBar",
00697                                    xmRowColumnWidgetClass, tool_bar_frame,
00698                                    XmNchildType, XmFRAME_WORKAREA_CHILD,
00699                                    XmNrowColumnType, XmWORK_AREA,
00700                                    XmNorientation, XmHORIZONTAL,
00701                                    XmNtraversalOn, False,
00702                                    XmNisHomogeneous, False,
00703                                    XmNpacking, XmPACK_TIGHT,
00704                                    XmNspacing, 0, /* override to use SEPARATOR(n) instead */
00705                                    XmNadjustLast, True,
00706                                    NULL);
00707 
00708 #if HAVE_XPM
00709     /* parse toolbar_translations, create the widgets and assign the actions */
00710     for (n = 0, c_ptr = resource.toolbar_translations;
00711         c_ptr != NULL && *c_ptr != '\0';
00712         c_ptr = e_ptr, n++) {
00713        char **line_items = NULL;
00714        int extra_space;
00715        size_t len, curr, item_count = 0;
00716        
00717        if ((e_ptr = strchr(c_ptr, '\n')) == NULL
00718            /* ... and in case last line doesn't end with \n ... */
00719            && (e_ptr = strchr(c_ptr, '\0')) == NULL) {
00720            break;
00721        }
00722 
00723        if (e_ptr == c_ptr) {
00724            XDVI_WARNING((stderr, "Skipping empty line in toolbarTranslations resource."));
00725            e_ptr++;
00726            continue;
00727        }
00728        len = e_ptr - c_ptr;
00729        TRACE_GUI((stderr, "LEN %lu: |%.*s|", (unsigned long)len, (int)len, c_ptr));
00730 
00731        line_items = split_line(c_ptr, SEP_CHAR, 0, len, &item_count);
00732 
00733        if (globals.debug & DBG_GUI) {
00734            int k;
00735            for (k = 0; line_items[k] != NULL; k++) {
00736               fprintf(stderr, "ITEM %d of %lu: |%s|\n", k, (unsigned long)item_count, line_items[k]);
00737            }
00738        }
00739        while (alloc_len <= n + 1) {
00740            alloc_len += alloc_step;
00741            toolbar_buttons = xrealloc(toolbar_buttons, alloc_len * sizeof *toolbar_buttons);
00742        }
00743            
00744        if (item_count == 1 && sscanf(line_items[0], "SPACER(%d)", &extra_space) == 1) {
00745            TRACE_GUI((stderr, "creating spacer of witdh %d at %lu", extra_space, (unsigned long)n));
00746            create_toolbar_separator(tool_bar, &(toolbar_buttons[n].button), extra_space);
00747            toolbar_buttons[n].type = TB_SEPARATOR;
00748        }
00749        else if (item_count == 4) {
00750            Pixmap sens, insens;
00751            int idx = strtoul(line_items[0], (char **)NULL, 10);
00752            struct xdvi_action *action;
00753 
00754            TRACE_GUI((stderr, "creating pixmap at %d", idx));
00755            if (!create_pixmap(tool_bar, idx, &sens, &insens)) {
00756               free(toolbar_buttons);
00757               toolbar_buttons = NULL;
00758               break;
00759            }
00760            TRACE_GUI((stderr, "creating button %ld", (unsigned long)n));
00761            if (!create_toolbar_button(tool_bar, &(toolbar_buttons[n].button), &sens, &insens)) {
00762               free(toolbar_buttons);
00763               toolbar_buttons = NULL;
00764               break;
00765            }
00766            toolbar_buttons[n].type = TB_BUTTON;
00767 
00768            if ((action = compile_action(line_items[3])) != NULL) {
00769               char *long_tooltip = xstrdup(line_items[1]);
00770               toolbar_buttons[n].tip = xstrdup(line_items[2]);
00771               /* char *short_tooltip = xstrdup(line_items[2]); */
00772               command_call[0].closure = (XtPointer) action;
00773 
00774               /*
00775                 eventually save this widget in list of `special' buttons
00776                 that need to toggle between sensitive/insensitive
00777               */
00778               button_info_save(action, toolbar_buttons[n].button);
00779 
00780               XtVaSetValues(toolbar_buttons[n].button, XmNactivateCallback, (XtArgVal)command_call, NULL);
00781               XtAddEventHandler(toolbar_buttons[n].button,
00782                               EnterWindowMask | LeaveWindowMask,
00783                               False,
00784                               enter_leave,
00785                               long_tooltip);
00786            }
00787            else {
00788               XDVI_WARNING((stderr, "Invalid action \"%s\" in toolbarTranslations resource:\n\"%.*s\"",
00789                            line_items[3], (int)len, c_ptr));
00790            }
00791        }
00792        else {
00793            XDVI_WARNING((stderr, "Skipping malformed line \"%.*s\" in toolbarTranslations resource "
00794                        "(%lu instead of 4 items).",
00795                        (int)len, c_ptr, (unsigned long)item_count));
00796            toolbar_buttons[n].button = 0;
00797        }
00798 
00799        for (curr = 0; curr < item_count; curr++) {
00800            free(line_items[curr]);
00801        }
00802        free(line_items);
00803        line_items = NULL;
00804        
00805        if (*e_ptr != '\0')
00806            e_ptr++;
00807     }
00808 #else
00809     if ((resource.expert_mode & XPRT_SHOW_SCROLLBARS) != 0) {
00810        XDVI_WARNING((stderr, "This version has been compiled without XPM support. "
00811                     "Disabling the toolbar, which needs XPM."));
00812     }
00813 #endif /* HAVE_XPM */
00814 
00815     if (toolbar_buttons == NULL) {
00816        resource.toolbar_unusable = True;
00817        resource.expert_mode ^= XPRT_SHOW_TOOLBAR;
00818     }
00819     else {
00820 #if HAVE_XPM  
00821        toolbar_buttons[n].button = 0; /* terminate info */
00822 #endif
00823     }
00824     return tool_bar;
00825 }
00826 
00827 /*
00828  * Add the tips to the toolbar.  This has to be done after the toolbar has been
00829  * realized.
00830  */
00831 void
00832 create_tips(Widget toplevel)
00833 {
00834 #if HAVE_XPM
00835     Widget tip_shell;
00836     
00837     int i;
00838 
00839     if (resource.toolbar_unusable) /* don't create tips in this case */
00840        return;
00841     
00842     tip_shell = XtVaCreatePopupShell("tipShell", tipWidgetClass,
00843                                  toplevel, XtNwidth, 1, XtNheight, 1,
00844                                  NULL);
00845 
00846     for (i = 0; toolbar_buttons[i].button != 0; i++) {
00847        if (toolbar_buttons[i].type == TB_BUTTON) {
00848            TipAddWidget(tip_shell, toolbar_buttons[i].button, toolbar_buttons[i].tip);
00849        }
00850     }
00851 #else
00852     UNUSED(toplevel);
00853 #endif /* HAVE_XPM */
00854 }
00855 
00856 
00857 #else /* MOTIF */
00858 /* silence "empty compilation unit" and "`toolbar_xpm' defined but not used" warnings */
00859 static void bar(void); static void foo() { UNUSED(toolbar_xpm); bar(); } static void bar(void) { foo(); }
00860 #endif /* MOTIF */