Back to index

tetex-bin  3.0
xm_prefs.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2004 Stefan Ulrich
00003  *
00004  * Permission is hereby granted, free of charge, to any person obtaining a copy
00005  * of this software and associated documentation files (the "Software"), to
00006  * deal in the Software without restriction, including without limitation the
00007  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
00008  * sell copies of the Software, and to permit persons to whom the Software is
00009  * furnished to do so, subject to the following conditions:
00010  *
00011  * The above copyright notice and this permission notice shall be included in
00012  * all copies or substantial portions of the Software.
00013  *
00014  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00015  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00016  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
00017  * PAUL VOJTA OR ANY OTHER AUTHOR OF THIS SOFTWARE BE LIABLE FOR ANY CLAIM,
00018  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
00019  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
00020  * OTHER DEALINGS IN THE SOFTWARE.
00021  *
00022  */
00023 
00024 /*
00025  * Preferences dialog for xdvik.
00026  */
00027 
00028 #include "xdvi-config.h"
00029 #include "xdvi.h"
00030 
00031 #include "x_util.h"
00032 #include "xm_colorsel.h"
00033 #include "topic-window.h"
00034 #include "message-window.h"
00035 #include "util.h"
00036 #include "events.h"
00037 
00038 #include "xm_prefsP.h"
00039 #include "xm_prefs.h"
00040 
00041 #include "xm_prefs_appearance.h"
00042 #include "xm_prefs_fonts.h"
00043 #include "xm_prefs_helpers.h"
00044 #include "xm_prefs_page.h"
00045 #include "xm_prefs_scroll.h"
00046 
00047 /* for reverting preferences, we need access to more prototypes ... */
00048 #include "dvi-draw.h"
00049 #include "search-internal.h"
00050 #include "statusline.h"
00051 #include "xm_toolbar.h"
00052 #include "xm_menu.h"
00053 #include "pagesel.h"
00054 
00055 #ifdef MOTIF /* entire file */
00056 
00057 #include <X11/Xatom.h>
00058 
00059 #include <Xm/Xm.h>
00060 #include <Xm/Protocols.h>
00061 #include <Xm/DialogS.h>
00062 #include <Xm/Form.h>
00063 
00064 #define SCROLLING_DONE 0
00065 
00066 #define NUM_PREFS_TOPICS 16 /* should be ample ... */
00067 
00068 /* hmm, should maybe move these into xm_prefsP.c ... */
00069 void
00070 h_attach_below(Widget x, Widget y) {
00071     if (y == NULL) {
00072        /* no top widget, attach to form - could just pass NULL as XmNtopWidget, but lesstif warns in that case ... */
00073        XtVaSetValues(x,
00074                     XmNtopAttachment, XmATTACH_FORM,
00075                     XmNleftAttachment, XmATTACH_FORM,
00076                     XmNrightAttachment, XmATTACH_FORM,
00077                     NULL);
00078     }
00079     else {
00080        XtVaSetValues(x,
00081                     XmNtopAttachment, XmATTACH_WIDGET,
00082                     XmNtopWidget, y,
00083                     XmNleftAttachment, XmATTACH_FORM,
00084                 XmNrightAttachment, XmATTACH_FORM,
00085                     NULL);
00086     }
00087 }
00088 
00089 
00090 static void
00091 initialize_items(struct topic_info *info)
00092 {
00093     int i = 0;
00094     
00095     info->items[i].widget = prefs_appearance(info);
00096     info->items[i].topic = xstrdup("Appearance");
00097     info->items[i].title = xstrdup("Change the appearance of xdvi");
00098 
00099     i++;
00100     info->items[i].widget = prefs_fonts_and_colors(info);
00101     info->items[i].topic = xstrdup("Fonts and Colors");
00102     info->items[i].title = xstrdup("Display of fonts and colors in DVI files");
00103 
00104     i++;
00105     info->items[i].widget = prefs_paper(info);
00106     info->items[i].topic = xstrdup("Page Size");
00107     info->items[i].title = xstrdup("Customize the default window and page size");
00108 
00109 #if SCROLLING_DONE /* not finished yet */
00110     i++;
00111     info->items[i].widget = prefs_scrolling(info);
00112     info->items[i].topic = xstrdup("Scrolling");
00113     info->items[i].title = xstrdup("Customize the scrolling behaviour when switching pages");
00114 #endif
00115 
00116     i++;
00117     info->items[i].widget = prefs_helpers(info);
00118 #ifdef LESSTIF_VERSION
00119     /* compensate for width computation bug by adding extra whitespace at end */
00120     info->items[i].topic = xstrdup("Helper Applications ");
00121 #else
00122     info->items[i].topic = xstrdup("Helper Applications");
00123 #endif
00124     info->items[i].title = xstrdup("External programs used by xdvi");
00125 
00126     /* terminate */
00127     i++;
00128     info->items[i].widget = 0;
00129     info->items[i].topic = info->items[i].title = NULL;
00130 
00131     ASSERT(i < NUM_PREFS_TOPICS, "NUM_PREFS_TOPICS too small!");
00132 }
00133 
00134 void
00135 remove_from_deplist(struct prefs_choice *prefs, Widget w)
00136 {
00137     size_t i;
00138     Boolean found = False;
00139 
00140     /* locate this widget */
00141     for (i = 0; i < prefs->depwin_cnt; i++) {
00142        if (prefs->depwin[i] == w) {
00143            found = True;
00144            break;
00145        }
00146     }
00147     
00148     if (found) {
00149        /* move later widgets down */
00150        for (; i < prefs->depwin_cnt - 1; i++) {
00151            prefs->depwin[i] = prefs->depwin[i + 1];
00152        }
00153        prefs->depwin_cnt--;
00154     }
00155 
00156 #if 0
00157     fprintf(stderr, "deplist after removal:\n");
00158     for (i = 0; i < prefs->depwin_cnt; i++) {
00159        fprintf(stderr, "%d: %p\n", i, prefs->depwin[i]);
00160     }
00161 #endif    
00162 }
00163 
00164 static void
00165 update_button(Widget button, Pixel pix)
00166 {
00167     struct color_button_info *cinfo;
00168     static XmDrawnButtonCallbackStruct cbs;
00169     
00170     XtVaGetValues(button, XmNuserData, &cinfo, NULL);
00171     cinfo->pixel = pix;
00172     cbs.reason = XmCR_EXPOSE;
00173     XtCallCallbacks(button, XmNexposeCallback, &cbs);
00174 }
00175 
00176 static void
00177 update_button_by_name(Pixel pix, const char *name)
00178 {
00179     static Widget pref_shell = 0;
00180     Widget button;
00181 
00182     if (pref_shell == 0
00183        && !get_widget_by_name(&pref_shell, globals.widgets.top_level, Xdvi_PREFS_DIALOG_NAME, True))
00184        return;
00185     
00186     if (get_widget_by_name(&button, pref_shell, name, True)) {
00187        update_button(button, pix);
00188     }
00189 }
00190 
00191 static Boolean
00192 revert_colors(Pixel fg, Pixel bg, Pixel hl)
00193 {
00194     XColor color_data[2];
00195     Boolean need_redraw = False;    
00196     Widget button;
00197     Pixel visited, unvisited;
00198     
00199     color_data[0].pixel = resource.fore_Pixel;
00200     color_data[1].pixel = resource.back_Pixel;
00201     
00202     str_to_pixel(globals.widgets.top_level, resource.visited_link_color, &visited);
00203     str_to_pixel(globals.widgets.top_level, resource.link_color, &unvisited);
00204 
00205     XQueryColors(DISP, G_colormap, color_data, 2);
00206 
00207     if (fg != resource.fore_Pixel) {
00208        XGCValues values;
00209        
00210        fg_initial.r = color_data[0].red;
00211        fg_initial.g = color_data[0].green;
00212        fg_initial.b = color_data[0].blue;
00213 
00214        values.foreground = resource.rule_pixel = resource.fore_Pixel;
00215        XChangeGC(DISP, globals.gc.ruler, GCForeground, &values);
00216 
00217        scanned_page = scanned_page_color = scanned_page_reset;
00218 
00219        update_button_by_name(resource.fore_Pixel, Xdvi_FG_COLOR_BTN);
00220 
00221        need_redraw = True;
00222     }
00223     if (bg != resource.back_Pixel) {
00224        bg_initial.r = color_data[1].red;
00225        bg_initial.g = color_data[1].green;
00226        bg_initial.b = color_data[1].blue;
00227 
00228        scanned_page = scanned_page_color = scanned_page_reset;
00229 
00230        update_button_by_name(resource.back_Pixel, Xdvi_BG_COLOR_BTN);
00231 
00232        need_redraw = True;
00233     }
00234     if (hl != resource.hl_Pixel) {
00235        XGCValues values;
00236        values.foreground = resource.hl_Pixel;
00237        XChangeGC(DISP, globals.gc.high, GCForeground, &values);
00238        /* hack to update match GC: fake change in inverted property, redraw
00239           so that GC is cleared, then change inverted property back */
00240        resource.match_highlight_inverted = !resource.match_highlight_inverted;
00241        search_draw_inverted_regions();
00242        resource.match_highlight_inverted = !resource.match_highlight_inverted;
00243 
00244        update_button_by_name(resource.hl_Pixel, Xdvi_HL_COLOR_BTN);
00245 
00246        need_redraw = True;
00247     }
00248     /* NOTE: pixels for hyperlinks are not stored anywhere; just update always: */
00249     if (get_widget_by_name(&button, globals.widgets.top_level, Xdvi_VISITED_LINKS_BTN, True)) {
00250        update_button(button, visited);
00251        h_update_hyperlinks(button, visited); /* this already triggers a redraw */
00252     }
00253     if (get_widget_by_name(&button, globals.widgets.top_level, Xdvi_UNVISITED_LINKS_BTN, True)) {
00254        update_button(button, unvisited);
00255        h_update_hyperlinks(button, unvisited); /* this already triggers a redraw */
00256     }
00257     return need_redraw;
00258 }
00259 
00260 static void
00261 revert_resources(void)
00262 {
00263     Pixel curr_fg = resource.fore_Pixel;
00264     Pixel curr_bg = resource.back_Pixel;
00265     Pixel curr_hl = resource.hl_Pixel;
00266     Boolean need_redraw = False;
00267     
00268     /* save some old values */
00269     int save_shrink  = resource.shrinkfactor;
00270         
00271     reload_app_resources();
00272 
00273     /* revert from saved values */
00274     resource.use_color = globals.curr_use_color;
00275     resource.gamma = globals.curr_gamma;
00276     resource.paper = globals.curr_paper;
00277     resource.shrinkfactor = save_shrink;
00278     do_set_shrinkfactor(resource.shrinkfactor, True);
00279     
00280     update_preferences_darkness();
00281 
00282     /* revert expert mode */
00283     if (resource.expert)
00284        resource.expert_mode = XPRT_SHOW_NONE;
00285     update_expert_mode();
00286 
00287     toggle_statusline();
00288 #ifndef MOTIF
00289     if (!BROKEN_RECONFIG)
00290        toggle_scrollbars();
00291 #else
00292     toggle_scrollbars();
00293 #endif
00294     
00295 #ifdef MOTIF
00296     toggle_pagelist();
00297     toggle_toolbar();
00298     toggle_menubar();
00299 #else
00300     toggle_buttons();
00301 #endif
00302 
00303     /* reset tooltips_wait_period, similar to TipAddWidget() in Tip.c */
00304     resource.tooltips_wait_period = resource.tooltips_wait_period_bak;
00305     if (resource.tooltips_wait_period < 0) {
00306        resource.show_tooltips = False;
00307     }
00308     else if (!resource.show_tooltips) {
00309        if (resource.tooltips_wait_period == 0)
00310            resource.tooltips_wait_period = -1;
00311        else
00312            resource.tooltips_wait_period = -resource.tooltips_wait_period;
00313     }
00314     
00315     update_preferences_expert();
00316     update_preferences_tooltips();
00317     update_preferences_search();
00318     
00319     update_preferences_color();
00320     update_preferences_hyperlinks();
00321 
00322     update_preferences_windowsize();
00323     update_preferences_shrink();
00324     update_preferences_paper();
00325 
00326     update_preferences_helpers();
00327     
00328     need_redraw |= revert_colors(curr_fg, curr_bg, curr_hl);
00329 
00330 /*     if (need_redraw) */
00331     /* just reload it always, there's too many exceptions ...
00332        redraw isn't always sufficient if file has colors. */
00333     globals.ev.flags |= EV_RELOAD;
00334 
00335 }
00336 
00337 /* add widget w to list of dependent windows */
00338 void
00339 add_to_deplist(struct prefs_choice *prefs, Widget w)
00340 {
00341 #if 0
00342     size_t i;
00343 #endif    
00344     prefs->depwin = xrealloc(prefs->depwin,
00345                           (prefs->depwin_cnt + 1) * sizeof *(prefs->depwin));
00346     prefs->depwin[prefs->depwin_cnt] = w;
00347     prefs->depwin_cnt++;
00348 
00349 #if 0
00350     fprintf(stderr, "deplist after adding:\n");
00351     for (i = 0; i < prefs->depwin_cnt; i++) {
00352        fprintf(stderr, "%lu: %p\n", (unsigned long)i, prefs->depwin[i]);
00353     }
00354 #endif    
00355 }
00356 
00357 static void
00358 reread_prefs_cb(Window w)
00359 {
00360     if (w != XtWindow(globals.widgets.top_level)) {
00361        XChangeProperty(DISP, w,
00362                      atom_reread_prefs(), atom_reread_prefs(), 8,
00363                      PropModeReplace,
00364                      /* dummy values, since all the other instance needs to do is
00365                         reread the ~/.xdvirc.tmp file */
00366                      (unsigned char *)"Y", 1);
00367     }
00368 }
00369 
00370 static void
00371 apply_prefs_cb(XtPointer arg)
00372 {
00373     struct topic_info *info = (struct topic_info *)arg;
00374     struct prefs_choice *prefs = (struct prefs_choice *)info->data;
00375     size_t i;
00376     Widget colorsel;
00377 
00378     if (get_widget_by_name(&colorsel, globals.widgets.top_level, Xdvi_COLOR_DIALOG_NAME, False)) {
00379        XtPopdown(XtParent(colorsel));
00380     }
00381 
00382     /* pop down dependent windows */
00383     TRACE_GUI((stderr, "window count: %lu\n", (unsigned long)prefs->depwin_cnt));
00384     for (i = 0; i < prefs->depwin_cnt; i++) {
00385        TRACE_GUI((stderr, "popping down %lu: %p", (unsigned long)i, (void *)(prefs->depwin[i])));
00386        if (XtIsRealized(prefs->depwin[i])) {
00387            XtCallCallbacks(prefs->depwin[i], XmNcancelCallback, NULL);
00388            XSync(DISP, True); /* wait for server to catch up */
00389            if (XtIsRealized(prefs->depwin[i])) {
00390               TRACE_GUI((stderr, "calling XmNokCallback of %lu: %p", (unsigned long)i, (void *)(prefs->depwin[i])));
00391               XtCallCallbacks(prefs->depwin[i], XmNokCallback, NULL);
00392            }
00393        }
00394     }
00395     free(prefs->depwin);
00396     prefs->depwin = NULL;
00397     prefs->depwin_cnt = 0;
00398 
00399     if (prefs->db == NULL) /* callback invoked multiple times? */
00400        return;
00401 
00402     merge_into_user_db(prefs->db); /* this destroys prefs->db */
00403     prefs->db = NULL;
00404 
00405     /* remember some current values */
00406     free(globals.curr_paper);
00407     if (resource.paper != NULL)
00408        globals.curr_paper = xstrdup(resource.paper);
00409     
00410     free(globals.curr_editor);
00411     if (resource.editor != NULL)
00412        globals.curr_editor = xstrdup(resource.editor);
00413     
00414     free(globals.curr_browser);
00415     if (resource.browser != NULL)
00416     globals.curr_browser = xstrdup(resource.browser);
00417 
00418 /*     fprintf(stderr, "set curr_browser to: |%s|\n", globals.curr_browser); */
00419 /*     fprintf(stderr, "set curr_editor to: |%s|\n", globals.curr_editor); */
00420     
00421     if (get_xdvi_window_id(False, NULL) && save_user_preferences(False)) {
00422        /* if other instances of xdvi are running, make them reread the
00423           changed preferences by writing them to ~/.xdvirc.tmp and having
00424           them read that file; otherwise they would overwrite the file if
00425           user quits them after the current instance.
00426        */
00427        get_xdvi_window_id(False, reread_prefs_cb);
00428     }
00429 }
00430 
00431 static void
00432 revert_prefs_cb(XtPointer arg)
00433 {
00434     struct topic_info *info = (struct topic_info *)arg;
00435     struct prefs_choice *prefs = (struct prefs_choice *)info->data;
00436     size_t i;
00437 
00438     /* pop down dependent windows */
00439     TRACE_GUI((stderr, "window count: %lu", (unsigned long)prefs->depwin_cnt));
00440     for (i = 0; i < prefs->depwin_cnt; i++) {
00441        TRACE_GUI((stderr, "popping down %lu: %p", (unsigned long)i, (void *)(prefs->depwin[i])));
00442        if (XtIsRealized(prefs->depwin[i])) {
00443            XtCallCallbacks(prefs->depwin[i], XmNcancelCallback, NULL);
00444            XSync(DISP, True); /* wait for server to catch up */
00445            if (XtIsRealized(prefs->depwin[i])) {
00446               TRACE_GUI((stderr, "calling XmNokCallback of %lu: %p", (unsigned long)i, (void *)(prefs->depwin[i])));
00447               XtCallCallbacks(prefs->depwin[i], XmNokCallback, NULL);
00448            }
00449        }
00450     }
00451     free(prefs->depwin);
00452     prefs->depwin = NULL;
00453     prefs->depwin_cnt = 0;
00454 
00455     if (prefs->db == NULL) { /* callback invoked multiple times, or prefs not changed */
00456        return;
00457     }
00458 
00459     revert_resources();
00460     
00461     XrmDestroyDatabase(prefs->db);
00462     prefs->db = NULL;
00463 }
00464 
00465 static void
00466 save_prefs_exit(XtPointer arg)
00467 {
00468     struct topic_info *info = (struct topic_info *)arg;
00469     apply_prefs_cb(info);
00470     XtPopdown(info->shell);
00471     XSync(DISP, False);
00472     xdvi_exit(EXIT_SUCCESS);
00473 }
00474 
00475 static void
00476 no_save_prefs_exit(XtPointer arg)
00477 {
00478     struct topic_info *info = (struct topic_info *)arg;
00479     revert_prefs_cb(info);
00480     XtPopdown(info->shell);
00481     XSync(DISP, False);
00482     xdvi_exit(EXIT_SUCCESS);
00483 }
00484 
00485 static void
00486 close_prefs_exit(Widget w, XtPointer arg)
00487 {
00488     struct topic_info *info = (struct topic_info *)arg;
00489     struct prefs_choice *prefs = (struct prefs_choice *)info->data;
00490 /*      Widget dialog; */
00491 /*      if (get_widget_by_name(&dialog, w, Xdvi_MESSAGE_DIALOG_NAME, True)) { */
00492 /*     fprintf(stderr, "!!!!!!!!! removing window from deplist!\n"); */
00493 /*     remove_from_deplist(prefs, dialog); */
00494 /*      } */
00495 
00496     remove_from_deplist(prefs, w);
00497     
00498 }
00499 
00500 Boolean
00501 preferences_changed(void)
00502 {
00503     Widget prefs_shell = 0, topic_pane = 0;
00504     
00505     if (get_widget_by_name(&prefs_shell, globals.widgets.top_level, "preferences_window", False)
00506        && get_widget_by_name(&topic_pane, prefs_shell, "topic_pane", False)) {
00507        struct topic_info *info = NULL;
00508        struct prefs_choice *prefs = NULL;
00509 
00510        XtVaGetValues(topic_pane, XmNuserData, &info, NULL);
00511        if (info == NULL) {
00512            return False;
00513        }
00514        prefs = (struct prefs_choice *)info->data;
00515        if (prefs->db != NULL) { /* prefs changed */
00516 
00517            Widget popup = choice3_dialog(prefs_shell,
00518                                      MSG_QUESTION, NULL,
00519 #ifndef MOTIF                       
00520                                      NULL,
00521 #endif
00522                                      close_prefs_exit, info, /* pre_callbacks */
00523                                      "Save and Exit", save_prefs_exit, info,
00524                                      "Exit, don't Save", no_save_prefs_exit, info,
00525                                      "Cancel", NULL, NULL,
00526                                      "Preferences have been changed, but not saved yet. "
00527                                      "Save them now?");
00528            add_to_deplist(prefs, popup);
00529            return True;         
00530        }
00531     }
00532 
00533     return False;
00534 }
00535 
00536 void
00537 popup_preferences_dialog(Widget parent, int arg)
00538 {
00539     static Widget preferences_shell = 0;
00540     static struct topic_info info;
00541     static struct topic_item items[NUM_PREFS_TOPICS];
00542     static struct prefs_choice *prefs = NULL;
00543 
00544     if (preferences_shell == 0) { /* called 1st time; create widget */
00545        info.ok_callback = apply_prefs_cb;
00546        info.cancel_callback = revert_prefs_cb;
00547        info.items = items;
00548 /*     info.items_size = NUM_PREFS_TOPICS; */
00549 
00550        prefs = xmalloc(sizeof *prefs);
00551        prefs->depwin_cnt = 0;
00552        prefs->depwin = NULL;
00553 /*     prefs->orig = orig_prefs; */
00554        /* apply_prefs_cb/revert_prefs_cb are responsible for copying
00555           the changed preferences into the current preferences as
00556           appropriate, and free()ing prefs.changed */
00557 /*     prefs->changed = xmalloc(sizeof *(prefs->changed)); */
00558 /*     copy_resources(orig_prefs, prefs->changed); */
00559        prefs->db = NULL; /*  XrmGetStringDatabase(""); */
00560        info.data = prefs;
00561        
00562        preferences_shell = create_topic_window(parent,
00563                                           "xdvik: Preferences",
00564                                           Xdvi_PREFS_DIALOG_NAME,
00565                                           &info,
00566                                           initialize_items,
00567                                           "OK", "Cancel");
00568        info.shell = preferences_shell;
00569        center_window(preferences_shell, parent);
00570        select_topic(&info, 0);
00571     }
00572 
00573     if (arg >= 0)
00574        select_topic(&info, arg);
00575     
00576     XtPopup(preferences_shell, XtGrabNone);
00577 
00578     if (resource.no_init_file) {
00579        popup_message(preferences_shell,
00580                     MSG_WARN,
00581                     NULL,
00582                     "You specified the resource `noInitFile' or the `-q' command-line option. "
00583                     "Any preferences that you set in this dialog will be lost when you exit xdvi.");
00584     }
00585 }
00586 
00587 #else
00588 /* silence `empty compilation unit' warnings */
00589 static void bar(void); static void foo() { bar(); } static void bar(void) { foo(); }
00590 #endif /* MOTIF */