/* $Id: infowindows.c,v 10.1 92/10/06 22:58:20 ca Exp $ */ #include #include #include "sim.h" #include "simx.h" #include "xtables.h" /* This file contains the code for creating, destroying, and painting information windows for a component. An information window contains a list of all the component's parameters (that are of any use to the user) and their values. */ /* This routine accepts a poniter to a componenet and puts up an info_window next to it. It returns 0 if an error occurs.*/ Status pop_info_window(scomponent) COMPONENT *scomponent; /* A pointer to the component's data structure */ { unsigned num_params_display = 0; int p_location; int xcoord, ycoord; int window_height; XWindowAttributes comp_info; Window info_wind; XCOMPONENT *xcomponent1, *xcomponent2; PARAM *parameter; /* Don't pop up an infowindow if there already is one */ if (scomponent->co_menu_up == TRUE) { return (-1); } xcomponent1 = (XCOMPONENT *) scomponent->co_picture; /* First go through the parameters for the component */ for (parameter = (PARAM *)scomponent->co_params->q_head; parameter; parameter = parameter->p_next) { if (parameter->p_flags & DisplayMask) num_params_display++; } /* If there are not any parameters to be diplayed, don't do anything and return -1. */ if (num_params_display == 0) { return (-1); } /* Allocate space for the xcomponent */ xcomponent2 = (XCOMPONENT *) sim_malloc(sizeof(XCOMPONENT)); if (!xcomponent2) { return (-1); } xcomponent2->scomponent = scomponent; xcomponent2->comp_window = xcomponent1->comp_window; xcomponent2->which_one = INFO_WINDOW; /* Allocate space for the pointer list */ xcomponent1->p_list = (PARAM **) sim_calloc(num_params_display, sizeof(PARAM *)); xcomponent2->p_list = xcomponent1->p_list; xcomponent2->num_params = xcomponent1->num_params = num_params_display; /* Now actually put parameter pointers in p_list equal to their values */ p_location = 0; for (parameter = (PARAM *)scomponent->co_params->q_head; parameter; parameter = parameter->p_next) { if (parameter->p_flags & DisplayMask) xcomponent1->p_list[p_location++] = parameter; } window_height = num_params_display * the_environment.info_window_height; /* Next, find x and y coordinates, this will be messy */ /* First, get x value */ if (xcomponent1->x < the_environment.x_center) xcoord = xcomponent1->x + xcomponent1->width + the_environment.info_window_horizontal_padding; else xcoord = xcomponent1->x - the_environment.info_window_horizontal_padding - the_environment.info_window_width; /* Next, get y value */ if (xcomponent1->y < the_environment.y_center) ycoord = xcomponent1->y + the_environment.component_window_height + the_environment.info_window_horizontal_padding; else ycoord = xcomponent1->y - the_environment.info_window_horizontal_padding - window_height; /* Create and map the window */ info_wind = XCreateSimpleWindow(the_environment.the_display, the_environment.back_window, xcoord, ycoord, the_environment.info_window_width, window_height, the_environment.border_width, the_environment.fore_color, the_environment.component_color[scomponent->co_type]); XSelectInput(the_environment.the_display, info_wind, ExposureMask); XMapWindow(the_environment.the_display, info_wind); xcomponent2->back_gc = the_environment.component_back_gc[scomponent->co_type]; xcomponent1->back_gc = xcomponent2->back_gc; xcomponent1->info_window = info_wind; xcomponent2->info_window = info_wind; xcomponent2->x = xcoord; xcomponent2->y = ycoord; xcomponent2->width = the_environment.info_window_width; xcomponent2->height = window_height; /* Now put an entry in comp_xtable for the infowindow */ XSaveContext(the_environment.the_display, info_wind, comp_xtable, (caddr_t)xcomponent2); /* Set flag for infowindow on */ scomponent->co_menu_up = TRUE; /* Return status */ return (OK); } /* Simple procedure to draw the text for a line of an infowindow. Optionally clears the area where the line was first. */ #ifdef INLINE /* Make it inline so that it will be fast for calls in this file. */ inline #endif draw_info_text(xcomponent, position, clear_first_p) XCOMPONENT *xcomponent; int position; int clear_first_p; { int x = the_environment.meter_indicator_width * 2; int y = position * the_environment.info_window_height; PARAM *p = xcomponent->p_list[position]; char *s = (*p->p_make_text)(xcomponent->scomponent, xcomponent->p_list[position]); XTextItem xtext; /* Clear the area where the text was. */ if (clear_first_p) XFillRectangle(the_environment.the_display, xcomponent->info_window, xcomponent->back_gc, x + 1, y + 1, the_environment.info_window_width - x, the_environment.info_window_height - 1); /* And paint the new text there. */ xtext.chars = s; xtext.nchars = strlen(s); xtext.font = the_environment.info_font; xtext.delta = 0; XDrawText(the_environment.the_display, xcomponent->info_window, the_environment.the_gc, x + 6, y + the_environment.info_window_vertical_padding, &xtext, 1); } /* This rountine takes a component's data structure and writes the paramters' values in the component's info_window */ paint_info_window(scomponent, xcomponent, ev) COMPONENT *scomponent; /* the component's data structure */ XCOMPONENT *xcomponent; XExposeEvent *ev; { PARAM *parameter; int x, y; char *info_string; short i; unsigned long color; int start_param, end_param; int do_meter_indicator, do_log_indicator, do_text; /* Get the component's x-information */ if (xcomponent == NULL) xcomponent = (XCOMPONENT *) scomponent->co_picture; if (xcomponent->info_window == (int) NULL) return; /* Clear the window to its background color */ /* No, don't--this is very annoying when only a small part needs to be redrawn because of an expose event. */ /* XClearWindow(the_environment.the_display, xcomponent->info_window);*/ /* If this is an expose event, figure out which parts of the infowindow need to be redrawn, else redraw everything. */ do_text = do_meter_indicator = do_log_indicator = FALSE; if (ev) { if (ev->x + ev->width > 2 * the_environment.meter_indicator_width) do_text = TRUE; if (ev->x < the_environment.meter_indicator_width) do_meter_indicator = TRUE; if (ev->x <= 2 * the_environment.meter_indicator_width && ev->x + ev->width >= the_environment.meter_indicator_width) do_log_indicator = TRUE; /* Figure out which of the params we need to do. */ start_param = ev->y / the_environment.info_window_height; end_param = (ev->y + ev->height) / the_environment.info_window_height; if (end_param >= xcomponent->num_params) end_param = xcomponent->num_params - 1; } else { start_param = 0; end_param = xcomponent->num_params - 1; do_text = do_meter_indicator = do_log_indicator = TRUE; } /* Quit now if there is nothing to do. */ if ( ! (do_text || do_meter_indicator || do_log_indicator) ) return; /* Set initial values for x and y. */ x = the_environment.meter_indicator_width * 2; y = start_param * the_environment.info_window_height; /* Loop through each parameter (Note that end_param is included in the loop)*/ for (i = start_param; i <= end_param; ++i) { /* Get the parameter. */ parameter = xcomponent->p_list[i]; /* Get the textstring and draw it at x,y */ if (do_text) draw_info_text(xcomponent, i, FALSE); /* Fill meter window with correct color. Note the conditional expression used to pick the gc to be used in both of the next statements. */ if (do_meter_indicator && (parameter->p_flags & MeterMask)) { XFillRectangle(the_environment.the_display, xcomponent->info_window, the_environment.meter_or_log_indicator_gc, 0, (y > 0 ? y+1 : 0), the_environment.meter_indicator_width-1, the_environment.info_window_height - (y > 0 ? 1 : 0)); } /* Fill logging window with correct color */ if (do_log_indicator && (parameter->p_flags & LogMask)) { XFillRectangle(the_environment.the_display, xcomponent->info_window, the_environment.meter_or_log_indicator_gc, the_environment.meter_indicator_width, (y > 0 ? y+1 : 0), the_environment.meter_indicator_width, the_environment.info_window_height - (y > 0 ? 1 : 0)); } /* Draw the horizontal line. */ if (i > 0) XDrawLine(the_environment.the_display, xcomponent->info_window, the_environment.the_gc, 0, y, the_environment.info_window_width, y); /* Increment Y */ y += the_environment.info_window_height; } /* Draw the two vertical lines if necessary. */ if (do_meter_indicator) XDrawLine(the_environment.the_display, xcomponent->info_window, the_environment.the_gc, the_environment.meter_indicator_width - 1, 0, the_environment.meter_indicator_width - 1, y); if (do_log_indicator) XDrawLine(the_environment.the_display, xcomponent->info_window, the_environment.the_gc, x, 0, x, y); return; } /* This routine accpets a pointer to a component. It then destroys the information window for that component. It returns -1 if the information window does not exist. */ Status unpop_info_window(scomponent) COMPONENT *scomponent; /* Pointer to the component */ { Window info_wind; /* Window id# for the information window */ XCOMPONENT *xcomponent; /* pointer to the xcomponent structure of the information window */ xcomponent = (XCOMPONENT *) scomponent->co_picture; /* return NULL if there is no info window */ if (!(info_wind = xcomponent->info_window)) return (-1); /* Delete info window, delete its entry in comp_xtable, and free its xcomponent's memory */ xcomponent->info_window = (int) NULL; xcomponent->num_params = 0; free ((char *) xcomponent->p_list); XFindContext(the_environment.the_display, info_wind, comp_xtable, (caddr_t *) &xcomponent); XDestroyWindow(the_environment.the_display, info_wind); XDeleteContext(the_environment.the_display, info_wind, comp_xtable); free((char *)xcomponent); XFlush(the_environment.the_display); scomponent->co_menu_up = FALSE; return (TRUE); } infowindow_button_event_handler(xcomponent, bevent, c_m_r) XCOMPONENT *xcomponent; XButtonPressedEvent *bevent; int c_m_r; { XWindowAttributes xinfo; int position; int x,y; /* XGetWindowAttributes(the_environment.the_display, xcomponent->info_window, &xinfo);*/ position = (int) ((bevent->y - xcomponent->y - the_environment.border_width) / the_environment.info_window_height); if (position >= xcomponent->num_params) position = xcomponent->num_params - 1; /* Now see what the user wants to do */ if ((bevent->button == user_bindings.move.button) && (bevent->state == user_bindings.move.key) && (c_m_r == 1)) { x = bevent->x; y = bevent->y; move_window(&x, &y, xcomponent->width, xcomponent->height, x - xcomponent->x, y - xcomponent->y); XMoveWindow(the_environment.the_display, xcomponent->info_window, x, y); xcomponent->x = x; xcomponent->y = y; } else if ((c_m_r == 0) && (bevent->button == user_bindings.select.button) && (bevent->state == user_bindings.select.key)) { if ((bevent->x - xcomponent->x) > (2 * the_environment.meter_indicator_width)) /* User has clicked on text */ { /* User wants to edit a parameter */ /* Ugly kludge to catch the case where the input routine changes the number of parameters that the component has. */ int old_nparams = xcomponent->scomponent->co_params->q_len; edit_parameter(xcomponent->scomponent, xcomponent->p_list[position]); if (old_nparams != xcomponent->scomponent->co_params->q_len) { unpop_info_window(xcomponent->scomponent); pop_info_window(xcomponent->scomponent); } else { /* Clear the area where the text was, and draw the new. */ draw_info_text(xcomponent, position, TRUE); } } else if ((bevent->x - xcomponent->x) < the_environment.meter_indicator_width) { toggle_meter(xcomponent, position); } else { toggle_logging(xcomponent, position); } } }