1# snd-xm.rb -- snd-motif classes and functions 2 3# Author: Michael Scholz <mi-scholz@users.sourceforge.net> 4# Created: 04/02/25 05:31:02 5# Changed: 20/09/19 00:41:29 6 7# Requires --with-motif 8# 9# Tested with Snd 20.x 10# Ruby 2.6 11# Motif 2.3.3 X11R6 12# 13# module Snd_XM 14# make_snd_menu(name, args) do ... end 15# make_menu(name, parent) do ... end 16# make_popup_menu(name, parent) do ... end 17# make_dialog(label, *rest) do |w, c, i| ... end 18# format_sound_comment(comment) 19# scale_log2linear(lo, val, hi) 20# scale_linear2log(lo, val, hi) 21# scale_log_label(lo, val, hi) 22# semi_scale_label(val) 23# semitones2ratio 24# ratio2semitones 25# get_color(*new_color) 26# create_color(color) 27# yellow_pixel 28# red_pixel 29# white_pixel 30# black_pixel 31# update_label(list) 32# change_label(widget, string, property) 33# find_child(widget, name) 34# each_child(widget) do |w| .... end 35# widget?(obj) 36# is_managed?(widget) 37# widget_name(widget) 38# set_scale_value(widget, value, scaler) 39# get_scale_value(widget, info, scaler) 40# raise_dialog(widget) 41# activate_dialog(dialog) 42# set_label_sensitive(widget, name, set_p) 43# set_sensitive(widget, flag) 44# add_main_pane(name, type, *args) 45# show_disk_space(snd) 46# 47# class Scale_widget 48# initialize(parent) 49# scale 50# label 51# add_scale(title, low, init, high, scale, kind) 52# 53# class Dialog_base 54# initialize(label, ok_cb, reset_cb, clear_cb, target_cb, help_cb) 55# dialog 56# parent 57# okay_button 58# doit_string(*args) 59# dismiss_string(*args) 60# help_string(*args) 61# reset_string(*args) 62# clear_string(*args) 63# 64# class Dialog < Dialog_base 65# add_slider(title, low, init, high, scl, kind, parent) do |w, c, i| ... end 66# add_toggle(label, value) do |val| ... end 67# add_target(labels) do |val| ... end 68# 69# module Snd_Motif 70# string2compound(*args) 71# compound2string(xstr) 72# get_xtvalue(widget, item) 73# current_label(widget) 74# current_screen 75# get_pixmap(screen, file) 76# screen_depth 77# display_widget_tree(widget, spaces) 78# add_sound_pane(snd, name, type, *args) 79# add_channel_pane(snd, chn, name, type, *args) 80# menu_option(name) 81# set_main_color_of_widget(w) 82# 83# add_mark_pane 84# 85# class Mark_pane 86# initialize 87# make_list(snd, chn) 88# deactivate_channel(snd, chn) 89# 90# class Variable_display 91# initialize(page_name, variable_name) 92# inspect 93# make_dialog 94# create 95# close 96# reset 97# 98# class Variable_display_text < Variable_display 99# create 100# display(var) 101# 102# class Variable_display_scale < Variable_display 103# initialize(page_name, variable_name, range) 104# inspect 105# create 106# display(var) 107# 108# class Variable_display_graph < Variable_display 109# create 110# display(var) 111# reset 112# 113# class Variable_display_spectrum < Variable_display 114# create 115# display(var) 116# reset 117# 118# make_variable_display(page_name, variable_name, type, range) 119# variable_display(vd, var) 120# variable_display_close(vd) 121# variable_display_reset(vd) 122# variable_display?(vd) 123# 124# class Dialog 125# add_frame(args) 126# add_label(label, args) 127# add_textfield(string, label, columns) do |w, c, i| ... end 128# add_text(*args) 129# 130# class Menu 131# initialize(name, menu, args) 132# menu 133# each_entry do |child| ... end 134# change_menu_color(new_color) 135# 136# class Snd_main_menu < Menu 137# initialize(name, parent, args) do ... end 138# menu_number 139# entry(klass, *rest) or entry(name) do ... end 140# separator 141# cascade(name, args) do ... end 142 143require "clm" 144 145$with_motif = provided?(:snd_motif) 146 147unless $with_motif 148 Snd.raise(:runtime_error, __FILE__, "--with-motif required") 149end 150 151# 152# --- Motif functions --- 153# 154module Snd_XM 155 class SndXError < StandardError 156 end 157 Ruby_exceptions[:snd_x_error] = SndXError 158 159 Dismiss_string = "Go Away" 160 Help_string = "Help" 161 Okay_string = "DoIt" 162 Reset_string = "Reset" 163 Clear_string = "Clear" 164 165 # main_widgets 166 Top_level_application = 0 167 Top_level_shell = 1 168 Main_pane_shell = 2 169 Main_sound_pane = 3 170 Listener_pane = 4 171 Notebook_outer_pane = 5 172 173 # menu_widgets 174 Top_menu_bar = 0 175 File_menu = 1 176 Edit_menu = 2 177 View_menu = 3 178 Options_menu = 4 179 Help_menu = 5 180 181 # sound_widgets 182 Main_pane = 0 183 Name_label = 1 184 Control_panel = 2 185 Minibuffer = 3 186 Play = 4 187 Filter_graph = 5 188 Unite = 6 189 Minibuffer_label = 7 190 Name_icon = 8 191 Sync = 9 192 193 # channel_widgets 194 Graph = 0 195 W = 1 196 F = 2 197 Sx = 3 198 Sy = 4 199 Zx = 5 200 Zy = 6 201 Edhist = 7 202 Gsy = 8 203 Gzy = 9 204 Channel_main_pane = 10 205 206 # dialog_widgets 207 Orientation_dialog = 0 208 Enved_dialog = 1 209 Transform_dialog = 2 210 File_open_dialog = 3 211 File_save_as_dialog = 4 212 View_files_dialog = 5 213 Raw_data_dialog = 6 214 New_file_dialog = 7 215 File_mix_dialog = 8 216 Edit_header_dialog = 9 217 Find_dialog = 10 218 Help_dialog = 11 219 Mix_panel_dialog = 12 220 Print_dialog = 13 221 Region_dialog = 14 222 Info_dialog = 15 223 Extra_controls_dialog = 16 224 Save_selection_dialog = 17 225 Insert_file_dialog = 18 226 Save_region_dialog = 19 227 Preferences_dialog = 20 228 # 229 # old names 230 # 231 Color_dialog = 0 232 Error_dialog = 0 233 Yes_or_no_dialog = 0 234 Completion_dialog = 0 235 Recorder_dialog = 0 236 Track_dialog = 0 237 238 # MAKE_MENU as well as MAKE_POPUP_MENU may be used in non-Snd 239 # scripts. MAKE_SND_MENU and MAKE_SND_POPUP (see popup.rb) are 240 # specialized using them in Snd. 241 # 242 # [...] 243 # main_widget = RXmCreateMainWindow(top_level, "main", []) 244 # menu_bar = RXmCreateMenuBar(main_widget, "menu-bar", []) 245 # RXtManageChild(menu_bar) 246 # make_menu("file-button", menu_bar) do 247 # entry("quit") do |w, c, i| exit(0) end 248 # end 249 # make_menu("help-button", menu_bar) do 250 # entry("general-help") do |w, c, i| ... end 251 # end 252 def make_menu(name, parent, &body) 253 Main_menu.new(name, parent, [], &body) 254 end 255 256 # play = lambda do |w, c, i| 257 # @play = Rset(i) 258 # send_values(!@play) 259 # end 260 # make_popup_menu("popup", main_widget) do 261 # entry("play", :widget_class, RxmToggleButtonWidgetClass, &play) 262 # separator 263 # entry("quit") do |w, c, i| exit(0) end 264 # end 265 def make_popup_menu(name, parent, &body) 266 Main_popup_menu.new(name, parent, [], &body) 267 end 268 269 def make_dialog(label, *rest, &ok_cb) 270 reset_cb, clear_cb, target_cb, help_cb, help_str = optkey(rest, 271 :reset_cb, 272 :clear_cb, 273 :target_cb, 274 :help_cb, 275 :info) 276 unless proc?(help_cb) 277 if string?(help_str) and !help_str.empty? 278 help_cb = lambda do |w, c, i| 279 help_dialog(label, help_str) 280 end 281 end 282 end 283 d = Dialog.new(label, ok_cb, reset_cb, clear_cb, target_cb, help_cb) 284 d.create_dialog 285 d 286 end 287 288 # simple comment formatter, indents comment text in popup.rb and nb.rb 289 # comment: long comment 290 # text ... 291 # and so on 292 def format_sound_comment(com) 293 if com.empty? 294 com 295 else 296 len = 0 297 text_length = if widget?(wid = dialog_widgets[Info_dialog]) 298 widget_size(wid).first / 10 299 else 300 56 301 end 302 indent_length = "comment: ".length 303 str = "" 304 format("comment: %s", com).split(/ /).each do |s| 305 unless (len += s.length + 1) < text_length 306 len = indent_length + s.length 307 str << "\n" << " " * indent_length 308 end 309 str << s << " " 310 end 311 str << "\n" 312 end 313 end 314 315 $semi_range = 24 unless defined? $semi_range 316 $log_scale_ticks = 500 unless defined? $log_scale_ticks 317 318 Log2 = log(2.0) 319 320 def scale_log2linear(lo, val, hi) 321 log_lo = log([lo, 1.0].max) / Log2 322 log_hi = log(hi) / Log2 323 log_val = log(val) / Log2 324 $log_scale_ticks.to_f * ((log_val - log_lo) / (log_hi - log_lo)) 325 end 326 327 def scale_linear2log(lo, val, hi) 328 log_lo = log([lo, 1.0].max) / Log2 329 log_hi = log(hi) / Log2 330 log_val = log_lo + ((val / $log_scale_ticks.to_f) * (log_hi - log_lo)) 331 2.0 ** log_val 332 end 333 334 def scale_log_label(lo, val, hi) 335 format("%1.2f", scale_linear2log(lo, val, hi)) 336 end 337 338 def semi_scale_label(val) 339 format("semitones: %d", val - $semi_range) 340 end 341 342 def semitones2ratio(val) 343 (2.0 ** val) / 12.0 344 end 345 346 def ratio2semitones(ratio) 347 (12.0 * (log(ratio) / log(2.0))).round 348 end 349 350 # get_color("ivory2") 351 # get_color(0.93, 0.93, 0.87) 352 # get_color(Ivory2) # from rgb.rb 353 def get_color(*new_color) 354 if string?(new_color[0]) 355 create_color(new_color[0]) 356 elsif new_color.length == 3 357 make_color(*new_color) 358 elsif color?(new_color[0]) 359 new_color[0] 360 else 361 make_color(0.0, 0.0, 0.0) 362 end 363 end 364 365 def yellow_pixel 366 create_color("yellow") 367 end 368 369 def red_pixel 370 create_color("red") 371 end 372 373 def update_label(list) 374 if array?(list) and (not widget?(list)) 375 list.each do |prc| 376 prc.call 377 end 378 end 379 end 380 381 add_help(:find_child, 382 "find_child(widget, name) \ 383Returns a widget named NAME, \ 384if one can be found in the widget hierarchy beneath WIDGET.") 385 def find_child(widget, name) 386 res = false 387 each_child(widget) do |child| 388 if widget_name(child) == name 389 # INFO 390 # Wed Nov 17 14:41:50 CET 2010 391 # "return child" 392 # RETURN seems not to return a value with RUBY_VERSION 1.8.0 393 res = child 394 return res 395 end 396 end 397 if res 398 res 399 else 400 Snd.raise(:no_such_widget, name) 401 end 402 end 403 404 def set_label_sensitive(widget, name, set_p = false) 405 wid = Snd.catch(:no_such_widget) do 406 find_child(widget, name) 407 end.first 408 if widget?(wid) 409 set_sensitive(wid, set_p) 410 end 411 end 412 413 set_property(:show_disk_space, :labelled_snds, []) 414 415 def labelled_snds 416 property(:show_disk_space, :labelled_snds) 417 end 418 419 def kmg(num) 420 if num <= 0 421 "disk full!" 422 else 423 if num > 1024 424 if num > 1024 * 1024 425 format("space: %6.3fG", (num / (1024.0 * 1024)).round) 426 else 427 format("space: %6.3fM", (num / 1024.0).round) 428 end 429 else 430 format("space: %10dK", num) 431 end 432 end 433 end 434end 435 436class Dialog_base 437 def initialize(label, ok_cb, reset_cb, clear_cb, target_cb, help_cb) 438 @label = label 439 @ok_cb = ok_cb 440 @reset_cb = reset_cb 441 @clear_cb = clear_cb 442 @target_cb = target_cb 443 @help_cb = help_cb 444 @doit = Okay_string 445 @dismiss = Dismiss_string 446 @help = Help_string 447 @reset = Reset_string 448 @clear = Clear_string 449 @dialog = nil 450 @parent = nil 451 @reset_button = nil 452 @clear_button = nil 453 @okay_button = nil 454 @dismiss_button = nil 455 @help_button = nil 456 end 457 attr_reader :dialog, :parent, :okay_button 458 459 def doit_string(*args) 460 change_label(@okay_button, @doit = format(*args)) 461 end 462 463 def dismiss_string(*args) 464 change_label(@dismiss_button, @dismiss = format(*args)) 465 end 466 467 def help_string(*args) 468 change_label(@help_button, @help = format(*args)) 469 end 470 471 def reset_string(*args) 472 change_label(@reset_button, @reset = format(*args)) 473 end 474 475 def clear_string(*args) 476 change_label(@clear_button, @clear = format(*args)) 477 end 478end 479 480# 481# --- Motif --- 482# 483module Snd_Motif 484 def make_snd_menu(name, args = [RXmNbackground, basic_color], &body) 485 Snd_main_menu.new(name, nil, args, &body) 486 end 487 488 def create_color(color) 489 col = RXColor() 490 dpy = RXtDisplay(main_widgets[Top_level_shell]) 491 c = RXAllocNamedColor(dpy, 492 RDefaultColormap(dpy, RDefaultScreen(dpy)), 493 color, col, col) 494 if c.zero? 495 Snd.raise(:no_such_color, color, "can't allocate") 496 else 497 Rpixel(col) 498 end 499 end 500 501 def white_pixel 502 RWhitePixelOfScreen(current_screen) 503 end 504 505 def black_pixel 506 RBlackPixelOfScreen(current_screen) 507 end 508 509 def change_label(widget, string, property = RXmNlabelString) 510 xs = string2compound(string) 511 RXtSetValues(widget, [property, xs]) 512 RXmStringFree(xs) 513 end 514 515 add_help(:each_child, 516 "each_child(w, &func) \ 517Applies FUNC to W and each of its children.") 518 add_help(:for_each_child, 519 "for_each_child(w, &func) \ 520Applies FUNC to W and each of its children.") 521 def each_child(widget, &body) 522 if RWidget?(widget) 523 body.call(widget) 524 if RXtIsComposite(widget) 525 (get_xtvalue(widget, RXmNchildren) or []).each do |wid| 526 each_child(wid, &body) 527 end 528 end 529 end 530 end 531 alias for_each_child each_child 532 533 def widget?(obj) 534 RWidget?(obj) 535 end 536 537 def is_managed?(wid) 538 RXtIsManaged(wid) 539 end 540 541 def widget_name(wid) 542 RXtName(wid) 543 end 544 545 def set_scale_value(widget, value, scaler = 1.0) 546 RXmScaleSetValue(widget, (value * Float(scaler)).round) 547 end 548 549 def get_scale_value(widget, info, scaler = 1.0) 550 Rvalue(info) / Float(scaler) 551 end 552 553 def raise_dialog(widget) 554 if RWidget?(widget) and RXtIsManaged(widget) 555 parent = RXtParent(widget) 556 if RWidget?(parent) and RXtIsSubclass(parent, RxmDialogShellWidgetClass) 557 RXtPopup(parent, RXtGrabNone) 558 end 559 end 560 end 561 562 def activate_dialog(dialog) 563 RXtIsManaged(dialog) ? raise_dialog(dialog) : RXtManageChild(dialog) 564 end 565 566 def set_sensitive(widget, flag) 567 RXtSetSensitive(widget, flag) 568 end 569 570 def add_main_pane(name, type, *args) 571 w = main_widgets[Notebook_outer_pane] or main_widgets[Main_sound_pane] 572 RXtCreateManagedWidget(name, type, w, *args) 573 end 574 575 def add_sound_pane(snd, name, type, *args) 576 RXtCreateManagedWidget(name, type, sound_widgets(snd)[Main_pane], *args) 577 end 578 579 def add_channel_pane(snd, chn, name, type, *args) 580 xp = RXtParent(RXtParent(channel_widgets(snd, chn)[Edhist])) 581 RXtCreateManagedWidget(name, type, xp, *args) 582 end 583 584 # string must be freed 585 def string2compound(*args) 586 args[0] = String(args[0]) 587 RXmStringCreateLocalized(format(*args)) 588 end 589 590 def compound2string(xstr) 591 RXmStringUnparse(xstr, false, RXmCHARSET_TEXT, RXmCHARSET_TEXT, 592 false, 0, RXmOUTPUT_ALL) 593 end 594 595 def get_xtvalue(widget, item) 596 RXtVaGetValues(widget, [item, 0])[1] 597 end 598 599 def current_label(widget) 600 compound2string(get_xtvalue(widget, RXmNlabelString)) 601 end 602 603 add_help(:current_screen, 604 "current_screen() \ 605Returns the current X screen number of the current display.") 606 def current_screen 607 RDefaultScreenOfDisplay(RXtDisplay(main_widgets[Top_level_shell])) 608 end 609 610 def get_pixmap(screen, file) 611 pix = RXmGetPixmap(screen, file, RBlackPixelOfScreen(screen), 612 RWhitePixelOfScreen(screen)) 613 if pix == RXmUNSPECIFIED_PIXMAP 614 Snd.raise(:snd_x_error, pix, "can't create pixmap") 615 else 616 pix 617 end 618 end 619 620 def screen_depth 621 RDefaultDepthOfScreen(current_screen) 622 end 623 624 add_help(:display_widget_tree, 625 "display_widget_tree(widget, spaces=\"\") \ 626Displays the hierarchy of widgets beneath WIDGET." ) 627 def display_widget_tree(widget, spaces = "") 628 if (name = RXtName(widget)).null? 629 name = "<unnamed>" 630 end 631 Snd.display("%s%s", spaces, name) 632 if RXtIsComposite(widget) 633 (get_xtvalue(widget, RXmNchildren) or []).each do |w| 634 display_widget_tree(w, spaces + " ") 635 end 636 end 637 end 638 639 add_help(:show_disk_space, 640 "show_disk_space(snd) \ 641Adds a label to the minibuffer area showing \ 642the current free space (for use with $after_open_hook).") 643 def show_disk_space(snd) 644 previous_label = labelled_snds.detect do |n| 645 n.first == snd 646 end 647 unless previous_label 648 app = main_widgets[Top_level_application] 649 minibuffer = sound_widgets(snd)[Minibuffer] 650 name_form = RXtParent(minibuffer) 651 space = kmg(disk_kspace(file_name(snd))) 652 str = RXmStringCreateLocalized(space) 653 new_label = RXtCreateManagedWidget("space:", 654 RxmLabelWidgetClass, name_form, 655 [RXmNbackground, basic_color, 656 RXmNleftAttachment, RXmATTACH_WIDGET, 657 RXmNleftWidget, minibuffer, 658 RXmNlabelString, str, 659 RXmNrightAttachment, RXmATTACH_NONE, 660 RXmNtopAttachment, RXmATTACH_FORM]) 661 RXmStringFree(str) 662 previous_label = [snd, new_label, app] 663 labelled_snds.push(previous_label) 664 end 665 show_label = lambda do |data, id| 666 if sound?(data.first) 667 space = kmg(disk_kspace(file_name(data.first))) 668 str = RXmStringCreateLocalized(space) 669 RXtSetValues(data[1], [RXmNlabelString, str]) 670 RXmStringFree(str) 671 RXtAppAddTimeOut(data[2], 10000, show_label, data) 672 end 673 end 674 RXtAppAddTimeOut(previous_label[2], 10000, show_label, previous_label) 675 end 676 # $after_open_hook.add_hook!("disk-space", &method(:show_disk_space).to_proc) 677 678 add_help(:menu_option, 679 "menu_option(name) \ 680Finds the widget associated with a given menu item NAME.") 681 def menu_option(name) 682 menu_widgets.cdr.each do |top_menu| 683 each_child(top_menu) do |w| 684 option_holder = RXtGetValues(w, [RXmNsubMenuId, 0]).cadr 685 each_child(option_holder) do |menu| 686 if name == RXtName(menu) 687 return menu 688 else 689 if RXmIsCascadeButton(menu) 690 options = RXtGetValues(menu, [RXmNsubMenuId, 0]).cadr 691 each_child(options) do |inner_menu| 692 if name == RXtName(inner_menu) 693 return inner_menu 694 end 695 end 696 end 697 end 698 end 699 end 700 end 701 Snd.raise(:no_such_menu, name) 702 end 703 704 add_help(:set_main_color_of_widget, 705 "set_main_color_of_widget(widget) \ 706Sets the background color of WIDGET.") 707 def set_main_color_of_widget(w) 708 each_child(w) do |n| 709 if RXtIsWidget(n) 710 if RXmIsScrollBar(n) 711 RXmChangeColor(n, position_color) 712 else 713 RXmChangeColor(n, basic_color) 714 end 715 end 716 end 717 end 718 719 # 720 # add_mark_pane 721 # 722 # Adds a pane to each channel giving the current mark locations 723 # (sample values). These can be edited to move the mark, or deleted 724 # to delete the mark. Can't use channel-property here because the 725 # widget lists are permanent (just unmanaged) 726 727 $including_mark_pane = false # for prefs 728 729 def add_mark_pane 730 mark_pane = Mark_pane.new 731 $mark_hook.add_hook!("remark") do |id, snd, chn, reason| 732 mark_pane.make_list(snd, chn) 733 end 734 $close_hook.add_hook!("unremark") do |snd| 735 channels(snd).times do |chn| 736 mark_pane.deactivate_channel(snd, chn) 737 end 738 end 739 $after_open_hook.add_hook!("open-remarks") do |snd| 740 chans(snd).times do |chn| 741 after_edit_hook(snd, chn).add_hook!("open-remarks") do | | 742 if RWidget?(mark_pane.list(snd, chn)) 743 mark_pane.make_list(snd, chn) 744 end 745 end 746 undo_hook(snd, chn).add_hook!("open-remarks") do | | 747 if RWidget?(mark_pane.list(snd, chn)) 748 mark_pane.make_list(snd, chn) 749 end 750 end 751 end 752 end 753 $update_hook.add_hook!("") do |snd| 754 lambda do |update_snd| 755 chans(update_snd).times do |chn| 756 mark_pane.make_list(update_snd, chn) 757 end 758 end 759 end 760 $including_mark_pane = true 761 end 762 763 class Mark_pane 764 def initialize 765 @mark_list_lengths = [] 766 @mark_lists = [] 767 end 768 769 def make_list(snd, chn) 770 deactivate_channel(snd, chn) 771 unless RWidget?(list(snd, chn)) 772 mark_box = add_channel_pane(snd, chn, "mark-box", RxmFormWidgetClass, 773 [RXmNbackground, basic_color, 774 RXmNorientation, RXmVERTICAL, 775 RXmNpaneMinimum, 100, 776 RXmNbottomAttachment, RXmATTACH_FORM]) 777 ls = [RXmNbackground, highlight_color, 778 RXmNleftAttachment, RXmATTACH_FORM, 779 RXmNrightAttachment, RXmATTACH_FORM, 780 RXmNalignment, RXmALIGNMENT_CENTER, 781 RXmNtopAttachment, RXmATTACH_FORM] 782 mark_label = RXtCreateManagedWidget("Marks", 783 RxmLabelWidgetClass, mark_box, ls) 784 ls = [RXmNbackground, basic_color, 785 RXmNscrollingPolicy, RXmAUTOMATIC, 786 RXmNscrollBarDisplayPolicy, RXmSTATIC, 787 RXmNleftAttachment, RXmATTACH_FORM, 788 RXmNrightAttachment, RXmATTACH_FORM, 789 RXmNtopAttachment, RXmATTACH_WIDGET, 790 RXmNtopWidget, mark_label, 791 RXmNbottomAttachment, RXmATTACH_FORM] 792 mark_scr = RXtCreateManagedWidget("mark-scr", 793 RxmScrolledWindowWidgetClass, 794 mark_box, ls) 795 ls = [RXmNorientation, RXmVERTICAL, 796 RXmNtopAttachment, RXmATTACH_FORM, 797 RXmNbottomAttachment, RXmATTACH_FORM, 798 RXmNspacing, 0] 799 mlist = RXtCreateManagedWidget("mark-list", 800 RxmRowColumnWidgetClass, mark_scr, ls) 801 set_main_color_of_widget(mark_scr) 802 RXtSetValues(mark_box, [RXmNpaneMinimum, 1]) 803 set_list(snd, chn, mlist) 804 end 805 lst = list(snd, chn) 806 new_marks = Snd.marks(snd, chn) 807 current_list_length = @mark_list_lengths.length 808 if new_marks.length > current_list_length 809 current_list_length.upto(new_marks.length) do 810 tf = RXtCreateWidget("field", RxmTextFieldWidgetClass, lst, 811 [RXmNbackground, basic_color]) 812 RXtAddCallback(tf, RXmNfocusCallback, 813 lambda do |w, c, i| 814 RXtSetValues(w, [RXmNbackground, text_focus_color]) 815 end) 816 RXtAddCallback(tf, RXmNlosingFocusCallback, 817 lambda do |w, c, i| 818 RXtSetValues(w, [RXmNbackground, basic_color]) 819 end) 820 RXtAddCallback(tf, RXmNactivateCallback, 821 lambda do |w, c, i| 822 id = RXtGetValues(w, [RXmNuserData, 0]).cadr 823 txt = RXtGetValues(w, [RXmNvalue, 0]).cadr 824 if string?(txt) and txt.length > 0 825 set_mark_sample(id, txt.to_i) 826 else 827 delete_mark(id) 828 end 829 RXtSetValues(w, [RXmNbackground, basic_color]) 830 end) 831 RXtAddEventHandler(tf, REnterWindowMask, false, 832 lambda do |w, c, i, f| 833 $mouse_enter_text_hook.call(w) 834 end) 835 RXtAddEventHandler(tf, RLeaveWindowMask, false, 836 lambda do |w, c, i, f| 837 $mouse_leave_text_hook.call(w) 838 end) 839 end 840 end 841 set_length(snd, chn, new_marks.length) 842 RXtGetValues(lst, [RXmNchildren, 0], 1).cadr.each do |n| 843 break if new_marks.empty? 844 if RXmIsTextField(n) 845 mk = new_marks.shift 846 RXtSetValues(n, [RXmNvalue, mark_sample(mk).to_s, RXmNuserData, mk]) 847 RXtManageChild(n) 848 end 849 end 850 false 851 end 852 853 def deactivate_channel(snd, chn) 854 if length(snd, chn) > 0 and RWidget?(list(snd, chn)) 855 RXtGetValues(list(snd, chn), [RXmNchildren, 0], 1).cadr.each do |n| 856 RXtUnmanageChild(n) 857 end 858 end 859 end 860 861 def list(snd, chn) 862 find(snd, chn, @mark_lists) 863 end 864 865 private 866 def find(snd, chn, dats) 867 val = dats.detect do |dat| 868 snd == dat.car and chn == dat.cadr 869 end 870 if val 871 val.caddr 872 else 873 false 874 end 875 end 876 877 def length(snd, chn) 878 find(snd, chn, @mark_list_lengths) or 0 879 end 880 881 def set_length(snd, chn, len) 882 @mark_list_lengths.delete_if do |dat| 883 snd == dat.car and chn == dat.cadr 884 end 885 @mark_list_lengths.push([snd, chn, len]) 886 end 887 888 def set_list(snd, chn, wid) 889 @mark_lists.push([snd, chn, wid]) 890 end 891 end 892 893 class Variable_display 894 include Snd_XM 895 896 def initialize(page_name, variable_name) 897 @name = page_name 898 @variable = variable_name 899 @@dialog = nil unless defined? @@dialog 900 @@pages = {} unless defined? @@pages 901 @@notebook = nil unless defined? @@notebook 902 @widget = nil 903 @snd = false 904 @data = nil 905 @default_background = nil 906 create 907 end 908 attr_reader :snd, :data 909 910 def dialog_widget 911 @@dialog 912 end 913 914 def inspect 915 format("%s.new(%s, %s)", self.class, @name, @variable) 916 end 917 918 def make_dialog 919 xdismiss = RXmStringCreateLocalized("Dismiss") 920 titlestr = RXmStringCreateLocalized("Variables") 921 @@dialog = RXmCreateTemplateDialog(main_widgets[Top_level_shell], 922 "variables-dialog", 923 [RXmNokLabelString, xdismiss, 924 RXmNautoUnmanage, false, 925 RXmNdialogTitle, titlestr, 926 RXmNresizePolicy, RXmRESIZE_GROW, 927 RXmNnoResize, false, 928 RXmNtransient, false, 929 RXmNheight, 400, 930 RXmNwidth, 400, 931 RXmNbackground, basic_color]) 932 RXtAddCallback(@@dialog, RXmNokCallback, 933 lambda do |w, c, i| 934 RXtUnmanageChild(@@dialog) 935 end) 936 RXmStringFree(xdismiss) 937 RXmStringFree(titlestr) 938 ls = [RXmNleftAttachment, RXmATTACH_FORM, 939 RXmNrightAttachment, RXmATTACH_FORM, 940 RXmNtopAttachment, RXmATTACH_FORM, 941 RXmNbottomAttachment, RXmATTACH_WIDGET, 942 RXmNbottomWidget, 943 RXmMessageBoxGetChild(@@dialog, RXmDIALOG_SEPARATOR), 944 RXmNbackground, basic_color, 945 RXmNframeBackground, zoom_color, 946 RXmNbindingWidth, 14] 947 @@notebook = RXtCreateManagedWidget("variables-notebook", 948 RxmNotebookWidgetClass, @@dialog, ls) 949 RXtManageChild(@@dialog) 950 c = RDefaultScreenOfDisplay(RXtDisplay(@@dialog)) 951 @default_background = RWhitePixelOfScreen(c) 952 end 953 954 def create 955 unless RWidget?(@@dialog) 956 make_dialog 957 end 958 unless @@pages[@name] 959 panes = RXtCreateManagedWidget(@name, RxmPanedWindowWidgetClass, 960 @@notebook, []) 961 simple_cases = RXtCreateManagedWidget(@name, 962 RxmRowColumnWidgetClass, panes, 963 [RXmNorientation, RXmVERTICAL, 964 RXmNpaneMinimum, 30, 965 RXmNbackground, basic_color]) 966 RXtCreateManagedWidget(@name, RxmPushButtonWidgetClass, @@notebook, 967 [RXmNnotebookChildType, RXmMAJOR_TAB, 968 RXmNbackground, basic_color]) 969 @@pages[@name] = [@name, panes, simple_cases] 970 end 971 @@pages[@name] 972 end 973 974 def close 975 RXtUnmanageChild(@@dialog) 976 end 977 978 def reset 979 end 980 end 981 982 class Variable_display_text < Variable_display 983 def create 984 page_info = super 985 row_pane = page_info[2] 986 var_label = @variable + ":" 987 row = RXtCreateManagedWidget(@variable + "-row", 988 RxmRowColumnWidgetClass, row_pane, 989 [RXmNorientation, RXmHORIZONTAL, 990 RXmNbackground, basic_color]) 991 RXtCreateManagedWidget(var_label, RxmLabelWidgetClass, row, 992 [RXmNbackground, basic_color]) 993 @widget = RXtCreateManagedWidget(@variable + "-value", 994 RxmTextFieldWidgetClass, row, 995 [RXmNeditable, false, 996 RXmNresizeWidth, true, 997 RXmNbackground, @default_background]) 998 end 999 1000 def display(var) 1001 old_str = RXmTextFieldGetString(@widget) 1002 new_str = var.to_s 1003 if old_str != new_str 1004 RXmTextFieldSetString(@widget, new_str) 1005 if RXtIsManaged(@widget) 1006 RXmUpdateDisplay(@widget) 1007 end 1008 end 1009 var 1010 end 1011 end 1012 1013 class Variable_display_scale < Variable_display 1014 def initialize(page_name, variable_name, range = [0.0, 1.0]) 1015 @range = range 1016 super(page_name, variable_name) 1017 end 1018 1019 def inspect 1020 format("%s.new(%s, %s, %s)", self.class, @name, @variable, @range) 1021 end 1022 1023 def create 1024 page_info = super() 1025 row_pane = page_info[2] 1026 var_label = @variable + ":" 1027 title = RXmStringCreateLocalized(var_label) 1028 @widget = RXtCreateManagedWidget(@variable, 1029 RxmScaleWidgetClass, row_pane, 1030 [RXmNbackground, basic_color, 1031 RXmNslidingMode, RXmTHERMOMETER, 1032 RXmNminimum, (100.0 * @range[0]).floor, 1033 RXmNmaximum, (100.0 * @range[1]).floor, 1034 RXmNdecimalPoints, 2, 1035 RXmNtitleString, title, 1036 RXmNorientation, RXmHORIZONTAL, 1037 RXmNshowValue, RXmNEAR_BORDER]) 1038 wid = Snd.catch(:no_such_widget) do 1039 find_child(@widget, "Scrollbar") 1040 end.first 1041 if widget?(wid) 1042 RXtVaSetValues(wid, [RXmNtroughColor, red_pixel]) 1043 end 1044 RXmStringFree(title) 1045 end 1046 1047 def display(var) 1048 RXmScaleSetValue(@widget, (100.0 * var).floor) 1049 var 1050 end 1051 end 1052 1053 class Variable_display_graph < Variable_display 1054 def create 1055 page_info = super 1056 pane = page_info[1] 1057 var_label = @variable + ":" 1058 form = RXtCreateManagedWidget(var_label, RxmFormWidgetClass, pane, 1059 [RXmNpaneMinimum, 100]) 1060 @snd = make_variable_graph(form, @variable + ": time", 1061 2048, mus_srate.to_i) 1062 @data = channel_data(@snd, 0) 1063 end 1064 1065 def display(var) 1066 frames = @data.length 1067 loc = cursor(snd, 0) 1068 @data[loc] = var 1069 if time_graph?(@snd) 1070 update_time_graph(@snd) 1071 end 1072 if transform_graph?(@snd) 1073 update_transform_graph(@snd) 1074 end 1075 if loc + 1 == frames 1076 set_cursor(0, @snd, 0) 1077 else 1078 set_cursor(loc + 1, @snd, 0) 1079 end 1080 var 1081 end 1082 1083 def reset 1084 set_cursor(0, @snd, 0) 1085 @data.fill(0.0) 1086 end 1087 end 1088 1089 class Variable_display_spectrum < Variable_display 1090 def create 1091 page_info = super 1092 pane = page_info[1] 1093 var_label = @variable + ":" 1094 form = RXtCreateManagedWidget(var_label, RxmFormWidgetClass, pane, 1095 [RXmNpaneMinimum, 100]) 1096 @snd = make_variable_graph(form, @variable, 2048, mus_srate.to_i) 1097 set_time_graph?(false, @snd, 0) 1098 set_transform_graph?(true, @snd, 0) 1099 set_x_axis_label(@variable + ": frequency", @snd, 0, Transform_graph) 1100 @data = channel_data(@snd, 0) 1101 end 1102 1103 def display(var) 1104 frames = @data.length 1105 loc = cursor(snd, 0) 1106 @data[loc] = var 1107 if time_graph?(@snd) 1108 update_time_graph(@snd) 1109 end 1110 if transform_graph?(@snd) 1111 update_transform_graph(@snd) 1112 end 1113 if loc + 1 == frames 1114 set_cursor(0, @snd, 0) 1115 else 1116 set_cursor(loc + 1, @snd, 0) 1117 end 1118 var 1119 end 1120 1121 def reset 1122 set_cursor(0, @snd, 0) 1123 @data.fill(0.0) 1124 end 1125 end 1126 1127 def make_variable_display(page_name, variable_name, 1128 type = :text, range = [0.0, 1.0]) 1129 case type 1130 when :text 1131 Variable_display_text.new(page_name, variable_name) 1132 when :scale 1133 Variable_display_scale.new(page_name, variable_name, range) 1134 when :graph 1135 Variable_display_graph.new(page_name, variable_name) 1136 when :spectrum 1137 Variable_display_spectrum.new(page_name, variable_name) 1138 else 1139 nil 1140 end 1141 end 1142 1143 def variable_display(vd, var) 1144 vd.display(var) 1145 end 1146 1147 def variable_display_close(vd) 1148 vd.close 1149 end 1150 1151 def variable_display_reset(vd) 1152 vd.reset 1153 end 1154 1155 def variable_display?(vd) 1156 vd.kind_of?(Variable_display) 1157 end 1158 1159 class Scale_widget 1160 include Snd_XM 1161 1162 def initialize(parent) 1163 @parent = parent 1164 @scale = nil 1165 @label = nil 1166 end 1167 attr_reader :scale, :label 1168 1169 def add_scale(title, low, init, high, scale, kind) 1170 xtitle = string2compound(title) 1171 rc = RXtCreateManagedWidget("rc", RxmRowColumnWidgetClass, @parent, 1172 [RXmNorientation, RXmVERTICAL, 1173 RXmNbackground, highlight_color]) 1174 case kind 1175 when :log 1176 s = format("%1.2f", init), 1177 @label = RXtCreateManagedWidget(s, RxmLabelWidgetClass, rc, 1178 [RXmNalignment, RXmALIGNMENT_BEGINNING, 1179 RXmNbackground, basic_color]) 1180 @scale = general_scale(rc, title, xtitle) 1181 RXtVaSetValues(@scale, 1182 [RXmNmaximum, $log_scale_ticks, 1183 RXmNvalue, scale_log2linear(low, init, high).round]) 1184 RXtAddCallback(@scale, RXmNvalueChangedCallback, 1185 lambda do |w, c, i| 1186 change_label(@label, 1187 scale_log_label(low, Rvalue(i), high)) 1188 end) 1189 RXtAddCallback(@scale, RXmNdragCallback, 1190 lambda do |w, c, i| 1191 change_label(@label, 1192 scale_log_label(low, Rvalue(i), high)) 1193 end) 1194 when :semi 1195 s = format("semitones: %d", ratio2semitones(init)) 1196 @label = RXtCreateManagedWidget(s, RxmLabelWidgetClass, rc, 1197 [RXmNalignment, RXmALIGNMENT_BEGINNING, 1198 RXmNbackground, basic_color]) 1199 @scale = general_scale(rc, title, xtitle) 1200 RXtVaSetValues(@scale, 1201 [RXmNmaximum, 2 * $semi_range, 1202 RXmNvalue, $semi_range + ratio2semitones(init)]) 1203 RXtAddCallback(@scale, RXmNvalueChangedCallback, 1204 lambda do |w, c, i| 1205 change_label(@label, semi_scale_label(Rvalue(i))) 1206 end) 1207 RXtAddCallback(@scale, RXmNdragCallback, 1208 lambda do |w, c, i| 1209 change_label(@label, semi_scale_label(Rvalue(i))) 1210 end) 1211 else 1212 @scale = linear_scale(rc, title, xtitle, low, init, high, scale) 1213 end 1214 RXmStringFree(xtitle) 1215 end 1216 1217 private 1218 def linear_scale(parent, title, xtitle, low, init, high, scale) 1219 RXtCreateManagedWidget(title, RxmScaleWidgetClass, parent, 1220 [RXmNorientation, RXmHORIZONTAL, 1221 RXmNshowValue, true, 1222 RXmNminimum, (low * scale).round, 1223 RXmNmaximum, (high * scale).round, 1224 RXmNtitleString, xtitle, 1225 RXmNbackground, basic_color, 1226 RXmNvalue, (init * scale).round, 1227 RXmNdecimalPoints, case scale 1228 when 1000 1229 3 1230 when 100 1231 2 1232 when 10 1233 1 1234 else 1235 0 1236 end]) 1237 end 1238 1239 def general_scale(parent, title, xtitle) 1240 RXtCreateManagedWidget(title, RxmScaleWidgetClass, parent, 1241 [RXmNorientation, RXmHORIZONTAL, 1242 RXmNshowValue, false, 1243 RXmNminimum, 0, 1244 RXmNdecimalPoints, 0, 1245 RXmNtitleString, xtitle, 1246 RXmNbackground, basic_color]) 1247 end 1248 end 1249 1250 class Dialog < Dialog_base 1251 include Snd_XM 1252 1253 def create_dialog 1254 xdismiss = RXmStringCreateLocalized(@dismiss) 1255 xhelp = RXmStringCreateLocalized(@help) 1256 xok = RXmStringCreateLocalized(@doit) 1257 titlestr = RXmStringCreateLocalized(@label) 1258 @dialog = RXmCreateTemplateDialog(main_widgets[Top_level_shell], @label, 1259 [RXmNcancelLabelString, xdismiss, 1260 RXmNhelpLabelString, xhelp, 1261 RXmNokLabelString, xok, 1262 RXmNautoUnmanage, false, 1263 RXmNdialogTitle, titlestr, 1264 RXmNresizePolicy, RXmRESIZE_GROW, 1265 RXmNnoResize, false, 1266 RXmNbackground, basic_color, 1267 RXmNtransient, false]) 1268 RXmStringFree(xhelp) 1269 RXmStringFree(xok) 1270 RXmStringFree(xdismiss) 1271 RXmStringFree(titlestr) 1272 if defined?(R_XEditResCheckMessages()) 1273 RXtAddEventHandler(RXtParent(@dialog), 0, true, 1274 lambda do |w, c, i, f| 1275 R_XEditResCheckMessages(w, c, i, f) 1276 end) 1277 end 1278 [[RXmDIALOG_HELP_BUTTON, highlight_color], 1279 [RXmDIALOG_CANCEL_BUTTON, highlight_color], 1280 [RXmDIALOG_OK_BUTTON, highlight_color]].each do |button, color| 1281 RXtVaSetValues(RXmMessageBoxGetChild(@dialog, button), 1282 [RXmNarmColor, selection_color, 1283 RXmNbackground, color]) 1284 end 1285 RXtAddCallback(@dialog, RXmNcancelCallback, 1286 lambda do |w, c, i| 1287 RXtUnmanageChild(@dialog) 1288 end) 1289 RXtAddCallback(@dialog, RXmNhelpCallback, 1290 lambda do |w, c, i| 1291 @help_cb.call(w, c, i) 1292 end) 1293 RXtAddCallback(@dialog, RXmNokCallback, 1294 lambda do |w, c, i| 1295 @ok_cb.call(w, c, i) 1296 end) 1297 vals = [RXmNbackground, highlight_color, 1298 RXmNforeground, black_pixel, 1299 RXmNarmColor, selection_color] 1300 if @clear_cb 1301 @clear_button = RXtCreateManagedWidget(@clear, 1302 RxmPushButtonWidgetClass, 1303 @dialog, vals) 1304 RXtAddCallback(@clear_button, RXmNactivateCallback, 1305 lambda do |w, c, i| 1306 @clear_cb.call(w, c, i) 1307 end) 1308 end 1309 if @reset_cb 1310 @reset_button = RXtCreateManagedWidget(@reset, 1311 RxmPushButtonWidgetClass, 1312 @dialog, vals) 1313 RXtAddCallback(@reset_button, RXmNactivateCallback, 1314 lambda do |w, c, i| 1315 @reset_cb.call(w, c, i) 1316 end) 1317 end 1318 @help_button = RXmMessageBoxGetChild(@dialog, RXmDIALOG_HELP_BUTTON) 1319 @dismiss_button = RXmMessageBoxGetChild(@dialog, RXmDIALOG_CANCEL_BUTTON) 1320 @okay_button = RXmMessageBoxGetChild(@dialog, RXmDIALOG_OK_BUTTON) 1321 if @target_cb 1322 RXtSetSensitive(@okay_button, @target_cb.call()) 1323 $effects_hook.add_hook!("create-dialog-target") do | | 1324 RXtSetSensitive(@okay_button, @target_cb.call()) 1325 end 1326 else 1327 RXtSetSensitive(@okay_button, (not Snd.sounds.empty?)) 1328 $effects_hook.add_hook!("create-dialog-target") do | | 1329 RXtSetSensitive(@okay_button, (not Snd.sounds.empty?)) 1330 end 1331 end 1332 @parent = RXtCreateManagedWidget("pane", 1333 RxmPanedWindowWidgetClass, @dialog, 1334 [RXmNsashHeight, 1, 1335 RXmNsashWidth, 1, 1336 RXmNbackground, basic_color, 1337 RXmNseparatorOn, true, 1338 RXmNalignment, RXmALIGNMENT_BEGINNING, 1339 RXmNorientation, RXmVERTICAL]) 1340 end 1341 1342 # kind :log, :semi, :linear 1343 # returns instance of Scale_widget not widget 1344 # so we can access the widget and label if needed 1345 # slider = @dialog.add_slider(...) 1346 # slider.scale --> widget 1347 # slider.label --> label 1348 def add_slider(title, low, init, high, 1349 scale = 1, kind = :linear, parent = @parent, &func) 1350 slider = Scale_widget.new(parent) 1351 slider.add_scale(title, low, init, high, scale, kind) 1352 unless proc?(func) and func.arity == 3 1353 func = lambda do |w, c, i| 1354 func.call 1355 end 1356 end 1357 RXtAddCallback(slider.scale, RXmNvalueChangedCallback, func) 1358 slider 1359 end 1360 1361 # change_cb.arity == 1 1362 def add_toggle(label = "truncate at end", value = true, &change_cb) 1363 button = RXtCreateManagedWidget(label, 1364 RxmToggleButtonWidgetClass, @parent, 1365 [RXmNbackground, basic_color, 1366 RXmNalignment, RXmALIGNMENT_BEGINNING, 1367 RXmNset, value, 1368 RXmNselectColor, yellow_pixel]) 1369 RXtAddCallback(button, RXmNvalueChangedCallback, 1370 lambda do |w, c, i| 1371 change_cb.call(Rset(i)) 1372 end) 1373 h = get_xtvalue(button, RXmNheight) 1374 h += (h * 0.1).round 1375 RXtVaSetValues(button, [RXmNpaneMinimum, h, RXmNpaneMaximum, h]) 1376 button 1377 end 1378 1379 # target_cb.arity == 1 1380 def add_target(labels = [["entire sound", :sound, true], 1381 ["selection", :selection, false], 1382 ["between marks", :marks, false]], &target_cb) 1383 RXtCreateManagedWidget("sep", RxmSeparatorWidgetClass, @parent, 1384 [RXmNorientation, RXmHORIZONTAL, 1385 RXmNseparatorType, RXmSHADOW_ETCHED_OUT, 1386 RXmNbackground, basic_color]) 1387 ls = [RXmNorientation, RXmHORIZONTAL, 1388 RXmNbackground, basic_color, 1389 RXmNradioBehavior, true, 1390 RXmNradioAlwaysOne, true, 1391 RXmNbottomAttachment, RXmATTACH_FORM, 1392 RXmNleftAttachment, RXmATTACH_FORM, 1393 RXmNrightAttachment, RXmATTACH_FORM, 1394 RXmNentryClass, RxmToggleButtonWidgetClass, 1395 RXmNisHomogeneous, true] 1396 rc = RXtCreateManagedWidget("rc", RxmRowColumnWidgetClass, @parent, ls) 1397 labels.map do |name, type, on| 1398 RXtCreateManagedWidget(name, RxmToggleButtonWidgetClass, rc, 1399 [RXmNbackground, basic_color, 1400 RXmNselectColor, yellow_pixel, 1401 RXmNset, on, 1402 RXmNindicatorType, RXmONE_OF_MANY_ROUND, 1403 RXmNarmCallback, [lambda do |w, c, i| 1404 target_cb.call(type) 1405 end, false]]) 1406 end 1407 rc 1408 end 1409 1410 def add_frame(args = []) 1411 RXtCreateManagedWidget("frame", RxmFrameWidgetClass, @parent, args) 1412 # [RXmNshadowThickness, 4, RXmNshadowType, RXmSHADOW_ETCHED_OUT] 1413 end 1414 1415 def add_label(label, args = []) 1416 RXtCreateManagedWidget(label, RxmLabelWidgetClass, @parent, 1417 [RXmNalignment, RXmALIGNMENT_BEGINNING, 1418 RXmNbackground, basic_color] + args) 1419 end 1420 1421 def add_textfield(string, label = nil, columns = 80, &activate_cb) 1422 rc = RXtCreateManagedWidget("rc", RxmRowColumnWidgetClass, @parent, 1423 [RXmNorientation, RXmVERTICAL, 1424 RXmNbackground, basic_color]) 1425 if string?(label) 1426 RXtCreateManagedWidget(label, RxmLabelWidgetClass, rc, 1427 [RXmNalignment, RXmALIGNMENT_BEGINNING, 1428 RXmNbackground, basic_color]) 1429 end 1430 text_field = RXtCreateManagedWidget("text", RxmTextFieldWidgetClass, rc, 1431 [RXmNvalue, string, 1432 RXmNresizeWidth, false, 1433 RXmNcolumns, columns, 1434 RXmNbackground, basic_color]) 1435 RXtAddCallback(text_field, RXmNactivateCallback, activate_cb) 1436 RXtAddCallback(text_field, RXmNfocusCallback, 1437 lambda do |w, c, i| 1438 RXtSetValues(w, [RXmNbackground, text_focus_color]) 1439 end) 1440 RXtAddCallback(text_field, RXmNlosingFocusCallback, 1441 lambda do |w, c, i| 1442 RXtSetValues(w, [RXmNbackground, basic_color]) 1443 end) 1444 RXtAddEventHandler(text_field, REnterWindowMask, false, 1445 lambda do |w, c, i, f| 1446 $mouse_enter_text_hook.call(w) 1447 end) 1448 RXtAddEventHandler(text_field, RLeaveWindowMask, false, 1449 lambda do |w, c, i, f| 1450 $mouse_leave_text_hook.call(w) 1451 end) 1452 text_field 1453 end 1454 1455 def add_text(*args) 1456 rows, columns, wordwrap, value, horizontal = optkey(args, 1457 [:rows, 16], 1458 [:columns, 60], 1459 [:wordwrap, true], 1460 [:value, ""], 1461 [:scroll_horizontal, 1462 false]) 1463 text = RXmCreateScrolledText(@parent, "text", 1464 [RXmNtopAttachment, RXmATTACH_WIDGET, 1465 RXmNeditMode, RXmMULTI_LINE_EDIT, 1466 RXmNrows, rows, 1467 RXmNcolumns, columns, 1468 RXmNwordWrap, wordwrap, 1469 RXmNscrollHorizontal, horizontal, 1470 RXmNvalue, value, 1471 RXmNbackground, basic_color]) 1472 RXtAddCallback(text, RXmNfocusCallback, 1473 lambda do |w, c, i| 1474 RXtSetValues(w, [RXmNbackground, text_focus_color]) 1475 end) 1476 RXtAddCallback(text, RXmNlosingFocusCallback, 1477 lambda do |w, c, i| 1478 RXtSetValues(w, [RXmNbackground, basic_color]) 1479 end) 1480 RXtAddEventHandler(text, REnterWindowMask, false, 1481 lambda do |w, c, i, f| 1482 $mouse_enter_text_hook.call(w) 1483 end) 1484 RXtAddEventHandler(text, RLeaveWindowMask, false, 1485 lambda do |w, c, i, f| 1486 $mouse_leave_text_hook.call(w) 1487 end) 1488 RXtManageChild(text) 1489 text 1490 end 1491 end 1492end 1493 1494module Snd_XM 1495 include Snd_Motif 1496 alias is_managed is_managed? 1497end 1498 1499=begin 1500add_channel_pane(0, 0, "new-pane", RxmDrawingAreaWidgetClass, 1501 [RXmNbackground, graph_color, RXmNforeground, data_color]) 1502=end 1503 1504# SND_MAIN_MENU (for a similar popup menu class see popup.rb) 1505# 1506# make_snd_menu(name, args) do ... end 1507# 1508# class Menu 1509# initialize(name, menu, args) 1510# menu 1511# each_entry do |child| ... end 1512# change_menu_color(new_color) 1513# 1514# class Snd_main_menu < Menu 1515# initialize(name, parent, args) do ... end 1516# menu_number 1517# entry(klass, *rest) or entry(name) do ... end 1518# separator 1519# cascade(name, args) do ... end 1520# 1521# `Snd_main_menu#entry(arg, *rest, &body)': If ARG is of kind Class, 1522# `entry' calls klass.new(*rest), so you can set initialize values 1523# (e.g. the label or other args). If ARG is not of kind Class it is 1524# taken as a label string and a block must exist. Classes for the 1525# menu must have a method `post_dialog' and `inspect'. `inspect' 1526# shows the values in the menu label. See the various examples in 1527# effects.rb. 1528# 1529# class Foo 1530# def initialize(label, val1, val2) 1531# @label = label 1532# @val1 = val1 1533# @val2 = val2 1534# @dialog = nil 1535# ... 1536# end 1537# 1538# def inspect 1539# format("%s (%.3f %.3f", @label, @val1, @val2) 1540# end 1541# 1542# def post_dialog 1543# ... 1544# unless @dialog.kind_of?(Dialog) and RWidget?(@dialog.dialog) 1545# ... 1546# @dialog = make_dialog(@label, 1547# :info, "Help text", 1548# :reset_cb, lambda do |w, c, i| 1549# ... (reset your values) 1550# end) do |w, c, i| 1551# ... (main action) 1552# end 1553# ... 1554# end 1555# activate_dialog(@dialog.dialog) 1556# end 1557# end 1558# 1559# make_snd_menu("Foo Menu") do 1560# entry(Foo, 3.14, 0.0) 1561# end 1562 1563=begin 1564# example menu using Effects (see effects.rb and new-effects.scm) 1565require "effects" 1566 1567make_snd_menu("Effects") do 1568 cascade("Amplitude Effects") do 1569 entry(Gain, "Gain") 1570 entry(Normalize, "Normalize") 1571 entry(Gate, "Gate") 1572 end 1573 cascade("Delay Effects") do 1574 entry(Echo, "Echo") 1575 entry(Filtered_echo, "Filtered echo") 1576 entry(Modulated_echo, "Modulated echo") 1577 end 1578 separator 1579 entry("Octave-down") do 1580 down_oct 1581 end 1582 entry("Remove DC") do 1583 lastx = lasty = 0.0 1584 map_chan(lambda do |inval| 1585 lasty = inval + (0.999 * lasty - lastx) 1586 lastx = inval 1587 lasty 1588 end) 1589 end 1590 entry("Spiker") do 1591 spike 1592 end 1593end 1594=end 1595 1596class Menu 1597 include Snd_XM 1598 1599 def initialize(name, menu, args) 1600 @label = name 1601 @menu = menu 1602 @args = args 1603 end 1604 attr_reader :menu 1605 1606 def inspect 1607 format("#<%s: label: %p, menu: %p, args: %p>", 1608 self.class, @label, @menu, @args) 1609 end 1610 1611 def entry(name, *rest, &body) 1612 child = false 1613 args, widget_class = optkey(rest, 1614 [:args, @args], 1615 [:widget_class, RxmPushButtonWidgetClass]) 1616 child = RXtCreateManagedWidget(name, widget_class, @menu, args) 1617 case widget_class 1618 when RxmPushButtonWidgetClass 1619 RXtAddCallback(child, RXmNactivateCallback, body) 1620 when RxmToggleButtonWidgetClass 1621 RXtAddCallback(child, RXmNvalueChangedCallback, body) 1622 end 1623 child 1624 end 1625 1626 def label(name, args = @args) 1627 RXtCreateManagedWidget(name, RxmLabelWidgetClass, @menu, args) 1628 end 1629 1630 def separator(single = :single) 1631 line = (single == :double ? RXmDOUBLE_LINE : RXmSINGLE_LINE) 1632 RXtCreateManagedWidget("s", RxmSeparatorWidgetClass, @menu, 1633 [RXmNseparatorType, line]) 1634 end 1635 1636 def each_entry(&body) 1637 each_child(@menu, &body) 1638 end 1639 1640 # $menu.change_menu_color("ivory2") 1641 # $menu.change_menu_color([0.93, 0.93, 0.87]) 1642 # require 'rgb' 1643 # $menu.change_menu_color(Ivory2) 1644 def change_menu_color(new_color) 1645 color_pixel = get_color(new_color) 1646 each_child(@menu) do |child| 1647 RXmChangeColor(child, color_pixel) 1648 end 1649 end 1650end 1651 1652class Snd_main_menu < Menu 1653 def initialize(name, parent, args, &body) 1654 if widget? parent 1655 @menu_number = -1 1656 super(name, parent, args) 1657 else 1658 @menu_number = add_to_main_menu(name, lambda do | | end) 1659 super(name, main_menu(@menu_number), args) 1660 instance_eval(&body) if block_given? 1661 end 1662 end 1663 attr_reader :menu_number 1664 1665 def entry(arg, *rest, &body) 1666 if arg.class == Class 1667 menu = arg.new(*rest) 1668 if menu.respond_to?(:post_dialog) 1669 child = RXtCreateManagedWidget(rest[0].to_s, 1670 RxmPushButtonWidgetClass, @menu, @args) 1671 RXtAddCallback(child, RXmNactivateCallback, 1672 lambda do |w, c, i| 1673 menu.post_dialog 1674 end) 1675 child 1676 else 1677 Snd.raise(:snd_x_error, arg.class, 1678 "class does not respond to `post_dialog'") 1679 end 1680 else 1681 if block_given? 1682 add_to_menu(@menu_number, arg, body) 1683 else 1684 Snd.raise(:wrong_number_of_args, "no block given") 1685 end 1686 end 1687 end 1688 1689 def separator 1690 add_to_menu(@menu_number, false, false) 1691 end 1692 1693 def cascade(name, args = @args, &body) 1694 cas = Cascade.new(name, @menu, args) 1695 cas.instance_eval(&body) if block_given? 1696 cas 1697 end 1698 1699 class Cascade < Snd_main_menu 1700 def initialize(name, parent, args) 1701 super 1702 @children = [] 1703 @menu = RXmCreatePulldownMenu(parent, @label, @args) 1704 cascade = RXtCreateManagedWidget(@label, 1705 RxmCascadeButtonWidgetClass, 1706 parent, 1707 [RXmNsubMenuId, @menu] + @args) 1708 RXtAddCallback(cascade, RXmNcascadingCallback, 1709 lambda do |w, c, i| 1710 update_label(@children) 1711 end) 1712 end 1713 1714 def entry(arg, *rest, &body) 1715 child = false 1716 if arg.class == Class 1717 menu = arg.new(*rest) 1718 if menu.respond_to?(:post_dialog) 1719 child = RXtCreateManagedWidget(rest[0].to_s, 1720 RxmPushButtonWidgetClass, 1721 @menu, @args) 1722 RXtAddCallback(child, RXmNactivateCallback, 1723 lambda do |w, c, i| 1724 menu.post_dialog 1725 end) 1726 @children.push(lambda do | | 1727 change_label(child, menu.inspect) 1728 end) 1729 else 1730 Snd.raise(:snd_x_error, arg.class, 1731 "class does not respond to `post_dialog'") 1732 end 1733 else 1734 if block_given? 1735 child = RXtCreateManagedWidget(arg.to_s, 1736 RxmPushButtonWidgetClass, 1737 @menu, @args) 1738 RXtAddCallback(child, RXmNactivateCallback, 1739 lambda do |w, c, i| 1740 body.call 1741 end) 1742 change_label(child, arg) 1743 else 1744 Snd.raise(:wrong_number_of_args, "no block given") 1745 end 1746 end 1747 child 1748 end 1749 1750 def separator(single = :single) 1751 line = (single == :double ? RXmDOUBLE_LINE : RXmSINGLE_LINE) 1752 RXtCreateManagedWidget("s", RxmSeparatorWidgetClass, @menu, 1753 [RXmNseparatorType, line]) 1754 end 1755 end 1756end 1757 1758# non-Snd menu functions, may be used outside Snd scripts 1759class Main_menu < Menu 1760 def initialize(name, parent, args, &body) 1761 super(name, parent, args) 1762 @menu = RXmCreatePulldownMenu(parent, "pulldown-menu", @args) 1763 wid = RXtCreateManagedWidget(@label, RxmCascadeButtonWidgetClass, parent, 1764 [RXmNsubMenuId, @menu] + @args) 1765 RXtVaSetValues(parent, [RXmNmenuHelpWidget, wid]) if name =~ /help/ 1766 if block_given? 1767 instance_eval(&body) 1768 end 1769 end 1770end 1771 1772class Main_popup_menu < Menu 1773 def initialize(name, parent, args, &body) 1774 super(name, parent, args) 1775 @parent = parent 1776 @menu = RXmCreatePopupMenu(@parent, "popup-menu", 1777 [RXmNpopupEnabled, RXmPOPUP_AUTOMATIC] + @args) 1778 RXtAddEventHandler(@parent, RButtonPressMask, false, 1779 lambda do |w, c, i, f| 1780 if Rbutton(i) == 3 1781 RXmMenuPosition(@menu, i) 1782 RXtManageChild(@menu) 1783 end 1784 end) 1785 unless @label.empty? 1786 label(@label) 1787 separator 1788 end 1789 if block_given? 1790 instance_eval(&body) 1791 end 1792 end 1793end 1794 1795include Snd_XM 1796 1797# snd-xm.rb ends here 1798