scope_vert.c
1 /** This file, 'halsc_vert.c', contains the portion of halscope 2 that deals with vertical stuff - signal sources, scaling, 3 position and such. 4 */ 5 6 /** Copyright (C) 2003 John Kasunich 7 <jmkasunich AT users DOT sourceforge DOT net> 8 */ 9 10 /** This program is free software; you can redistribute it and/or 11 modify it under the terms of version 2 of the GNU General 12 Public License as published by the Free Software Foundation. 13 This library is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public 19 License along with this library; if not, write to the Free Software 20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 22 THE AUTHORS OF THIS LIBRARY ACCEPT ABSOLUTELY NO LIABILITY FOR 23 ANY HARM OR LOSS RESULTING FROM ITS USE. IT IS _EXTREMELY_ UNWISE 24 TO RELY ON SOFTWARE ALONE FOR SAFETY. Any machinery capable of 25 harming persons must have provisions for completely removing power 26 from all motors, etc, before persons enter any danger area. All 27 machinery must be designed to comply with local and national safety 28 codes, and the authors of this software can not, and do not, take 29 any responsibility for such compliance. 30 31 This code was written as part of the EMC HAL project. For more 32 information, go to www.linuxcnc.org. 33 */ 34 35 #include "config.h" 36 #include <locale.h> 37 #include <libintl.h> 38 #define _(x) gettext(x) 39 40 #include <sys/types.h> 41 #include <unistd.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <ctype.h> 45 #include <string.h> 46 47 #include "rtapi.h" /* RTAPI realtime OS API */ 48 #include <rtapi_mutex.h> 49 #include "hal.h" /* HAL public API decls */ 50 #include "../hal_priv.h" /* private HAL decls */ 51 52 #include <gtk/gtk.h> 53 #include <gdk/gdkkeysyms.h> 54 55 #include "miscgtk.h" /* generic GTK stuff */ 56 #include "scope_usr.h" /* scope related declarations */ 57 58 #define BUFLEN 80 /* length for sprintf buffers */ 59 60 /*********************************************************************** 61 * GLOBAL VARIABLES DECLARATIONS * 62 ************************************************************************/ 63 64 #define VERT_POS_RESOLUTION 100.0 65 66 /* The channel select buttons sometimes need to be toggled by 67 the code rather than the user, without causing any action. 68 This global is used for that */ 69 static int ignore_click = 0; 70 71 /*********************************************************************** 72 * LOCAL FUNCTION PROTOTYPES * 73 ************************************************************************/ 74 75 struct offset_data { 76 char buf[BUFLEN]; 77 int ac_coupled; 78 }; 79 80 static void init_chan_sel_window(void); 81 static void init_chan_info_window(void); 82 static void init_vert_info_window(void); 83 84 static gboolean dialog_select_source(int chan_num); 85 static void selection_made(GtkWidget * clist, gint row, gint column, 86 GdkEventButton * event, dialog_generic_t * dptr); 87 static void change_source_button(GtkWidget * widget, gpointer gdata); 88 static void channel_off_button(GtkWidget * widget, gpointer gdata); 89 static void offset_button(GtkWidget * widget, gpointer gdata); 90 static gboolean dialog_set_offset(int chan_num); 91 static void scale_changed(GtkAdjustment * adj, gpointer gdata); 92 static void offset_changed(GtkEditable * editable, struct offset_data *); 93 static void offset_activated(GtkEditable * editable, gchar * button); 94 static void pos_changed(GtkAdjustment * adj, gpointer gdata); 95 static void chan_sel_button(GtkWidget * widget, gpointer gdata); 96 97 /* helper functions */ 98 static void write_chan_config(FILE *fp, scope_chan_t *chan); 99 100 /*********************************************************************** 101 * PUBLIC FUNCTIONS * 102 ************************************************************************/ 103 104 void init_vert(void) 105 { 106 scope_chan_t *chan; 107 int n; 108 109 /* stop sampling */ 110 ctrl_shm->state = IDLE; 111 /* init non-zero members of the vertical structure */ 112 invalidate_all_channels(); 113 /* init non-zero members of the channel structures */ 114 for (n = 1; n <= 16; n++) { 115 chan = &(ctrl_usr->chan[n - 1]); 116 chan->position = 0.5; 117 } 118 /* set up the windows */ 119 init_chan_sel_window(); 120 init_chan_info_window(); 121 init_vert_info_window(); 122 } 123 124 int set_active_channel(int chan_num) 125 { 126 int n, count; 127 scope_vert_t *vert; 128 scope_chan_t *chan; 129 if (( chan_num < 1 ) || ( chan_num > 16 )) { 130 return -1; 131 } 132 vert = &(ctrl_usr->vert); 133 chan = &(ctrl_usr->chan[chan_num - 1]); 134 135 if (vert->chan_enabled[chan_num - 1] == 0 ) { 136 /* channel is disabled, want to enable it */ 137 if (ctrl_shm->state != IDLE) { 138 /* acquisition in progress, must restart it */ 139 prepare_scope_restart(); 140 } 141 count = 0; 142 for (n = 0; n < 16; n++) { 143 if (vert->chan_enabled[n]) { 144 count++; 145 } 146 } 147 if (count >= ctrl_shm->sample_len) { 148 /* max number of channels already enabled */ 149 return -2; 150 } 151 if (chan->name == NULL) { 152 /* no signal source */ 153 return -3; 154 } 155 /* "push" the button in to indicate enabled channel */ 156 ignore_click = 1; 157 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(vert->chan_sel_buttons[chan_num-1]), TRUE); 158 vert->chan_enabled[chan_num - 1] = 1; 159 } 160 if (vert->selected != chan_num) { 161 /* make chan_num the selected channel */ 162 vert->selected = chan_num; 163 channel_changed(); 164 } 165 return 0; 166 } 167 168 int set_channel_off(int chan_num) 169 { 170 scope_vert_t *vert; 171 int n; 172 173 if ((chan_num < 1) || (chan_num > 16)) { 174 return -1; 175 } 176 vert = &(ctrl_usr->vert); 177 if ( vert->chan_enabled[chan_num - 1] == 0 ) { 178 /* channel is already off, nothing to do */ 179 return -1; 180 } 181 vert->chan_enabled[chan_num - 1] = 0; 182 /* force the button to pop out */ 183 ignore_click = 1; 184 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(vert-> 185 chan_sel_buttons[chan_num - 1]), FALSE); 186 if ( chan_num == vert->selected ) { 187 /* channel was selected, pick new selected channel */ 188 n = 0; 189 vert->selected = 0; 190 do { 191 chan_num++; 192 if (chan_num > 16) { 193 chan_num = 1; 194 } 195 if (vert->chan_enabled[chan_num - 1] != 0) { 196 vert->selected = chan_num; 197 } 198 } while ((++n < 16) && (vert->selected == 0)); 199 } 200 channel_changed(); 201 return 0; 202 } 203 204 int set_channel_source(int chan_num, int type, char *name) 205 { 206 scope_vert_t *vert; 207 scope_chan_t *chan; 208 hal_pin_t *pin; 209 hal_sig_t *sig; 210 hal_param_t *param; 211 212 vert = &(ctrl_usr->vert); 213 chan = &(ctrl_usr->chan[chan_num - 1]); 214 /* locate the selected item in the HAL */ 215 if (type == 0) { 216 /* search the pin list */ 217 pin = halpr_find_pin_by_name(name); 218 if (pin == NULL) { 219 /* pin not found */ 220 return -1; 221 } 222 chan->data_source_type = 0; 223 chan->data_source = SHMOFF(pin); 224 chan->data_type = pin->type; 225 chan->name = pin->name; 226 } else if (type == 1) { 227 /* search the signal list */ 228 sig = halpr_find_sig_by_name(name); 229 if (sig == NULL) { 230 /* signal not found */ 231 return -1; 232 } 233 chan->data_source_type = 1; 234 chan->data_source = SHMOFF(sig); 235 chan->data_type = sig->type; 236 chan->name = sig->name; 237 } else if (type == 2) { 238 /* search the parameter list */ 239 param = halpr_find_param_by_name(name); 240 if (param == NULL) { 241 /* parameter not found */ 242 return -1; 243 } 244 chan->data_source_type = 2; 245 chan->data_source = SHMOFF(param); 246 chan->data_type = param->type; 247 chan->name = param->name; 248 } 249 switch (chan->data_type) { 250 case HAL_BIT: 251 chan->data_len = sizeof(hal_bit_t); 252 chan->min_index = -2; 253 chan->max_index = 2; 254 break; 255 case HAL_FLOAT: 256 chan->data_len = sizeof(hal_float_t); 257 chan->min_index = -36; 258 chan->max_index = 36; 259 break; 260 case HAL_S32: 261 chan->data_len = sizeof(hal_s32_t); 262 chan->min_index = -2; 263 chan->max_index = 30; 264 break; 265 case HAL_U32: 266 chan->data_len = sizeof(hal_u32_t); 267 chan->min_index = -2; 268 chan->max_index = 30; 269 break; 270 default: 271 /* Shouldn't get here, but just in case... */ 272 chan->data_len = 0; 273 chan->min_index = -1; 274 chan->max_index = 1; 275 } 276 /* invalidate any data in the buffer for this channel */ 277 vert->data_offset[chan_num - 1] = -1; 278 /* set scale and offset to nominal values */ 279 chan->vert_offset = 0.0; 280 chan->scale_index = 0; 281 /* return success */ 282 return 0; 283 } 284 285 int set_vert_scale(int setting) 286 { 287 scope_vert_t *vert; 288 scope_chan_t *chan; 289 GtkAdjustment *adj; 290 int chan_num, index; 291 double scale; 292 gchar buf[BUFLEN]; 293 294 vert = &(ctrl_usr->vert); 295 chan_num = vert->selected; 296 if ((chan_num < 1) || (chan_num > 16)) { 297 return -1; 298 } 299 chan = &(ctrl_usr->chan[chan_num - 1]); 300 if ((setting > chan->max_index) || (setting < chan->min_index)) { 301 /* value out of range for this data type */ 302 return -1; 303 } 304 /* save new index */ 305 chan->scale_index = setting; 306 /* set scale slider based on new setting */ 307 adj = GTK_ADJUSTMENT(vert->scale_adj); 308 gtk_adjustment_set_value(adj, setting); 309 310 /* compute scale factor */ 311 scale = 1.0; 312 index = chan->scale_index; 313 while (index >= 3) { 314 scale *= 10.0; 315 index -= 3; 316 } 317 while (index <= -3) { 318 scale *= 0.1; 319 index += 3; 320 } 321 switch (index) { 322 case 2: 323 scale *= 5.0; 324 break; 325 case 1: 326 scale *= 2.0; 327 break; 328 case -1: 329 scale *= 0.5; 330 break; 331 case -2: 332 scale *= 0.2; 333 break; 334 default: 335 break; 336 } 337 chan->scale = scale; 338 format_scale_value(buf, BUFLEN - 1, scale); 339 gtk_label_set_text_if(vert->scale_label, buf); 340 if (chan_num == ctrl_shm->trig_chan) { 341 refresh_trigger(); 342 } 343 request_display_refresh(1); 344 return 0; 345 } 346 347 int set_vert_pos(double setting) 348 { 349 scope_vert_t *vert; 350 scope_chan_t *chan; 351 int chan_num; 352 GtkAdjustment *adj; 353 354 /* range check setting */ 355 if (( setting < 0.0 ) || ( setting > 1.0 )) { 356 return -1; 357 } 358 /* point to data */ 359 vert = &(ctrl_usr->vert); 360 chan_num = vert->selected; 361 if ((chan_num < 1) || (chan_num > 16)) { 362 return -1; 363 } 364 chan = &(ctrl_usr->chan[chan_num - 1]); 365 chan->position = setting; 366 /* set position slider based on new setting */ 367 adj = GTK_ADJUSTMENT(vert->pos_adj); 368 gtk_adjustment_set_value(adj, chan->position * VERT_POS_RESOLUTION); 369 /* refresh other stuff */ 370 if (chan_num == ctrl_shm->trig_chan) { 371 refresh_trigger(); 372 } 373 request_display_refresh(1); 374 return 0; 375 } 376 377 int set_vert_offset(double setting, int ac_coupled) 378 { 379 scope_vert_t *vert; 380 scope_chan_t *chan; 381 int chan_num; 382 gchar buf1[BUFLEN + 1], buf2[BUFLEN + 1]; 383 384 /* point to data */ 385 vert = &(ctrl_usr->vert); 386 chan_num = vert->selected; 387 if ((chan_num < 1) || (chan_num > 16)) { 388 return -1; 389 } 390 chan = &(ctrl_usr->chan[chan_num - 1]); 391 /* set the new offset */ 392 chan->vert_offset = setting; 393 chan->ac_offset = ac_coupled; 394 /* update the offset display */ 395 if (chan->data_type == HAL_BIT) { 396 snprintf(buf1, BUFLEN, "----"); 397 } else { 398 if(chan->ac_offset) { 399 snprintf(buf1, BUFLEN, "(AC)"); 400 } else { 401 format_signal_value(buf1, BUFLEN, chan->vert_offset); 402 } 403 } 404 snprintf(buf2, BUFLEN, _("Offset\n%s"), buf1); 405 gtk_label_set_text_if(vert->offset_label, buf2); 406 /* refresh other stuff */ 407 if (chan_num == ctrl_shm->trig_chan) { 408 refresh_trigger(); 409 } 410 request_display_refresh(1); 411 return 0; 412 } 413 414 void format_signal_value(char *buf, int buflen, double value) 415 { 416 char *units; 417 int decimals; 418 char sign, symbols[] = "pnum KMGT"; 419 420 if (value < 0) { 421 value = -value; 422 sign = '-'; 423 } else { 424 sign = '+'; 425 } 426 if (value <= 1.0e-24) { 427 /* pretty damn small, call it zero */ 428 snprintf(buf, buflen, "0.000"); 429 return; 430 } 431 if (value <= 1.0e-12) { 432 /* less than pico units, use scientific notation */ 433 snprintf(buf, buflen, "%c%10.3e", sign, value); 434 return; 435 } 436 if (value >= 1.0e+12) { 437 /* greater than tera-units, use scientific notation */ 438 snprintf(buf, buflen, "%c%10.3e", sign, value); 439 return; 440 } 441 units = &(symbols[4]); 442 while (value < 1.0) { 443 value *= 1000.0; 444 units--; 445 } 446 while (value >= 1000.0) { 447 value /= 1000.0; 448 units++; 449 } 450 decimals = 3; 451 if (value >= 9.999) { 452 decimals--; 453 } 454 if (value >= 99.99) { 455 decimals--; 456 } 457 snprintf(buf, buflen, "%c%0.*f%c", sign, decimals, value, *units); 458 } 459 460 void write_vert_config(FILE *fp) 461 { 462 int n; 463 scope_vert_t *vert; 464 scope_chan_t *chan; 465 466 vert = &(ctrl_usr->vert); 467 /* first write disabled channels */ 468 for ( n = 1 ; n <= 16 ; n++ ) { 469 if ( vert->chan_enabled[n-1] != 0 ) { 470 // channel enabled, do it later 471 continue; 472 } 473 chan = &(ctrl_usr->chan[n-1]); 474 if ( chan->name == NULL ) { 475 // no source for this channel, skip it 476 continue; 477 } 478 fprintf(fp, "CHAN %d\n", n); 479 write_chan_config(fp, chan); 480 fprintf(fp, "CHOFF\n"); 481 } 482 /* next write enabled channels */ 483 for ( n = 1 ; n <= 16 ; n++ ) { 484 if ( vert->chan_enabled[n-1] == 0 ) { 485 // channel disabled, skip it 486 continue; 487 } 488 if ( vert->selected == n ) { 489 // channel selected, do it last 490 continue; 491 } 492 chan = &(ctrl_usr->chan[n-1]); 493 if ( chan->name == NULL ) { 494 // no source for this channel, skip it 495 continue; 496 } 497 fprintf(fp, "CHAN %d\n", n); 498 write_chan_config(fp, chan); 499 } 500 /* write selected channel last */ 501 if ((vert->selected < 1) || (vert->selected > 16)) { 502 return; 503 } 504 chan = &(ctrl_usr->chan[vert->selected-1]); 505 fprintf(fp, "CHAN %d\n", vert->selected); 506 write_chan_config(fp, chan); 507 } 508 509 510 /*********************************************************************** 511 * LOCAL FUNCTIONS * 512 ************************************************************************/ 513 514 void set_color(GdkColor *color, unsigned char red, 515 unsigned char green, unsigned char blue) { 516 color->red = ((unsigned long) red) << 8; 517 color->green = ((unsigned long) green) << 8; 518 color->blue = ((unsigned long) blue) << 8; 519 color->pixel = 520 ((unsigned long) red) << 16 | ((unsigned long) green) << 8 | 521 ((unsigned long) blue); 522 } 523 524 extern int normal_colors[16][3], selected_colors[16][3]; 525 static void init_chan_sel_window(void) 526 { 527 scope_vert_t *vert; 528 GtkWidget *button; 529 long n; 530 gchar buf[5]; 531 GdkColor c; 532 533 vert = &(ctrl_usr->vert); 534 for (n = 0; n < 16; n++) { 535 snprintf(buf, 4, "%ld", n + 1); 536 /* define the button */ 537 button = gtk_toggle_button_new_with_label(buf); 538 539 /* set up colors of the label */ 540 set_color(&c, normal_colors[n][0], 541 normal_colors[n][1], normal_colors[n][2]); 542 gtk_widget_modify_bg(button, GTK_STATE_ACTIVE, &c); 543 gtk_widget_modify_bg(button, GTK_STATE_SELECTED, &c); 544 545 set_color(&c, selected_colors[n][0], 546 selected_colors[n][1], selected_colors[n][2]); 547 gtk_widget_modify_bg(button, GTK_STATE_PRELIGHT, &c); 548 549 set_color(&c, 0, 0, 0); 550 gtk_widget_modify_fg(button, GTK_STATE_ACTIVE, &c); 551 gtk_widget_modify_fg(button, GTK_STATE_SELECTED, &c); 552 gtk_widget_modify_fg(button, GTK_STATE_PRELIGHT, &c); 553 554 /* put it in the window */ 555 gtk_box_pack_start(GTK_BOX(ctrl_usr->chan_sel_win), button, TRUE, 556 TRUE, 0); 557 gtk_widget_show(button); 558 /* hook a callback function to it */ 559 gtk_signal_connect(GTK_OBJECT(button), "clicked", 560 GTK_SIGNAL_FUNC(chan_sel_button), (gpointer) n + 1); 561 /* save the button pointer */ 562 vert->chan_sel_buttons[n] = button; 563 } 564 } 565 566 static void init_chan_info_window(void) 567 { 568 scope_vert_t *vert; 569 char dummyname[HAL_NAME_LEN+1]; 570 int n; 571 572 vert = &(ctrl_usr->vert); 573 574 vert->chan_num_label = 575 gtk_label_new_in_box("--", ctrl_usr->chan_info_win, FALSE, FALSE, 5); 576 gtk_label_size_to_fit(GTK_LABEL(vert->chan_num_label), "99"); 577 gtk_vseparator_new_in_box(ctrl_usr->chan_info_win, 3); 578 579 /* a button to change the source */ 580 vert->source_name_button = gtk_button_new_with_label("------"); 581 gtk_box_pack_start(GTK_BOX(ctrl_usr->chan_info_win), 582 vert->source_name_button, FALSE, FALSE, 3); 583 584 vert->source_name_label = (GTK_BIN(vert->source_name_button))->child; 585 gtk_label_set_justify(GTK_LABEL(vert->source_name_label), 586 GTK_JUSTIFY_LEFT); 587 /* longest source name we ever need to display */ 588 for ( n = 0 ; n < HAL_NAME_LEN ; n++) dummyname[n] = 'x'; 589 dummyname[n] = '\0'; 590 gtk_label_size_to_fit(GTK_LABEL(vert->source_name_label), dummyname); 591 /* activate the source selection dialog if button is clicked */ 592 gtk_signal_connect(GTK_OBJECT(vert->source_name_button), "clicked", 593 GTK_SIGNAL_FUNC(change_source_button), NULL); 594 gtk_widget_show(vert->source_name_button); 595 596 597 vert->readout_label = gtk_label_new_in_box("", 598 ctrl_usr->chan_info_win, FALSE, FALSE, 0); 599 gtk_misc_set_alignment(GTK_MISC(vert->readout_label), 0, 0); 600 gtk_label_set_justify(GTK_LABEL(vert->readout_label), GTK_JUSTIFY_LEFT); 601 gtk_label_size_to_fit(GTK_LABEL(vert->readout_label), 602 "f(99999.9999) = 99999.9999 (ddt 99999.9999)"); 603 } 604 605 static void init_vert_info_window(void) 606 { 607 scope_vert_t *vert; 608 GtkWidget *hbox, *vbox; 609 GtkWidget *button; 610 611 vert = &(ctrl_usr->vert); 612 613 /* box for the two sliders */ 614 hbox = 615 gtk_hbox_new_in_box(TRUE, 0, 0, ctrl_usr->vert_info_win, TRUE, TRUE, 616 0); 617 /* box for the scale slider */ 618 vbox = gtk_vbox_new_in_box(FALSE, 0, 0, hbox, TRUE, TRUE, 0); 619 gtk_label_new_in_box(_("Gain"), vbox, FALSE, FALSE, 0); 620 vert->scale_adj = gtk_adjustment_new(0, -5, 5, 1, 1, 0); 621 vert->scale_slider = gtk_vscale_new(GTK_ADJUSTMENT(vert->scale_adj)); 622 gtk_scale_set_digits(GTK_SCALE(vert->scale_slider), 0); 623 gtk_scale_set_draw_value(GTK_SCALE(vert->scale_slider), FALSE); 624 gtk_box_pack_start(GTK_BOX(vbox), vert->scale_slider, TRUE, TRUE, 0); 625 /* connect the slider to a function that re-calcs vertical scale */ 626 gtk_signal_connect(GTK_OBJECT(vert->scale_adj), "value_changed", 627 GTK_SIGNAL_FUNC(scale_changed), NULL); 628 gtk_widget_show(vert->scale_slider); 629 /* box for the position slider */ 630 vbox = gtk_vbox_new_in_box(FALSE, 0, 0, hbox, TRUE, TRUE, 0); 631 gtk_label_new_in_box(_("Pos"), vbox, FALSE, FALSE, 0); 632 vert->pos_adj = 633 gtk_adjustment_new(VERT_POS_RESOLUTION / 2, 0, VERT_POS_RESOLUTION, 1, 634 1, 0); 635 vert->pos_slider = gtk_vscale_new(GTK_ADJUSTMENT(vert->pos_adj)); 636 gtk_scale_set_digits(GTK_SCALE(vert->pos_slider), 0); 637 gtk_scale_set_draw_value(GTK_SCALE(vert->pos_slider), FALSE); 638 gtk_box_pack_start(GTK_BOX(vbox), vert->pos_slider, TRUE, TRUE, 0); 639 /* connect the slider to a function that re-calcs vertical pos */ 640 gtk_signal_connect(GTK_OBJECT(vert->pos_adj), "value_changed", 641 GTK_SIGNAL_FUNC(pos_changed), NULL); 642 gtk_widget_show(vert->pos_slider); 643 /* Scale display */ 644 gtk_hseparator_new_in_box(ctrl_usr->vert_info_win, 3); 645 gtk_label_new_in_box(_("Scale"), ctrl_usr->vert_info_win, FALSE, FALSE, 0); 646 vert->scale_label = 647 gtk_label_new_in_box(" ---- ", ctrl_usr->vert_info_win, FALSE, FALSE, 648 0); 649 /* Offset control */ 650 vert->offset_button = gtk_button_new_with_label(_("Offset\n----")); 651 vert->offset_label = (GTK_BIN(vert->offset_button))->child; 652 gtk_box_pack_start(GTK_BOX(ctrl_usr->vert_info_win), 653 vert->offset_button, FALSE, FALSE, 0); 654 gtk_signal_connect(GTK_OBJECT(vert->offset_button), "clicked", 655 GTK_SIGNAL_FUNC(offset_button), NULL); 656 gtk_widget_show(vert->offset_button); 657 /* a button to turn off the channel */ 658 button = gtk_button_new_with_label(_("Chan Off")); 659 gtk_box_pack_start(GTK_BOX(ctrl_usr->vert_info_win), button, FALSE, FALSE, 660 0); 661 /* turn off the channel if button is clicked */ 662 gtk_signal_connect(GTK_OBJECT(button), "clicked", 663 GTK_SIGNAL_FUNC(channel_off_button), NULL); 664 gtk_widget_show(button); 665 } 666 667 static void scale_changed(GtkAdjustment * adj, gpointer gdata) 668 { 669 set_vert_scale(adj->value); 670 } 671 672 static void pos_changed(GtkAdjustment * adj, gpointer gdata) 673 { 674 set_vert_pos(adj->value / VERT_POS_RESOLUTION); 675 } 676 677 static void offset_button(GtkWidget * widget, gpointer gdata) 678 { 679 scope_vert_t *vert; 680 scope_chan_t *chan; 681 int chan_num; 682 683 vert = &(ctrl_usr->vert); 684 chan_num = vert->selected; 685 if ((chan_num < 1) || (chan_num > 16)) { 686 return; 687 } 688 chan = &(ctrl_usr->chan[chan_num - 1]); 689 if (chan->data_type == HAL_BIT) { 690 /* no offset for bits */ 691 return; 692 } 693 if (dialog_set_offset(chan_num)) { 694 if (chan_num == ctrl_shm->trig_chan) { 695 refresh_trigger(); 696 } 697 channel_changed(); 698 request_display_refresh(1); 699 } 700 } 701 702 static gboolean dialog_set_offset(int chan_num) 703 { 704 scope_vert_t *vert; 705 scope_chan_t *chan; 706 dialog_generic_t dialog; 707 gchar *title, msg[BUFLEN], *cptr; 708 struct offset_data data; 709 GtkWidget *label, *button; 710 double tmp; 711 712 vert = &(ctrl_usr->vert); 713 chan = &(ctrl_usr->chan[chan_num - 1]); 714 title = _("Set Offset"); 715 snprintf(msg, BUFLEN - 1, _("Set the vertical offset\n" 716 "for channel %d."), chan_num); 717 /* create dialog window, disable resizing */ 718 dialog.retval = 0; 719 dialog.window = gtk_dialog_new(); 720 dialog.app_data = &data; 721 /* allow user to grow but not shrink the window */ 722 gtk_window_set_policy(GTK_WINDOW(dialog.window), FALSE, TRUE, FALSE); 723 /* window should appear in center of screen */ 724 gtk_window_set_position(GTK_WINDOW(dialog.window), GTK_WIN_POS_CENTER); 725 /* set title */ 726 gtk_window_set_title(GTK_WINDOW(dialog.window), title); 727 /* display message */ 728 label = gtk_label_new(msg); 729 gtk_misc_set_padding(GTK_MISC(label), 15, 5); 730 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog.window)->vbox), label, FALSE, 731 TRUE, 0); 732 /* a separator */ 733 gtk_hseparator_new_in_box(GTK_DIALOG(dialog.window)->vbox, 0); 734 /* a checkbox: AC coupled */ 735 vert->offset_ac = gtk_check_button_new_with_label(_("AC Coupled")); 736 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog.window)->vbox), 737 vert->offset_ac, FALSE, TRUE, 0); 738 /* react to changes to the checkbox */ 739 gtk_signal_connect(GTK_OBJECT(vert->offset_ac), "toggled", 740 GTK_SIGNAL_FUNC(offset_changed), &data); 741 /* the entry */ 742 vert->offset_entry = gtk_entry_new(); 743 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog.window)->vbox), 744 vert->offset_entry, FALSE, TRUE, 0); 745 snprintf(data.buf, BUFLEN, "%f", chan->vert_offset); 746 gtk_entry_set_text(GTK_ENTRY(vert->offset_entry), data.buf); 747 gtk_entry_set_max_length(GTK_ENTRY(vert->offset_entry), BUFLEN-1); 748 /* point at first char */ 749 gtk_entry_set_position(GTK_ENTRY(vert->offset_entry), 0); 750 /* select all chars, so if the user types the original value goes away */ 751 gtk_entry_select_region(GTK_ENTRY(vert->offset_entry), 0, strlen(data.buf)); 752 /* make it active so user doesn't have to click on it */ 753 gtk_widget_grab_focus(GTK_WIDGET(vert->offset_entry)); 754 gtk_widget_show(vert->offset_entry); 755 /* capture entry data to the buffer whenever the user types */ 756 gtk_signal_connect(GTK_OBJECT(vert->offset_entry), "changed", 757 GTK_SIGNAL_FUNC(offset_changed), data.buf); 758 /* set up a callback function when the window is destroyed */ 759 gtk_signal_connect(GTK_OBJECT(dialog.window), "destroy", 760 GTK_SIGNAL_FUNC(dialog_generic_destroyed), &dialog); 761 /* make OK and Cancel buttons */ 762 button = gtk_button_new_with_label(_("OK")); 763 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog.window)->action_area), 764 button, TRUE, TRUE, 4); 765 gtk_signal_connect(GTK_OBJECT(button), "clicked", 766 GTK_SIGNAL_FUNC(dialog_generic_button1), &dialog); 767 /* hit the "OK" button if the user hits enter */ 768 gtk_signal_connect(GTK_OBJECT(vert->offset_entry), "activate", 769 GTK_SIGNAL_FUNC(offset_activated), button); 770 button = gtk_button_new_with_label(_("Cancel")); 771 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog.window)->action_area), 772 button, TRUE, TRUE, 4); 773 gtk_signal_connect(GTK_OBJECT(button), "clicked", 774 GTK_SIGNAL_FUNC(dialog_generic_button2), &dialog); 775 /* make window transient and modal */ 776 gtk_window_set_transient_for(GTK_WINDOW(dialog.window), 777 GTK_WINDOW(ctrl_usr->main_win)); 778 gtk_window_set_modal(GTK_WINDOW(dialog.window), TRUE); 779 gtk_widget_show_all(dialog.window); 780 gtk_main(); 781 /* we get here when the user makes a selection, hits Cancel, or closes 782 the window */ 783 if ((dialog.retval == 0) || (dialog.retval == 2)) { 784 /* user either closed dialog, or hit cancel */ 785 return FALSE; 786 } 787 tmp = strtod(data.buf, &cptr); 788 if (cptr == data.buf) { 789 return FALSE; 790 } 791 set_vert_offset(tmp, data.ac_coupled); 792 return TRUE; 793 } 794 795 static void offset_changed(GtkEditable * editable, struct offset_data *data) 796 { 797 const char *text; 798 799 /* maybe user hit "ac coupled" button" */ 800 data->ac_coupled = 801 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ctrl_usr->vert.offset_ac)); 802 gtk_entry_set_editable(GTK_ENTRY(ctrl_usr->vert.offset_entry), 803 !data->ac_coupled); 804 805 /* maybe user typed something, save it in the buffer */ 806 text = gtk_entry_get_text(GTK_ENTRY(ctrl_usr->vert.offset_entry)); 807 strncpy(data->buf, text, BUFLEN); 808 } 809 810 static void offset_activated(GtkEditable * editable, gchar * button) 811 { 812 /* user hit enter, generate a "clicked" event for the OK button */ 813 gtk_button_clicked(GTK_BUTTON(button)); 814 } 815 816 817 static void chan_sel_button(GtkWidget * widget, gpointer gdata) 818 { 819 long chan_num; 820 int n, count; 821 scope_vert_t *vert; 822 scope_chan_t *chan; 823 char *title, *msg; 824 825 vert = &(ctrl_usr->vert); 826 chan_num = (long) gdata; 827 chan = &(ctrl_usr->chan[chan_num - 1]); 828 829 if (ignore_click != 0) { 830 ignore_click = 0; 831 return; 832 } 833 if (vert->chan_enabled[chan_num - 1] == 0 ) { 834 /* channel is disabled, want to enable it */ 835 if (ctrl_shm->state != IDLE) { 836 /* acquisition in progress, must restart it */ 837 prepare_scope_restart(); 838 } 839 count = 0; 840 for (n = 0; n < 16; n++) { 841 if (vert->chan_enabled[n]) { 842 count++; 843 } 844 } 845 if (count >= ctrl_shm->sample_len) { 846 /* max number of channels already enabled */ 847 /* force the button to pop back out */ 848 ignore_click = 1; 849 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), FALSE); 850 title = _("Too many channels"); 851 msg = _("You cannot add another channel.\n\n" 852 "Either turn off one or more channels, or shorten\n" 853 "the record length to allow for more channels"); 854 dialog_generic_msg(ctrl_usr->main_win, title, msg, _("OK"), NULL, 855 NULL, NULL); 856 return; 857 } 858 if (chan->name == NULL) { 859 /* need to assign a source */ 860 if (dialog_select_source(chan_num) != TRUE) { 861 /* user failed to assign a source */ 862 /* force the button to pop back out */ 863 ignore_click = 1; 864 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), 865 FALSE); 866 return; 867 } 868 } 869 vert->chan_enabled[chan_num - 1] = 1; 870 } else { 871 /* channel was already enabled, user wants to select it */ 872 /* button should stay down, so we force it */ 873 ignore_click = 1; 874 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), TRUE); 875 } 876 if (vert->selected != chan_num) { 877 /* make chan_num the selected channel */ 878 vert->selected = chan_num; 879 channel_changed(); 880 } 881 } 882 883 static void channel_off_button(GtkWidget * widget, gpointer gdata) 884 { 885 scope_vert_t *vert; 886 int chan_num; 887 888 vert = &(ctrl_usr->vert); 889 chan_num = vert->selected; 890 set_channel_off(chan_num); 891 } 892 893 static void change_source_button(GtkWidget * widget, gpointer gdata) 894 { 895 int chan_num; 896 897 chan_num = ctrl_usr->vert.selected; 898 if ((chan_num < 1) || (chan_num > 16)) { 899 return; 900 } 901 if (ctrl_shm->state != IDLE) { 902 /* acquisition in progress, must restart it */ 903 prepare_scope_restart(); 904 } 905 invalidate_channel(chan_num); 906 dialog_select_source(chan_num); 907 channel_changed(); 908 } 909 910 static char search_target[HAL_NAME_LEN+1]; 911 static guint32 search_time = 0; 912 static int search_row = -1; 913 #define SEARCH_RESET_TIME 1000 /* ms */ 914 915 static void selection_made_common(GtkWidget *clist, gint row, dialog_generic_t *dptr) { 916 gint n, listnum; 917 gchar *name; 918 int rv, chan_num; 919 920 scope_vert_t *vert; 921 /* If we get here, it should be a valid selection */ 922 vert = &(ctrl_usr->vert); 923 chan_num = *((int *)(dptr->app_data)); 924 /* figure out which notebook tab it was */ 925 listnum = -1; 926 for (n = 0; n < 3; n++) { 927 if (clist == vert->lists[n]) { 928 listnum = n; 929 } 930 } 931 /* Get the text from the list */ 932 gtk_clist_get_text(GTK_CLIST(clist), row, 0, &name); 933 /* try to set up the new source */ 934 rv = set_channel_source(chan_num, listnum, name); 935 if ( rv == 0 ) { 936 /* set return value of dialog to indicate success */ 937 dptr->retval = 1; 938 } else { 939 /* new source invalid, return as if user hit cancel */ 940 dptr->retval = 2; 941 } 942 /* destroy window to cause dialog_generic_destroyed() to be called */ 943 gtk_widget_destroy(dptr->window); 944 return; 945 } 946 947 948 static gboolean search_for_entry(GtkWidget *widget, GdkEventKey *event, dialog_generic_t *dptr) 949 { 950 GtkCList *clist = GTK_CLIST(widget); 951 int z, wrapped; 952 953 if(event->keyval == GDK_Return) { 954 selection_made_common(widget, clist->focus_row, dptr); 955 } 956 957 if(!isprint(event->string[0])) { 958 strcpy(search_target, ""); 959 search_row = clist->focus_row; 960 return 0; 961 } 962 963 if(event->time - search_time > SEARCH_RESET_TIME) { 964 strcpy(search_target, ""); 965 search_row = clist->focus_row; 966 } 967 968 search_time = event->time; 969 if(strcmp(event->string, " ") == 0) { 970 char *text; 971 search_row = search_row + 1; 972 if(!gtk_clist_get_text(clist, search_row, 0, &text)) 973 search_row = 0; 974 printf(_("next search: %d\n"), search_row); 975 } else { 976 strcat(search_target, event->string); 977 } 978 979 for(z = search_row, wrapped=0; z != search_row || !wrapped; z ++) { 980 char *text; 981 982 printf(_("search: %d (wrapped=%d)\n"), z, wrapped); 983 if(!gtk_clist_get_text(clist, z, 0, &text)) { 984 if(wrapped) break; // wrapped second time (why?) 985 z = 0; 986 wrapped = 1; 987 } 988 989 if(strstr(text, search_target)) { 990 double pos = (z+.5) / (clist->rows-1); 991 if(pos > 1) pos = 1; 992 993 GTK_CLIST_GET_CLASS(clist)->scroll_vertical(clist, GTK_SCROLL_JUMP, pos); 994 gtk_clist_select_row(clist, z, 0); 995 search_row = z; 996 return 1; 997 } 998 } 999 return 0; 1000 } 1001 1002 static gboolean change_page(GtkNotebook *notebook, GtkNotebookPage *page, guint page_num, gpointer user_data) { 1003 scope_vert_t *vert; 1004 1005 vert = &(ctrl_usr->vert); 1006 if(page_num < 3) 1007 gtk_widget_grab_focus(GTK_WIDGET(vert->lists[page_num])); 1008 return 0; 1009 } 1010 1011 static gboolean dialog_select_source(int chan_num) 1012 { 1013 scope_vert_t *vert; 1014 scope_chan_t *chan; 1015 dialog_generic_t dialog; 1016 gchar *title, msg[BUFLEN]; 1017 int next, n, initial_page, row, initial_row, max_row; 1018 gchar *tab_label_text[3], *name; 1019 GtkWidget *hbox, *label, *notebk, *button; 1020 GtkAdjustment *adj; 1021 hal_pin_t *pin; 1022 hal_sig_t *sig; 1023 hal_param_t *param; 1024 1025 vert = &(ctrl_usr->vert); 1026 chan = &(ctrl_usr->chan[chan_num - 1]); 1027 title = _("Select Channel Source"); 1028 snprintf(msg, BUFLEN - 1, _("Select a pin, signal, or parameter\n" 1029 "as the source for channel %d."), chan_num); 1030 /* create dialog window, disable resizing */ 1031 dialog.retval = 0; 1032 dialog.window = gtk_dialog_new(); 1033 dialog.app_data = &chan_num; 1034 /* set initial height of window */ 1035 gtk_widget_set_usize(GTK_WIDGET(dialog.window), -2, 300); 1036 /* allow user to grow but not shrink the window */ 1037 gtk_window_set_policy(GTK_WINDOW(dialog.window), FALSE, TRUE, FALSE); 1038 /* window should appear in center of screen */ 1039 gtk_window_set_position(GTK_WINDOW(dialog.window), GTK_WIN_POS_CENTER); 1040 /* set title */ 1041 gtk_window_set_title(GTK_WINDOW(dialog.window), title); 1042 /* display message */ 1043 label = gtk_label_new(msg); 1044 gtk_misc_set_padding(GTK_MISC(label), 15, 5); 1045 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog.window)->vbox), label, FALSE, 1046 TRUE, 0); 1047 1048 /* a separator */ 1049 gtk_hseparator_new_in_box(GTK_DIALOG(dialog.window)->vbox, 0); 1050 1051 /* create a notebook to hold pin, signal, and parameter lists */ 1052 notebk = gtk_notebook_new(); 1053 /* add the notebook to the dialog */ 1054 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog.window)->vbox), notebk, TRUE, 1055 TRUE, 0); 1056 /* set overall notebook parameters */ 1057 gtk_notebook_set_homogeneous_tabs(GTK_NOTEBOOK(notebk), TRUE); 1058 gtk_signal_connect(GTK_OBJECT(notebk), "switch-page", GTK_SIGNAL_FUNC(change_page), &dialog); 1059 /* text for tab labels */ 1060 tab_label_text[0] = _("Pins"); 1061 tab_label_text[1] = _("Signals"); 1062 tab_label_text[2] = _("Parameters"); 1063 /* loop to create three identical tabs */ 1064 for (n = 0; n < 3; n++) { 1065 /* Create a scrolled window to display the list */ 1066 vert->windows[n] = gtk_scrolled_window_new(NULL, NULL); 1067 vert->adjs[n] = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(vert->windows[n])); 1068 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(vert->windows[n]), 1069 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); 1070 gtk_widget_show(vert->windows[n]); 1071 /* create a list to hold the data */ 1072 vert->lists[n] = gtk_clist_new(1); 1073 /* set up a callback for when the user selects a line */ 1074 gtk_signal_connect(GTK_OBJECT(vert->lists[n]), "select_row", 1075 GTK_SIGNAL_FUNC(selection_made), &dialog); 1076 gtk_signal_connect(GTK_OBJECT(vert->lists[n]), "key-press-event", 1077 GTK_SIGNAL_FUNC(search_for_entry), &dialog); 1078 /* It isn't necessary to shadow the border, but it looks nice :) */ 1079 gtk_clist_set_shadow_type(GTK_CLIST(vert->lists[n]), GTK_SHADOW_OUT); 1080 /* set list for single selection only */ 1081 gtk_clist_set_selection_mode(GTK_CLIST(vert->lists[n]), 1082 GTK_SELECTION_BROWSE); 1083 /* put the list into the scrolled window */ 1084 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW 1085 (vert->windows[n]), vert->lists[n]); 1086 /* another way to do it - not sure which is better 1087 gtk_container_add(GTK_CONTAINER(vert->windows[n]), vert->lists[n]); */ 1088 gtk_widget_show(vert->lists[n]); 1089 /* create a box for the tab label */ 1090 hbox = gtk_hbox_new(TRUE, 0); 1091 /* create a label for the page */ 1092 gtk_label_new_in_box(tab_label_text[n], hbox, TRUE, TRUE, 0); 1093 gtk_widget_show(hbox); 1094 /* add page to the notebook */ 1095 gtk_notebook_append_page(GTK_NOTEBOOK(notebk), vert->windows[n], hbox); 1096 /* set tab attributes */ 1097 gtk_notebook_set_tab_label_packing(GTK_NOTEBOOK(notebk), hbox, 1098 TRUE, TRUE, GTK_PACK_START); 1099 } 1100 /* determine initial page: pin, signal, or parameter */ 1101 if (( chan->data_source_type >= 0 ) && ( chan->data_source_type <= 2 )) { 1102 initial_page = chan->data_source_type; 1103 gtk_notebook_set_page(GTK_NOTEBOOK(notebk), initial_page); 1104 } else { 1105 initial_page = -1; 1106 gtk_notebook_set_page(GTK_NOTEBOOK(notebk), 0); 1107 } 1108 gtk_widget_show(notebk); 1109 1110 /* populate the pin, signal, and parameter lists */ 1111 gtk_clist_clear(GTK_CLIST(vert->lists[0])); 1112 gtk_clist_clear(GTK_CLIST(vert->lists[1])); 1113 gtk_clist_clear(GTK_CLIST(vert->lists[2])); 1114 rtapi_mutex_get(&(hal_data->mutex)); 1115 next = hal_data->pin_list_ptr; 1116 initial_row = -1; 1117 max_row = -1; 1118 while (next != 0) { 1119 pin = SHMPTR(next); 1120 name = pin->name; 1121 row = gtk_clist_append(GTK_CLIST(vert->lists[0]), &name); 1122 if ( initial_page == 0 ) { 1123 if ( strcmp(name, chan->name) == 0 ) { 1124 initial_row = row; 1125 } 1126 max_row = row; 1127 } 1128 next = pin->next_ptr; 1129 } 1130 next = hal_data->sig_list_ptr; 1131 while (next != 0) { 1132 sig = SHMPTR(next); 1133 name = sig->name; 1134 row = gtk_clist_append(GTK_CLIST(vert->lists[1]), &name); 1135 if ( initial_page == 1 ) { 1136 if ( strcmp(name, chan->name) == 0 ) { 1137 initial_row = row; 1138 } 1139 max_row = row; 1140 } 1141 next = sig->next_ptr; 1142 } 1143 next = hal_data->param_list_ptr; 1144 while (next != 0) { 1145 param = SHMPTR(next); 1146 name = param->name; 1147 row = gtk_clist_append(GTK_CLIST(vert->lists[2]), &name); 1148 if ( initial_page == 2 ) { 1149 if ( strcmp(name, chan->name) == 0 ) { 1150 initial_row = row; 1151 } 1152 max_row = row; 1153 } 1154 next = param->next_ptr; 1155 } 1156 rtapi_mutex_give(&(hal_data->mutex)); 1157 1158 if ( initial_row >= 0 ) { 1159 /* highlight the currently selected name */ 1160 gtk_clist_select_row(GTK_CLIST(vert->lists[initial_page]), initial_row, -1); 1161 /* set scrolling window to show the highlighted name */ 1162 /* FIXME - I can't seem to get this to work */ 1163 adj = vert->adjs[initial_page]; 1164 adj->value = adj->lower + (adj->upper - adj->lower)*((double)(initial_row)/(double)(max_row+1)); 1165 gtk_adjustment_value_changed(vert->adjs[initial_page]); 1166 } 1167 /* set up a callback function when the window is destroyed */ 1168 gtk_signal_connect(GTK_OBJECT(dialog.window), "destroy", 1169 GTK_SIGNAL_FUNC(dialog_generic_destroyed), &dialog); 1170 /* make Cancel button */ 1171 button = gtk_button_new_with_label(_("Cancel")); 1172 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog.window)->action_area), 1173 button, TRUE, TRUE, 4); 1174 gtk_signal_connect(GTK_OBJECT(button), "clicked", 1175 GTK_SIGNAL_FUNC(dialog_generic_button2), &dialog); 1176 /* make window transient and modal */ 1177 gtk_window_set_transient_for(GTK_WINDOW(dialog.window), 1178 GTK_WINDOW(ctrl_usr->main_win)); 1179 gtk_window_set_modal(GTK_WINDOW(dialog.window), TRUE); 1180 gtk_widget_show_all(dialog.window); 1181 gtk_main(); 1182 /* we get here when the user makes a selection, hits Cancel, or closes 1183 the window */ 1184 vert->lists[0] = NULL; 1185 vert->lists[1] = NULL; 1186 vert->lists[2] = NULL; 1187 if ((dialog.retval == 0) || (dialog.retval == 2)) { 1188 /* user either closed dialog, or hit cancel */ 1189 return FALSE; 1190 } 1191 /* user made a selection */ 1192 channel_changed(); 1193 return TRUE; 1194 } 1195 /* If we come here, then the user has clicked a row in the list. */ 1196 static void selection_made(GtkWidget * clist, gint row, gint column, 1197 GdkEventButton * event, dialog_generic_t * dptr) 1198 { 1199 GdkEventType type; 1200 1201 if ((event == NULL) || (clist == NULL)) { 1202 /* We get spurious events when the lists are populated I don't know 1203 why. If either clist or event is null, it's a bad one! */ 1204 return; 1205 } 1206 type = event->type; 1207 if (type != 4) { 1208 /* We also get bad callbacks if you drag the mouse across the list 1209 with the button held down. They can be distinguished because 1210 their event type is 3, not 4. */ 1211 return; 1212 } 1213 selection_made_common(clist, row, dptr); 1214 } 1215 1216 void channel_changed(void) 1217 { 1218 scope_vert_t *vert; 1219 scope_chan_t *chan; 1220 GtkAdjustment *adj; 1221 gchar *name; 1222 gchar buf1[BUFLEN + 1], buf2[BUFLEN + 1]; 1223 1224 vert = &(ctrl_usr->vert); 1225 if ((vert->selected < 1) || (vert->selected > 16)) { 1226 gtk_label_set_text_if(vert->scale_label, "----"); 1227 gtk_label_set_text_if(vert->chan_num_label, "--"); 1228 gtk_label_set_text_if(vert->source_name_label, "------"); 1229 request_display_refresh(1); 1230 return; 1231 } 1232 chan = &(ctrl_usr->chan[vert->selected - 1]); 1233 /* set position slider based on new channel */ 1234 gtk_adjustment_set_value(GTK_ADJUSTMENT(vert->pos_adj), 1235 chan->position * VERT_POS_RESOLUTION); 1236 /* set scale slider based on new channel */ 1237 adj = GTK_ADJUSTMENT(vert->scale_adj); 1238 adj->lower = chan->min_index; 1239 adj->upper = chan->max_index; 1240 adj->value = chan->scale_index; 1241 gtk_adjustment_changed(adj); 1242 gtk_adjustment_value_changed(adj); 1243 /* update the channel number and name display */ 1244 snprintf(buf1, BUFLEN, "%2d", vert->selected); 1245 name = chan->name; 1246 gtk_label_set_text_if(vert->chan_num_label, buf1); 1247 gtk_label_set_text_if(vert->source_name_label, name); 1248 /* update the offset display */ 1249 if (chan->data_type == HAL_BIT) { 1250 snprintf(buf1, BUFLEN, "----"); 1251 } else { 1252 if(chan->ac_offset) { 1253 snprintf(buf1, BUFLEN, "(AC)"); 1254 } else { 1255 format_signal_value(buf1, BUFLEN, chan->vert_offset); 1256 } 1257 } 1258 snprintf(buf2, BUFLEN, _("Offset\n%s"), buf1); 1259 gtk_label_set_text_if(vert->offset_label, buf2); 1260 request_display_refresh(1); 1261 } 1262 1263 void format_scale_value(char *buf, int buflen, double value) 1264 { 1265 char *units; 1266 char symbols[] = "pnum KMGT"; 1267 1268 if (value < 0.9e-12) { 1269 /* less than pico units, shouldn't happen */ 1270 snprintf(buf, buflen, "tiny"); 1271 return; 1272 } 1273 if (value > 1.1e+12) { 1274 /* greater than tera-units, shouldn't happen */ 1275 snprintf(buf, buflen, "huge"); 1276 return; 1277 } 1278 units = &(symbols[4]); 1279 while (value < 1.0) { 1280 value *= 1000.0; 1281 units--; 1282 } 1283 while (value >= 999.99) { 1284 value *= 0.001; 1285 units++; 1286 } 1287 snprintf(buf, buflen, "%0.0f%c/div", value, *units); 1288 } 1289 1290 static void write_chan_config(FILE *fp, scope_chan_t *chan) 1291 { 1292 if ( chan->data_source_type == 0 ) { 1293 // pin 1294 fprintf(fp, "PIN %s\n", chan->name); 1295 } else if ( chan->data_source_type == 1 ) { 1296 // signal 1297 fprintf(fp, "SIG %s\n", chan->name); 1298 } else if ( chan->data_source_type == 2 ) { 1299 // pin 1300 fprintf(fp, "PARAM %s\n", chan->name); 1301 } else { 1302 // not configured 1303 return; 1304 } 1305 fprintf(fp, "VSCALE %d\n", chan->scale_index); 1306 fprintf(fp, "VPOS %f\n", chan->position); 1307 if(chan->ac_offset) { 1308 fprintf(fp, "VAC %e\n", chan->vert_offset); 1309 } else { 1310 fprintf(fp, "VOFF %e\n", chan->vert_offset); 1311 } 1312 }