luis@rice.edu (Luis Soltero) (10/11/90)
Submitted-by: Luis Soltero <luis@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@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@vixen.uucp X * March 1987 (V1.0) hitchens@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@sun.com / argv@ora.com Opinions expressed reflect those of the author only.