v09i086: xrolo -- an XView rolodex, Part03/03
Luis Soltero
luis at rice.edu
Thu Oct 11 07:39:01 AEST 1990
Submitted-by: Luis Soltero <luis at rice.edu>
Posting-number: Volume 9, Issue 86
Archive-name: xrolo/part03
#! /bin/sh
# This is a shell archive. Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file". To overwrite existing
# files, type "sh file -c". You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g.. If this archive is complete, you
# will see the following message at the end:
# "End of archive 3 (of 3)."
# Contents: panel.c
# Wrapped by luis at oort on Wed Oct 10 15:56:19 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'panel.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'panel.c'\"
else
echo shar: Extracting \"'panel.c'\" \(37272 characters\)
sed "s/^X//" >'panel.c' <<'END_OF_FILE'
X#ifndef lint
Xstatic char sccsid[] = "@(#)panel.c 2.3 8/16/88";
X#endif
X
X/*
X * Stuff dealing with the panel
X */
X
X/*
X * -------------------------------------------------------------------------
X * ROLO - A Sun Tool to implement a Rolodex-style list of notes
X *
X * This code manipulates "cards" in a visual manner approximating
X * a rolodex file. All the cards are stored in one real file, the
X * cards are seperated by a ^L (form-feed). The default path
X * name is $HOME/.rolo. A different pathname may be specified at
X * startup on the command line. The pathname is relative to the
X * user's home directory.
X *
X * Due to bugs in the 3.0 distribution, especially with text subwindows,
X * this code is only guaranteed to compile and run properly with 3.2
X * or greater.
X *
X * This code is public domain, anyone and everyone is welcome to it.
X * All I ask is that my name and this notice remain on it. If Sun would
X * like to bundle it with their product they are welcome to do so,
X * I only ask that the sources be included in the binary distribution.
X *
X * Please return any fixes, improvements, gripes, etc to me.
X *
X * Ron Hitchens ronbo at vixen.uucp
X * March 1987 (V1.0) hitchens at cs.utexas.edu
X * August 1988 (V2.0)
X * -------------------------------------------------------------------------
X */
X
X
X#include <stdio.h>
X#include <xview/xview.h>
X#include <xview/panel.h>
X#include <xview/textsw.h>
X#include <xview/seln.h>
X#include <sys/param.h>
X#include <ctype.h>
X
X#include "defs.h"
X#include "help.h"
X
X
X
X/* ------------------------------ Exports ---------------------------------- */
X
Xvoid show_card (), set_slider_max ();
X
XNotify_value rolo_destroy(), catch_resize();
X
X
X/* ------------------------------ Imports ---------------------------------- */
X
Xextern Textsw rolocard;
X
Xextern struct card *first, *last, *current;
X
Xextern int need_save;
X
Xextern char *rolofile;
X
Xextern struct card *make_card (), *insert_card (), *undelete_card (),
X *pop_card ();
X
Xextern Menu gen_undelete (), gen_undelete_before();
X
Xextern Menu_item check_stack ();
X
Xextern void delete_card (), dispose_card (),
X push_card (), dump_rolo (),
X nuke_active_cards (), init_rolo (),
X sort_cards (), set_stripe (),
X read_rolo (), write_rolo ();
X
Xextern char *first_char (), *index (), *re_comp (), *strcpy (),
X *strncpy (), *strcat (), *sprintf (), *getenv ();
X
Xextern caddr_t undel_menu_card ();
X
X
X/* ------------------------------ Locals ----------------------------------- */
X
Xstatic Panel_item regex_item, slider_item;
X
Xstatic int panel_height, panel_width;
X
Xstatic int mask_from_menu_value (), value_from_mask (),
X filename_ok ();
X
Xstatic void next_button (), next_button_next(), next_button_S_next(),
X prev_button (), prev_button_prev(), prev_button_S_prev(),
X new_button (), new_card_after(), new_card_before(),
X delete_button (), delete_button_delete(), delete_button_undelete(),
X delete_button_undelete_before(),
X file_button (), file_button_save(), file_button_reload(), file_button_sort(),
X file_button_sort_backwards(), file_button_load(),
X file_button_save_to_file(),
X
X done_button (), done_n_save(), done_n_save_exit(), done_n_exit(),
X find_button (), find_button_forward(), find_button_reverse(),
X
Xlist_button (), help_button (),
X slider_proc (), button_event(), goto_card (),
X no_comprendo ();
X
Xstatic char *get_selection ();
X
X
X/* prev new next delete */
Xstatic u_short buttons1_image [] = {
X#include "buttons1.icon"
X};
X
X/* drawer (file), "?" (help), flag (finished) and list */
Xstatic u_short buttons2_image [] = {
X#include "buttons2.icon"
X};
X
X#undef pr_region
XServer_image pr_region(i_image, i_w, x, y, w, h)
Xchar *i_image;
X{
X int i;
X int wb = w/8;
X int xb = x/8;
X int i_wb = i_w/8;
X char *image = (char *)malloc(h*wb);
X Server_image retval;
X
X /* build the image */
X for( i = 0; i < h; i++ ) {
X bcopy(i_image + y*i_wb + xb + i*i_wb, image + i*wb, wb);
X }
X
X retval = (Server_image)xv_create(NULL, SERVER_IMAGE,
X XV_WIDTH, 32,
X XV_HEIGHT, 32,
X SERVER_IMAGE_BITS, image,
X NULL
X );
X
X return(retval);
X}
X
X
X/* ------------------------------------------------------------------------- */
X
X
X
X/*
X * Actually create the panel subwindow and all the items in it.
X */
Xstatic Panel panel;
Xstatic Frame frame;
XPanel init_panel (_frame)
X Frame _frame;
X{
X int panel_columns;
X Menu tmpmenu;
X
X frame = _frame;
X panel = xv_create (frame, PANEL,
X PANEL_LAYOUT, PANEL_HORIZONTAL,
X/* PANEL_EVENT_PROC, button_event, */
X PANEL_ITEM_X_GAP, 10,
X PANEL_ITEM_Y_GAP, 9,
X 0);
X
X /* 1st row */
X /* next prev new delete list file help done */
X tmpmenu = menu_create (
X MENU_ACTION_ITEM, " Next Card ", next_button_next,
X MENU_ACTION_ITEM, "(S) Last Card ", next_button_S_next,
X NULL, NULL);
X
X (void) xv_create (panel, PANEL_BUTTON,
X PANEL_LABEL_IMAGE, pr_region(buttons1_image, 64, 0, 32, 32, 32),
X PANEL_ITEM_MENU, tmpmenu,
X PANEL_NOTIFY_PROC, next_button,
X XV_X, xv_col(panel, 0),
X XV_Y, xv_row(panel, 0),
X 0);
X
X tmpmenu = menu_create (
X MENU_ACTION_ITEM," Previous Card", prev_button_prev,
X MENU_ACTION_ITEM, "(S) First Card ", prev_button_S_prev,
X NULL, NULL);
X (void) xv_create (panel, PANEL_BUTTON,
X PANEL_LABEL_IMAGE, pr_region(buttons1_image, 64, 0, 0, 32, 32),
X PANEL_ITEM_MENU, tmpmenu,
X PANEL_NOTIFY_PROC, prev_button,
X 0);
X
X tmpmenu = menu_create (
X MENU_ACTION_ITEM, " New Card After this One ", new_card_after,
X MENU_ACTION_ITEM, "(S) New Card Before this One", new_card_before,
X NULL, NULL);
X (void) xv_create (panel, PANEL_BUTTON,
X PANEL_LABEL_IMAGE, pr_region (buttons1_image, 64, 32, 0, 32, 32),
X PANEL_ITEM_MENU, tmpmenu,
X PANEL_NOTIFY_PROC, new_button,
X 0);
X
X tmpmenu = xv_create (NULL, MENU,
X MENU_NOTIFY_PROC, delete_button_delete,
X MENU_ITEM,
X MENU_STRING,
X " Delete this card ",
X MENU_VALUE, 1,
X MENU_NOTIFY_PROC, delete_button_delete,
X 0,
X MENU_ITEM,
X MENU_STRING,
X "(S) UnDelete a Card (after) ",
X MENU_NOTIFY_PROC, delete_button_undelete,
X MENU_GEN_PROC, check_stack,
X MENU_GEN_PULLRIGHT, gen_undelete,
X MENU_CLIENT_DATA, FALSE,
X 0,
X MENU_ITEM,
X MENU_STRING,
X "(C) UnDelete a Card (before)",
X MENU_NOTIFY_PROC, delete_button_undelete_before,
X MENU_GEN_PROC, check_stack,
X MENU_GEN_PULLRIGHT, gen_undelete_before,
X MENU_CLIENT_DATA, TRUE,
X NULL,
X NULL);
X
X (void) xv_create (panel, PANEL_BUTTON,
X PANEL_LABEL_IMAGE, pr_region (buttons1_image, 64, 32, 32, 32, 32),
X PANEL_ITEM_MENU, tmpmenu,
X PANEL_NOTIFY_PROC, delete_button,
X 0);
X
X tmpmenu = menu_create (
X MENU_ACTION_ITEM, "Show Index List of Cards", list_button,
X NULL);
X (void) xv_create (panel, PANEL_BUTTON,
X PANEL_LABEL_IMAGE, pr_region(buttons2_image, 64, 32, 32, 32, 32),
X PANEL_ITEM_MENU, tmpmenu,
X 0);
X
X tmpmenu = menu_create (
X MENU_ACTION_ITEM,
X " Save Cards to Disk ",
X file_button_save,
X
X MENU_ACTION_ITEM,
X " (S) Reload From Disk ",
X file_button_reload,
X
X MENU_ACTION_ITEM,
X " (C) Sort Cards ",
X file_button_sort,
X
X MENU_ACTION_ITEM,
X "(S+C) Sort Backwards ",
X file_button_sort_backwards,
X
X MENU_ACTION_ITEM,
X " Load From Named File",
X file_button_load,
X
X MENU_ACTION_ITEM,
X " Save To Named File ",
X file_button_save_to_file,
X NULL);
X
X (void) xv_create (panel, PANEL_BUTTON,
X PANEL_LABEL_IMAGE, pr_region(buttons2_image, 64, 0, 0, 32, 32),
X PANEL_NOTIFY_PROC, file_button,
X PANEL_ITEM_MENU, tmpmenu,
X 0);
X
X tmpmenu = menu_create (
X MENU_ACTION_ITEM, "Display Help Message", help_button,
X NULL);
X
X (void) xv_create (panel, PANEL_BUTTON,
X PANEL_LABEL_IMAGE, pr_region(buttons2_image, 64, 32, 0, 32, 32),
X PANEL_ITEM_MENU, tmpmenu,
X 0);
X
X tmpmenu = menu_create (
X MENU_ACTION_ITEM,
X " Save Changes and Close ",
X done_n_save,
X
X MENU_ACTION_ITEM,
X "(S) Exit Rolo, Save Changes ",
X done_n_save_exit,
X
X MENU_ACTION_ITEM,
X "(C) Exit, Don't Save Changes",
X done_n_exit,
X NULL);
X (void) xv_create (panel, PANEL_BUTTON,
X PANEL_LABEL_IMAGE, pr_region(buttons2_image, 64, 0, 32, 32, 32),
X PANEL_NOTIFY_PROC, done_button,
X PANEL_ITEM_MENU, tmpmenu,
X 0);
X
X /*
X * Tighten up the window around the buttons in the first row, this
X * will be the width of the panel window, so we save that size for
X * later reference. We also ask for the width of the panel in
X * columns for computing the width of the find pattern text item.
X */
X window_fit_width (panel);
X panel_width = (int) xv_get (panel, XV_WIDTH);
X panel_columns = (int) xv_get (panel, WIN_COLUMNS);
X
X /*
X * Begin second row, set the inter-item gap so the Find button and
X * the text item following it are placed nicely.
X */
X xv_set (panel, PANEL_ITEM_X_GAP, 8, 0);
X
X tmpmenu = menu_create (
X MENU_ACTION_ITEM,
X " Find Regular Expression, Forward",
X find_button_forward,
X
X MENU_ACTION_ITEM,
X "(S) Find Regular Expression, Reverse",
X find_button_reverse,
X NULL);
X
X (void) xv_create (panel, PANEL_BUTTON,
X PANEL_LABEL_STRING, "Find",
X PANEL_ITEM_MENU, tmpmenu,
X PANEL_NOTIFY_PROC, find_button,
X 0);
X
X regex_item = xv_create (panel, PANEL_TEXT,
X PANEL_BLINK_CARET, TRUE,
X PANEL_LABEL_STRING, "",
X PANEL_VALUE_DISPLAY_LENGTH, panel_columns - 10,
X PANEL_VALUE_STORED_LENGTH, 80,
X PANEL_NOTIFY_PROC, find_button,
X 0);
X
X /*
X * Begin the third row, squeeze the inter-item gap back down so that
X * the slider value is displayed close to the slider bar.
X */
X xv_set (panel, PANEL_ITEM_X_GAP, 4, 0);
X
X tmpmenu = menu_create (
X MENU_ITEM,
X MENU_STRING, "Pick a card, any card",
X MENU_VALUE, 0,
X 0,
X 0);
X
X slider_item = xv_create (panel, PANEL_SLIDER,
X PANEL_MIN_VALUE, 0,
X PANEL_MAX_VALUE, 1,
X PANEL_VALUE, 1,
X /* This slider width is temp, recomputed later */
X PANEL_SLIDER_WIDTH, 500,
X PANEL_SHOW_RANGE, FALSE,
X PANEL_SHOW_VALUE, TRUE,
X PANEL_NOTIFY_LEVEL, PANEL_DONE,
X PANEL_NOTIFY_PROC, slider_proc,
X PANEL_ITEM_MENU, tmpmenu,
X 0);
X
X /*
X * Adjust the position of the text item slightly for better aesthetic
X * placement relative to the label in the Find button. Do it after
X * creating the slider so that the slider is placed relative to the
X * original position of the text item, not the final position.
X */
X xv_set (regex_item,
X XV_Y, xv_get (regex_item, XV_Y) + 4,
X 0);
X /*
X * Tighten up the panel window in both directions around the final
X * layout. Save off the resulting height for later use.
X */
X window_fit_height (panel);
X panel_height = (int) xv_get (panel, XV_HEIGHT);
X
X return (panel);
X}
X
X/* ------------------------------------------------------------------------- */
X
X
X/* Panel item notification handlers */
X
X/*
X * Notification proc for the "next" button. If the button is pressed
X * with no shift, the card is advanced to the next one. If pressed
X * with the shift key, then jump to the last card.
X */
X
Xdo_bozo(panel,p)
XPanel panel;
Xstruct card *p;
X{
X static int bozo = 0; /* speak second time end is reached */
X if (p == NULL_CARD) { /* did we step off end of the list? */
X if (panel == NULL || bozo != 0) { /* have we already beeped once? */
X msg ("This is the last card");
X } else {
X window_bell (panel);
X }
X bozo = (bozo+1)&1;
X return(1);
X }
X return(0);
X}
X
Xstatic int in_next_panel;
Xstatic void next_button_next(item, event)
XPanel_item item;
XEvent *event;
X{
X struct card *p;
X if ( in_next_panel ) {
X in_next_panel = 0;
X return;
X }
X save_card (current);
X p = current->c_next;
X if ( !do_bozo(panel,p) )
X show_card (p); /* all is well, display the new card */
X}
X
Xstatic void next_button_S_next(item, event)
XPanel_item item;
XEvent *event;
X{
X if ( in_next_panel ) {
X in_next_panel = 0;
X return;
X }
X save_card (current);
X show_card (last); /* all is well, display the new card */
X}
X
X/*ARGSUSED*/
Xstatic
Xvoid
Xnext_button (item, event)
X Panel_item item;
X Event *event;
X{
X if ( event_action(event) == ACTION_MENU )
X return;
X switch (value_from_mask (event)) {
X case SHIFT_CLICK: /* click plus shift (menu item 2) */
X next_button_S_next(item, event);
X break;
X
X default: /* any other shift mask (or none) */
X next_button_next(item, event);
X break;
X }
X in_next_panel = 1;
X}
X
X
X/*
X * Notification proc for the "Previous" button. Similar to the proc
X * above for next card.
X */
Xstatic in_prev_panel;
X
Xstatic void prev_button_prev(item,event)
XPanel_item item;
XEvent *event;
X{
X struct card *p;
X if ( in_prev_panel ) {
X in_prev_panel = 0;
X return;
X }
X save_card (current);
X p = current->c_prev; /* move backwards one card */
X if ( !do_bozo(panel,p) )
X show_card(p);
X}
X
Xstatic void prev_button_S_prev(item,event)
XPanel_item item;
XEvent *event;
X{
X if ( in_prev_panel ) {
X in_prev_panel = 0;
X return;
X }
X save_card (current);
X show_card (first);
X}
X
X/*ARGSUSED*/
Xstatic void prev_button (item, event)
X Panel_item item;
X Event *event;
X{
X
X if ( event_action(event) == ACTION_MENU )
X return;
X
X switch (value_from_mask (event)) {
X case SHIFT_CLICK:
X prev_button_S_prev(item, event);
X break;
X
X default:
X prev_button_prev(item, event);
X break;
X }
X in_prev_panel = 1;
X}
X
X
X/*
X * Notification for the "New Card" button. The new card is inserted
X * after the currently displayed card, unless the shift key is down,
X * in which case it is inserted before the current card.
X */
X
Xstatic in_new_button;
X
Xstatic void new_card_after (item, event)
XPanel_item item;
XEvent *event;
X{
X struct card *c, *p;
X if ( in_new_button ) {
X in_new_button = 0;
X return;
X }
X save_card (current);
X c = make_card (NULL);
X if (c == NULL_CARD) {
X msg ("Can't allocate space for a new card");
X return;
X }
X p = current; /* insert after current */
X p = insert_card (c, p); /* insert c after p */
X need_save = TRUE; /* Things have changed */
X set_slider_max (renumber (first)); /* update slider range */
X show_card (p);
X}
X
Xstatic void new_card_before(item, event)
X Panel_item item;
X Event *event;
X{
X struct card *c, *p;
X if ( in_new_button ) {
X in_new_button = 0;
X return;
X }
X save_card (current);
X c = make_card (NULL);
X if (c == NULL_CARD) {
X msg ("Can't allocate space for a new card");
X return;
X }
X p = current->c_prev; /* c_prev may be NULL */
X p = insert_card (c, p); /* insert c after p */
X need_save = TRUE; /* Things have changed */
X set_slider_max (renumber (first)); /* update slider range */
X show_card (p);
X}
X
X/*ARGSUSED*/
Xstatic void new_button (item, event)
X Panel_item item;
X Event *event;
X{
X
X if ( event_action(event) == ACTION_MENU )
X return;
X switch (value_from_mask (event)) {
X case SHIFT_CLICK: /* click+shift, insert before */
X new_card_before(item, event);
X break;
X
X default:
X new_card_after(item, event);
X break;
X }
X in_new_button = 1;
X}
X
X
X/*
X * Notification proc for the "Delete" button. A plain click deletes
X * the current card and pushes it on the deleted stack. Shift and
X * Control key modifiers undelete the card on the top of the stack.
X * The undelete operations via the menu are special for the delete
X * button. Picking either undelete operation from the menu does not
X * wind up as a call to this function. Those operations are entirely
X * handled by the menu code via action and gen procs. See the menu
X * procs in cards.c.
X */
X
Xstatic int in_delete_button;
Xstatic void delete_button_delete (item, event)
XPanel_item item;
XEvent *event;
X{
X struct card *p;
X if ( in_delete_button ) {
X in_delete_button = 0;
X return;
X }
X if (first == last) {
X /* if we're deleting the last card, add a dummy */
X (void) insert_card (make_card (NULL), NULL_CARD);
X }
X
X /* and the new current card will be... */
X save_card(current);
X p = (current == last) ? current->c_prev : current->c_next;
X delete_card (current);
X need_save = TRUE;
X set_slider_max( renumber(first));
X if ( !do_bozo(panel,p) )
X show_card(p);
X}
X
Xstatic void delete_button_undelete_before (item, event)
XPanel_item item;
XEvent *event;
X{
X struct card *p;
X if ( in_delete_button ) {
X in_delete_button = 0;
X return;
X }
X save_card (current);
X p = undelete_card (current->c_prev); /* can be nil */
X need_save = TRUE;
X set_slider_max (renumber (first));
X if ( !do_bozo(panel,p) )
X show_card(p);
X}
X
Xstatic void delete_button_undelete (item, event)
XPanel_item item;
XEvent *event;
X{
X struct card *p;
X if ( in_delete_button ) {
X in_delete_button = 0;
X return;
X }
X save_card (current);
X p = undelete_card (current);
X need_save = TRUE;
X set_slider_max (renumber (first));
X if ( !do_bozo(panel,p) )
X show_card(p);
X}
X
X/*ARGSUSED*/
Xstatic void delete_button (item, event)
XPanel_item item;
XEvent *event;
X{
X if ( event_action(event) == ACTION_MENU )
X return;
X
X
X switch (value_from_mask (event)) {
X case PLAIN_CLICK:
X delete_button_delete(item, event);
X break;
X
X case SHIFT_CLICK:
X delete_button_undelete(item, event);
X break;
X
X case CTRL_CLICK:
X delete_button_undelete_before(item, event);
X break;
X
X default:
X no_comprendo (event, "delete_button");
X return;
X }
X in_delete_button = 1;
X}
X
X/*
X * Notification proc for the "File" button.
X */
X
Xstatic int in_file_button;
X
Xstatic void file_button_save (item, event)
XPanel_item item;
XEvent *event;
X{
X if ( in_file_button ) {
X in_file_button = 0;
X return;
X }
X save_card (current);
X dump_rolo (first, rolofile);
X}
X
Xstatic void file_button_reload (item, event)
XPanel_item item;
XEvent *event;
X{
X if ( in_file_button ) {
X in_file_button = 0;
X return;
X }
X save_card (current);
X if ((need_save == TRUE) && (verify_no_save () == FALSE)) {
X return;
X }
X nuke_active_cards ();
X init_rolo (rolofile);
X}
X
Xstatic void file_button_sort (item, event)
XPanel_item item;
XEvent *event;
X{
X if ( in_file_button ) {
X in_file_button = 0;
X return;
X }
X save_card (current);
X sort_cards (FALSE);
X need_save = TRUE;
X}
X
Xstatic void file_button_sort_backwards (item, event)
XPanel_item item;
XEvent *event;
X{
X if ( in_file_button ) {
X in_file_button = 0;
X return;
X }
X save_card (current);
X sort_cards (TRUE);
X need_save = TRUE;
X}
X
Xstatic void file_button_load (item, event)
XPanel_item item;
XEvent *event;
X{
X char *filename;
X if ( in_file_button ) {
X in_file_button = 0;
X return;
X }
X save_card (current);
X filename = get_selection ();
X if (filename == NULL) {
X msg ("No active selection, need a filename to load from");
X return;
X }
X if (filename_ok (filename) == FALSE) {
X return;
X }
X if ((need_save == TRUE) && (verify_no_save () == FALSE)) {
X return;
X }
X read_rolo (filename);
X}
X
Xstatic void file_button_save_to_file (item, event)
XPanel_item item;
XEvent *event;
X{
X char *filename;
X if ( in_file_button ) {
X in_file_button = 0;
X return;
X }
X save_card (current);
X filename = get_selection ();
X if (filename == NULL) {
X msg ("No active selection, need a filename to save to");
X return;
X }
X if (filename_ok (filename) == FALSE) {
X return;
X }
X write_rolo (filename);
X}
X
X/*ARGSUSED*/
Xstatic void file_button (item, event)
XPanel_item item;
XEvent *event;
X{
X char *filename;
X
X if ( event_action(event) == ACTION_MENU )
X return;
X
X switch (value_from_mask (event)) {
X case PLAIN_CLICK: /* Plain save */
X file_button_save(item, event);
X break;
X
X case SHIFT_CLICK: /* reload, no save first */
X file_button_reload(item, event);
X break;
X
X case CTRL_CLICK: /* sort ascending */
X file_button_sort(item, event);
X break;
X
X case CTRL_SHIFT_CLICK: /* sort descending */
X file_button_sort_backwards(item, event);
X break;
X
X case META_CLICK: /* load named file */
X file_button_load(item, event);
X break;
X
X case META_SHIFT_CLICK: /* store to named file */
X file_button_save_to_file(item, event);
X break;
X
X default: /* say what? */
X no_comprendo (event, "file_button");
X return;
X }
X in_file_button = 1;
X}
X
X
X/*
X * Notification proc for the "Done" button. This covers both closing
X * the tool and exiting. (The checkered flag button is supposed to
X * convey the idea of "finished". Yeah, I know, you got a better idea?)
X */
X
Xstatic int in_done_button;
Xstatic void done_n_save (item, event)
XPanel_item item;
XEvent *event;
X{
X if (in_done_button) {
X in_done_button = 0;
X return;
X }
X save_card (current);
X if (need_save) {
X dump_rolo (first, rolofile);
X }
X xv_set (frame, FRAME_CLOSED, TRUE, 0);
X}
X
Xstatic void done_n_save_exit (item, event)
XPanel_item item;
XEvent *event;
X{
X if (in_done_button) {
X in_done_button = 0;
X return;
X }
X save_card (current);
X if (need_save) {
X dump_rolo (first, rolofile);
X }
X xv_destroy_safe (frame);
X}
X
Xstatic void done_n_exit (item, event)
XPanel_item item;
XEvent *event;
X{
X if (in_done_button) {
X in_done_button = 0;
X return;
X }
X save_card (current);
X if (need_save && (verify_no_save () == FALSE)) {
X return;
X }
X need_save = FALSE;
X xv_destroy_safe (frame);
X}
X
Xstatic void done_button (item, event)
X Panel_item item;
X Event *event;
X{
X if ( event_action(event) == ACTION_MENU )
X return;
X
X switch (value_from_mask (event)) {
X case SHIFT_CLICK: /* save cards and exit */
X done_n_save_exit(item,event);
X break;
X
X case CTRL_CLICK: /* don't save and exit */
X done_n_exit(item,event);
X break;
X
X default: /* save and go iconic */
X done_n_save(item,event);
X return;
X }
X
X in_done_button = 1;
X xv_destroy_safe (frame);
X}
X
X
X/*
X * Notification proc for both the "Find" button and text item for
X * the search pattern. This proc will be called from the pattern item
X * if you type return. If this proc is called, and there is an active
X * selection, that will be used as the search pattern. The selection
X * will also be inserted into the pattern item if it is currently empty.
X */
X
Xstatic int in_find_button;
Xstatic char *e, regbuf [MAX_SELN_LEN];
X
Xinit_find_button()
X{
X static int bozo = 0;
X save_card (current);
X
X /* if selection active, use it */
X if ((e = get_selection ()) != NULL) {
X char *pe = (char *) xv_get(regex_item, PANEL_VALUE);
X
X (void) strcpy (regbuf, e);
X /* if panel item is empty, copy selection into it */
X/* if ((pe == NULL) || (strlen (pe) == 0)) { /* */
X xv_set (regex_item, PANEL_VALUE, regbuf, 0);
X/* } /* */
X } else {
X /* else use panel value */
X (void) strcpy (regbuf, (char *) xv_get(regex_item, PANEL_VALUE));
X }
X
X if (strlen (regbuf) == 0) {
X if (bozo) {
X msg ("Enter an expression to search for");
X } else {
X window_bell (panel);
X bozo++;
X }
X return(0);
X }
X
X bozo = 0;
X e = re_comp (regbuf);
X if (e != NULL) {
X msg ("Regular Expression error: %s", e);
X return(0);
X }
X return(1);
X}
X
Xstatic void find_button_forward (item, event)
XPanel_item item;
XEvent *event;
X{
X struct card *p;
X if ( in_find_button ) {
X in_find_button = 0;
X return;
X }
X if ( !init_find_button() )
X return;
X p = (current == last) ? first : current->c_next;
X while (p != current) {
X if (re_exec (p->c_text) == 1) {
X show_card (p);
X return;
X }
X p = (p == last) ? first : p->c_next;
X }
X if (re_exec (p->c_text) != 1) /* wrapped back to current */
X window_bell (panel);
X}
X
Xstatic void find_button_reverse (item, event)
XPanel_item item;
XEvent *event;
X{
X struct card *p;
X if ( in_find_button ) {
X in_find_button = 0;
X return;
X }
X if ( !init_find_button() )
X return;
X p = (current == first) ? last : current->c_prev;
X while (p != current) {
X if (re_exec (p->c_text) == 1) {
X show_card (p);
X return;
X }
X p = (p == first) ? last : p->c_prev;
X }
X if (re_exec (p->c_text) != 1) /* wrapped back to current */
X window_bell (panel);
X}
X
X/*ARGSUSED*/
Xstatic void find_button (item, event)
XPanel_item item;
XEvent *event;
X{
X if ( event_action(event) == ACTION_MENU )
X return;
X
X if (value_from_mask(event) == SHIFT_CLICK) { /* search backwards */
X find_button_reverse(item,event);
X } else {
X find_button_forward(item,event);
X }
X in_find_button = 1;
X}
X
X
X/*
X * Notification proc for the "List" button. The text window is used
X * to display the first non-blank line of each card.
X */
X
X/*ARGSUSED*/
Xstatic
Xvoid
Xlist_button (item, event)
X Panel_item item;
X Event *event;
X{
X struct card *p;
X
X save_card (current); /* save off pending changes */
X
X textsw_reset (rolocard, 0, 0); /* clear the text window */
X
X for (p = first; p != NULL_CARD; p = p->c_next) {
X char *nl;
X char line_buf [MAX_INDEX_LINE + 2];
X
X (void) sprintf (line_buf, "%d: ", p->c_num);/* prepend number */
X textsw_insert (rolocard, line_buf, strlen (line_buf));
X
X (void) strncpy (line_buf, first_char (p->c_text),
X MAX_INDEX_LINE);
X line_buf [MAX_INDEX_LINE] = 0; /* make sure it's terminated */
X
X (void) strcat (line_buf, "\n"); /* make sure of newline */
X nl = index (line_buf, '\n');
X *++nl = '\0'; /* chop at first line break */
X textsw_insert (rolocard, line_buf, strlen (line_buf));
X }
X
X xv_set (rolocard, TEXTSW_INSERTION_POINT, 0, 0); /* rewind */
X textsw_normalize_view (rolocard, 0);
X
X current = NULL_CARD; /* indicate no card displayed */
X update_num_display (LISTALLCARDS);
X}
X
X
X
X/*
X * Notification proc for the "Help" button. The text window is used to
X * display a help message. The text of the help message is defined
X * in help.h as an array of string pointers (there is too much text to
X * be parsed as one long string token).
X */
X
X/*ARGSUSED*/
Xstatic
Xvoid
Xhelp_button (item, event)
X Panel_item item;
X Event *event;
X{
X int i;
X
X save_card (current); /* capture any pending mods */
X
X textsw_reset (rolocard, 0, 0); /* empty the window */
X
X /* insert the help strings into the window in order */
X for (i = 0; i < sizeof (help_msg) / sizeof (char *); i++) {
X textsw_insert (rolocard, help_msg [i], strlen (help_msg [i]));
X }
X
X xv_set (rolocard, TEXTSW_INSERTION_POINT, 0, 0); /* rewind */
X textsw_normalize_view (rolocard, 0);
X
X current = NULL_CARD; /* indicate no card displayed */
X update_num_display (HELPDISPLAYED); /* set title bar */
X}
X
X
X/*
X * Notification proc for the slider item on the panel. This one's easy.
X */
X
X/*ARGSUSED*/
Xstatic
Xvoid
Xslider_proc (item, value, event)
X Panel_item item;
X int value;
X Event *event;
X{
X save_card (current);
X goto_card (value);
X}
X
X/* ----------------------------------------------------------------------- */
X
X/* Utility procs called by the event handlers above */
X
X
X/*
X * Set the range on the slider. Lower bound is always 1, upper bound
X * is set to the argument i. The slider size is adjusted according to
X * how many digits it takes to represent the value. This looks a bit
X * goofy because of the way the ATTR_COLS() macro works. These macros
X * do NOT return an integer which represents a number of pixels, they
X * encode a value in the high bits which is interpreted later inside
X * the library code. This means you cannot say:
X * x = panel_width - ATTR_COLS(n);
X * See the macro definitions in <sunwindow/attr.h>
X */
X
Xvoid
Xset_slider_max (i)
X int i;
X{
X int delta = 6; /* space for 3 digits */
X
X if (i < 100) {
X delta = 5; /* space for 2 digits */
X }
X
X if (i < 10) {
X delta = 4; /* space for 1 digit */
X }
X
X xv_set (slider_item,
X PANEL_MIN_VALUE, 1,
X PANEL_MAX_VALUE, i,
X/* PANEL_SLIDER_WIDTH, panel_width - 15 + xv_cols (-delta), */
X 0);
X}
X
X
X/*
X * Display the card pointed to by p. Reset the text window, which
X * clears it. Insert the text of the card into the window, then
X * roll it back to the beginning. After displaying the text of
X * the card, update the display items on the panel and set the global
X * current pointer to point at this card.
X */
X
Xvoid
Xshow_card (p)
X struct card *p;
X{
X textsw_reset (rolocard, 0, 0);
X textsw_insert (rolocard, p->c_text, strlen (p->c_text));
X xv_set (rolocard, TEXTSW_INSERTION_POINT, 0, 0);
X textsw_normalize_view (rolocard, 0);
X update_num_display (p->c_num);
X current = p;
X}
X
X
X/*
X * Change to the card with the given index number. The number is
X * range checked, then searched for. When found, the pointer to the
X * card struct is passed to the function which actually changes
X * to the new card.
X */
X
Xstatic
Xvoid
Xgoto_card (i)
X int i;
X{
X struct card *p, *q;
X
X if ((i < 1) || (i > last->c_num)) {
X msg ("Sorry, don't have a card #%d", i);
X show_card (first); /* show something we're sure of */
X return; /* unlikely to happen */
X }
X
X /* reduce the search space a bit */
X if ((current != NULL_CARD) && (i >= current->c_num)) {
X q = current;
X } else {
X q = first;
X }
X
X for (p = q; p != NULL_CARD; p = p->c_next) {
X if (p->c_num == i) {
X show_card (p);
X return;
X }
X }
X
X msg ("Unexpected inconsistency, couldn't find card #%d", i);
X}
X
X
X/*
X * Save off the contents of the card being displayed in the text window.
X * If the data in the window has been modified by the user, we need to
X * replace the data in the card struct with the contents of the window.
X * Since there isn't a way to load some data into the window as an
X * initial value and mark it as "clean", we need to use a brute force
X * method to determine if any changes have been made. We copy out
X * the contents of the window and compare it to the data we inserted
X * in the first place. If they are the same, no change was made and
X * there is nothing to do. If they are different, we throw away the
X * old contents and stash the pointer to the new in the card struct.
X */
X
Xsave_card (p)
X struct card *p;
X{
X int red, len;
X char *c;
X
X if (p == NULL_CARD) {
X /*
X * If nil, the text window is being used for an index list
X * or help message. If so, we know there is no card data
X * to be saved, and that some button has been clicked. So
X * at this point we'll redisplay the last card that was
X * displayed. The slider item should still remember which
X * one it was.
X */
X goto_card ((int) xv_get (slider_item, PANEL_VALUE));
X return;
X }
X
X len = (int) xv_get (rolocard, TEXTSW_LENGTH);
X c = malloc (len + 1);
X red = (int) xv_get (rolocard, TEXTSW_CONTENTS, 0, c, len);
X if (red != len) {
X fprintf (stderr, "rolo: fetch error: red=%d, len=%d\n",
X red, len);
X return;
X }
X
X c [len] = '\0';
X if (strcmp (c, p->c_text) == 0) {
X free (c); /* didn't change */
X } else {
X free (p->c_text); /* changed and must save */
X p->c_text = c;
X need_save = TRUE;
X }
X}
X
X
X/*
X * Update the displayed information which indicates which card is
X * currently being displayed. Provisions are made for the special
X * cases where the help message or index list is being displayed.
X * The tool name stripe and the slider value are set to indicate
X * the number of the current card.
X */
X
Xupdate_num_display (i)
X int i;
X{
X char buf [MAXPATHLEN + 20]; /* worst case */
X
X switch (i) {
X case LISTALLCARDS:
X (void) sprintf (buf, "%s - %s (First lines of all %d cards)",
X NAME, rolofile, last->c_num);
X break;
X
X case HELPDISPLAYED:
X (void) sprintf (buf, "%s - %s (Help Message)",
X NAME, rolofile);
X break;
X
X default:
X xv_set (slider_item, PANEL_VALUE, i, 0);
X (void) sprintf (buf, "%s - %s (Card %d of %d)",
X NAME, rolofile, i, last->c_num);
X break;
X }
X
X set_stripe (buf);
X}
X
X
X/*
X * Ask the user if they're really sure they don't want to save the
X * changes they've made. This function only really exists because
X * the same question is asked in two places (I really hate duplicating
X * code).
X */
X
Xverify_no_save ()
X{
X return (confirm (
X "You have made changes which will be lost. Are you sure?"));
X}
X
X
X/*
X * Complain about an unknown shiftmask in a click event.
X */
X
Xstatic
Xvoid
Xno_comprendo (event, p)
X Event *event;
X char *p;
X{
X char buf [100];
X
X (void) sprintf (buf, " %s: Huh? Don't understand click value %d",
X p, value_from_mask (event));
X msg ("%s", buf);
X}
X
X
X/*
X * Make sure a filename is reasonable. This is important because the
X * filename is provided via the selection mechanism, which could contain
X * all manner of gibberish.
X */
X
Xstatic
Xint
Xfilename_ok (p)
X char *p;
X{
X char *q, *bad_chars = "!^&*()|~`{}[]:;\\\"'<>?";
X
X for (q = p; *q != '\0'; q++) {
X if (( ! isgraph (*q)) || (index (bad_chars, *q) != NULL)) {
X msg ("Sorry, that looks like a bad filename to me");
X return (FALSE);
X }
X }
X
X return (TRUE);
X}
X
X
X/*
X * Get the primary selection, copy it into a static buffer, up to a
X * maximum, and return a pointer to it. Return NULL if there is no
X * current primary selection to get.
X */
X
Xstatic
Xchar *
Xget_selection()
X{
X Seln_holder holder;
X Seln_request *buffer;
X static char sel_text [MAX_SELN_LEN + 1];
X static notfirsttime = 0;
X Xv_server server = (Xv_server)xv_get(xv_get(frame, XV_SCREEN), SCREEN_SERVER);
X
X holder = selection_inquire (server, SELN_PRIMARY);
X buffer = selection_ask (server, &holder, SELN_REQ_CONTENTS_ASCII, NULL, NULL);
X
X (void) strcpy (sel_text, buffer->data + sizeof (SELN_REQ_CONTENTS_ASCII));
X
X if (strlen (sel_text) == 0 || !notfirsttime++) {
X /* empty string is no sel. */
X return (NULL);
X }
X
X return (sel_text);
X}
X
X/* ----------------------------------------------------------------------- */
X
X/* Panel event dispatcher, trap menu button events */
X
X
X/*
X * Convert a value representing a menu item into the corresponding
X * event shift mask for faking a button click. The menu value, which
X * is 1-relative, is decremented by one, then the lowest three bits
X * are examined and the corresponding shiftmask bits are set.
X */
X
X/*
X * Given an event, translate it into an integer number representing
X * which logical choice it is. The value returned will be in the
X * range 0-7, made up of the three possible bits representing the
X * states of the SHIFT, CONTROL and META shift keys. The effect is
X * that the number returned by this function will be one less than
X * the value initially passed into mask_from_menu_value().
X */
X
Xstatic
Xint
Xvalue_from_mask (event)
X Event *event;
X{
X int value = 0;
X
X /*
X * These macros don't return TRUE or FALSE, exactly. They return
X * the corresponding bits from the shiftmask. For example, the
X * value of event_ctrl_is_down() is 0x30 if the control key was
X * down. That means you can't compare the result to TRUE.
X */
X
X if (event_shift_is_down (event) != 0) {
X value |= 1;
X }
X
X if (event_ctrl_is_down (event) != 0) {
X value |= 2;
X }
X
X if (event_meta_is_down (event) != 0) {
X value |= 4;
X }
X
X return (value);
X}
X
X/* ----------------------------------------------------------------------- */
X
X/* Interposer functions watching for frame events */
X
X
X/*
X * Interposer proc for catching window resize events. We're a little
X * bit fascist here and insist that the frame remain at least big
X * enough to display the whole panel and at least three lines of the
X * text window.
X */
X
X#define MIN_CARD_ROWS 3
X
XNotify_value
Xcatch_resize (frame, event, arg, type)
X Frame frame;
X Event *event;
X Notify_arg arg;
X Notify_event_type type;
X{
X Panel panel;
X int width;
X int height;
X int card_height;
X int frame_height;
X Notify_value value;
X
X value = notify_next_event_func (frame, event, arg, type);
X
X if (event_id (event) != WIN_RESIZE) {
X return (value);
X }
X
X if ((int) xv_get (frame, FRAME_CLOSED) == TRUE) {
X return (value);
X }
X
X panel = (Panel) xv_get (frame, FRAME_NTH_SUBWINDOW, 1);
X
X
X width = (int) xv_get (panel, XV_WIDTH);
X
X if (width < panel_width) {
X xv_set (panel, XV_WIDTH, panel_width, 0);
X window_fit_width (frame);
X }
X
X
X height = (int) xv_get (panel, XV_HEIGHT);
X
X if (height < panel_height) {
X xv_set (panel, XV_HEIGHT, panel_height, 0);
X window_fit_height (frame);
X }
X
X card_height = (int) xv_get (rolocard, WIN_ROWS);
X if (card_height < MIN_CARD_ROWS) {
X xv_set (rolocard, WIN_ROWS, MIN_CARD_ROWS, 0);
X window_fit_height (frame);
X }
X
X /*
X * This catches cases where the subwindows are completely clipped
X * and don't shrink as far as the frame is concerned.
X */
X card_height = (int) xv_get (rolocard, XV_HEIGHT);
X frame_height = (int) xv_get (frame, XV_HEIGHT);
X if (frame_height < (panel_height + card_height)) {
X window_fit_height (frame);
X }
X
X return (value);
X}
X
X
X/*
X * Interposer function to catch destroy events. We want to know when
X * the tool about to be destroyed so that we can save any changes to the
X * cards back out to disk. This gets a little tricky because the
X * text edit window will veto a tool destroy (selecting Quit from
X * the tool menu) if there is any text in the window. This is because
X * from its point of view the text buffer has been modified because we
X * inserted the initial contents of the card. The way we get around
X * this is by saving the contents of the window, resetting it, calling
X * the rest of the notification chain, then restoring the contents of
X * the window. The destroy proc for the text window is called in that
X * notification chain and it will see an empty window and not object to
X * the tool being destroyed. If this function is called again with
X * a notification flag other than DESTROY_CHECKING, that means the tool
X * is really going away (either the user OKed a quit or suntools is
X * shutting down). In that case we write the cards back out if they
X * have been changed, without any fancy footwork.
X */
X
XNotify_value
Xrolo_destroy (frame, status)
X Frame frame;
X Destroy_status status;
X{
X if (status == DESTROY_CHECKING) {
X Notify_value s;
X
X save_card (current);
X textsw_reset (rolocard, 0, 0); /* fake out textedit */
X s = notify_next_destroy_func (frame, status);
X show_card (current);
X return (s);
X }
X
X save_card (current);
X if (need_save) {
X dump_rolo (first, rolofile);
X }
X
X return (notify_next_destroy_func (frame, status));
X}
X
END_OF_FILE
if test 37272 -ne `wc -c <'panel.c'`; then
echo shar: \"'panel.c'\" unpacked with wrong size!
fi
# end of 'panel.c'
fi
echo shar: End of archive 3 \(of 3\).
cp /dev/null ark3isdone
MISSING=""
for I in 1 2 3 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked all 3 archives.
rm -f ark[1-9]isdone
else
echo You still need to unpack the following archives:
echo " " ${MISSING}
fi
## End of shell archive.
exit 0
dan
----------------------------------------------------
O'Reilly && Associates argv at sun.com / argv at ora.com
Opinions expressed reflect those of the author only.
More information about the Comp.sources.x
mailing list