-
Notifications
You must be signed in to change notification settings - Fork 0
/
vp.cpp
executable file
·2633 lines (2334 loc) · 99.4 KB
/
vp.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// viewpoints - interactive linked scatterplots and more.
// copyright 2005 Creon Levit and Paul Gazis, all rights reserved.
//***************************************************************************
// File name: vp.cpp
//
// Class definitions: none
//
// Classes referenced:
// Various BLITZ templates
// Any classes in global_definitions_vp.h
// Plot_Window -- Plot window
// Control_Panel_Window -- Control panel window
// Data_File_Manager -- Manage data files
//
// Required packages
// FLTK 1.1.6 -- Fast Light Toolkit graphics packageF
// FLEWS 0.3 -- Extensions to FLTK
// OGLEXP 1.2.2 -- Access to OpenGL extension under Windows
// GSL 1.6 -- Gnu Scientific Library package for Windows
// Blitz++ 0.9 -- Various math routines
//
// Compiler directives:
// May require -D__WIN32__ to compile on windows
// See Makefile for linux and OSX compile & link info.
//
// Purpose: viewpoints - interactive linked scatterplots and more.
//
// General design philosophy:
// 1) This code represents a battle between Creon Levit's passion for speed
// and efficiency and Paul Gazis's obsession with organization and
// clarity, unified by a shared desire to produce a powerful and easy to
// use tool for exploratory data analysis. Creon's code reflects a
// strong 'C' heritage. Paul's code is written in C++ using the 'if
// only it were JAVA' programming style.
// 2) Functions in the main routine are sufficiently interdependant that it
// would be difficult to distribute them among separate objects. For
// this reason they are left in main(). It would be quite possible to
// combine them into a single vp class, but they have been left in
// main() for reasons of simplicity.
// 3) This code takes advantage of the FLTK graphics toolkit, the BOOST
// array package, BOOST serialization, and several other C++ packages.
// For the most part, this is straightforward, but use of BOOST
// serialization is complicated by its lack of documentation and its
// assumptions regarding the availability and use of constructors.
// 4) In addition to BOOST serialization, parts of this code also store
// settings via the FLTK FL_Preferences class.
//
// Functions:
// usage() -- Print help information
// make_help_about_window( *o) -- Draw the 'About' window
// create_main_control_panel( main_x, main_y, main_w, main_h, cWindowLabel)
// -- Create the main control panel window.
// cb_main_control_panel( *o, *u); -- Callback for main control panel
// create_brushes( w_x, w_y, w_w, w_h) -- Create brushes tabs
// brushes_tab_cb() -- Brushes window callback
// create_broadcast_group() -- Create special panel under tabs
// manage_plot_window_array( *o, *u) -- Manage plot window array
// cb_manage_plot_window_array( *o) -- Idle callback
// make_help_view_window( *o) -- Make Help View window
// textsize_help_view_widget( *o, *u) -- Change help text size
// close_help_window( *o, *u -- Help View window callback
// make_main_menu_bar() -- Create main menu bar
// change_screen_mode() -- Change screen mode
// make_file_name_window( *o) -- Make File|Current File Name window
// make_statistics_window( *o) -- Make Tools|Statistics window
// make_options_window( *o) -- Make Tools|Options window
// cb_options_window( *o, *u) -- Process Tools|Options window
// step_help_view_widget( *o, *u) -- Step through the Help|Help window.
// make_global_widgets() -- Controls for main control panel
// change_all_axes( *o) -- Change all axes
// npoints_changed( *o) -- Update number of points changed
// resize_selection_index_arrays( nplots_old, nplots) -- Resize arrays
// write_data( *o, *u) -- Write data widget
// reset_all_plots() -- Reset all plots
// read_data( *o, *u) -- Read data widget
// load_state( *o) -- Load saved state
// save_state( *o) -- Save current state
// redraw_if_changing( *dummy) -- Redraw changing plots
// reset_selection_arrays() -- Reset selection arrays
//
// Author: Creon Levit 2005-2006
// Modified: P. R. Gazis 24-MAR-2009
//***************************************************************************
// Include the necessary include libraries
#include "include_libraries_vp.h"
#include <FL/Fl_Tooltip.H>
#include <FL/Fl_Scroll.H>
// define and initialize globals
#define DEFINE_GLOBALS
#include "global_definitions_vp.h"
// Include associated headers and source code
#include "data_file_manager.h"
#include "plot_window.h"
#include "control_panel_window.h"
#include "brush.h"
#include "unescape.h"
// Define and initialize number of screens
static int number_of_screens = 0;
// Approximate values of window manager borders & desktop borders (stay out of
// these). The "*_frame" constants keep windows from crowding the coresponding
// screen edge. The "*_safe" constants keep windows from overlapping each
// other. These are used when the main control panel window is defined. And
// when the plot windows are tiled to fit the screen. Too bad they are only
// "hints" according most window managers (and we all know how well managers
// take hints).
#ifdef __APPLE__
static int top_frame=35, bottom_frame=0, left_frame=0, right_frame=5;
static int top_safe = 1, bottom_safe=5, left_safe=5, right_safe=1;
#else // __APPLE__
static int top_frame=25, bottom_frame=5, left_frame=4, right_frame=5;
static int top_safe = 1, bottom_safe=10, left_safe=10, right_safe=1;
#endif // __APPLE__
// These are needed to pass to manage_plot_window_array
static int global_argc;
static char **global_argv;
// Define and set default border style for the plot windows
static int borderless=0; // By default, use window manager borders
// Needed to track position in help window
static int help_topline;
// Define variables to hold main control panel window, tabs widget, and
// virtual control panel positions. Consolidated here for reasons of clarity.
// Increase this when the main panel needs to get wider:
static const int main_w = 365;
// Increase this when the main panel needs to get taller, including situations
// when cp_widget_h increases:
// static const int main_h = 850;
static const int main_h = 890;
// These are calculated and used to hold sizes for use with small laptop
// screens
static const float laptop_scale = 0.8;
static int laptop_main_w;
static int laptop_main_h;
// Increase this when the plot controls need more height to fit in their subpanel
// static const int cp_widget_h = 505;
static const int cp_widget_h = 430;
static const int brushes_h = 250;
// The rest of these should not have to change
static const int tabs_widget_h = cp_widget_h+20;
// static const int global_widgets_y = tabs_widget_h + brushes_h + 20;
static const int global_widgets_y = tabs_widget_h + brushes_h + 30;
static const int tabs_widget_x = 3, tabs_widget_y = 30;
static const int cp_widget_x = 3, cp_widget_y = tabs_widget_y+20;
// static const int brushes_x = 3, brushes_y = tabs_widget_y+tabs_widget_h;
static const int brushes_x = 3, brushes_y = tabs_widget_y+tabs_widget_h+10;
static const int global_widgets_x = 10;
// Instantiate a preferences object to manage persistent preferences
static Fl_Preferences
prefs_( Fl_Preferences::USER, "viewpoints.arc.nasa.gov", "viewpoints");
// Define class to hold data file manager
Data_File_Manager dfm;
// Define pointers to hold main control panel, main menu bar, and any pop-up
// windows. NOTE: help_view_widget must be defined here so it will be
// available to callback functions
Fl_Window *main_control_panel;
Fl_Scroll *main_scroll;
Fl_Group *main_scroll_group;
Fl_Menu_Bar *main_menu_bar;
Fl_Window *statistics_window;
Fl_Window *options_window;
Fl_Window *about_window;
Fl_Window *help_view_window;
Fl_Help_View *help_view_widget;
// Define pointers to widgets for the Tools|Options window
Fl_Check_Button* expertButton;
Fl_Check_Button* trivialColumnsButton;
Fl_Check_Button* preserveOldDataButton;
Fl_Check_Button* laptopModeButton;
Fl_Input* maxpoints_input;
Fl_Input* maxvars_input;
Fl_Input* bad_value_proxy_input;
Fl_Check_Button* use_VBOs_Button;
// Function definitions for the main method
void usage();
void make_help_about_window( Fl_Widget *o);
void create_main_control_panel(
int main_x, int main_y, int main_w, int main_h, const char* cWindowLabel);
void cb_main_control_panel( Fl_Widget *o, void* user_data);
void create_brushes( int w_x, int w_y, int w_w, int w_h);
void brushes_tab_cb();
void create_broadcast_group();
void manage_plot_window_array( Fl_Widget *o, void* user_data);
void cb_manage_plot_window_array( void* o);
void make_main_menu_bar();
void change_screen_mode();
void make_file_name_window( Fl_Widget *o);
void make_statistics_window( Fl_Widget *o);
void make_options_window( Fl_Widget *o);
void cb_options_window( Fl_Widget *o, void* user_data);
void make_help_view_window( Fl_Widget *o);
void textsize_help_view_widget( Fl_Widget *o, void* user_data);
void close_help_window( Fl_Widget *o, void* user_data);
void step_help_view_widget( Fl_Widget *o, void* user_data);
void make_global_widgets();
void change_all_axes( Fl_Widget *o);
void npoints_changed( Fl_Widget *o);
void write_data( Fl_Widget *o, void* user_data);
void reset_all_plots();
void read_data( Fl_Widget* o, void* user_data);
int load_initial_state( string configFileSpec);
int load_state( Fl_Widget* o);
int save_state( Fl_Widget* o);
void redraw_if_changing( void *dummy);
void reset_selection_arrays();
//***************************************************************************
// usage() -- Print help information to the console and exit. NOTE: This is
// formatted for 80 columns without wrapoaround because this is still a
// common console setting.
void usage()
{
cerr << endl;
cerr << "Usage: vp {optional arguments} {optional filename}" << endl;
cerr << endl;
cerr << "Optional arguments:" << endl;
cerr << " -b, --borderless "
<< "Don't show decorations on plot windows. NOTE:" << endl
<< " "
<< "this option may freeze plot windows in place." << endl;
cerr << " -B, --no_vbo "
<< "Don't use openGL vertex buffer objects. Try" << endl
<< " "
<< "this option if problems arise loading VBOs" << endl;
cerr << " -c, --cols=NCOLS "
<< "Startup with this many columns of plot windows," << endl
<< " "
<< "default=2" << endl;
cerr << " -C, --config_file=FILENAME "
<< "Read saved configuration from FILENAME." << endl;
cerr << " -d, --delimiter=CHAR "
<< "Interpret CHAR as a field separator, default is" << endl
<< " "
<< "whitespace." << endl;
cerr << " -f, --format={ascii,binary,fits} " << endl
<< " "
<< "Input file format, default=ascii. NOTE: for ASCII" << endl
<< " "
<< "files, the data block is assumed to begin with an" << endl
<< " "
<< "uncommented line that contains column labels" << endl;
cerr << " -i, --input_file=FILENAME "
<< "Read input data from FILENAME." << endl;
cerr << " -I, --stdin "
<< "Read input data from stdin." << endl;
cerr << " -l, --laptop_mode "
<< "Shrink control panel to fit in a laptop screen" << endl;
cerr << " -L, --commented_labels "
<< "Expect the line that contains column labels to" << endl
<< " "
<< "begin with a comment character." << endl;
cerr << " -m, --monitors=NSCREENS "
<< "Try and force output to display across NSCREENS" << endl
<< " "
<< "screens if available." << endl;
cerr << " -M, --missing_values=NUMBER "
<< "Set the value of unreadable, nonnumeric, empty," << endl
<< " "
<< "or missing data to NUMBER, default=0.0." << endl;
cerr << " -n, --npoints=NPOINTS "
<< "Read at most NPOINTS from input file, default is" << endl
<< " "
<< "min(until_EOF, 2000000)." << endl;
cerr << " -o, --ordering={rowmajor,columnmajor} " << endl
<< " "
<< "Ordering for binary data, default=columnmajor." << endl;
cerr << " -P, --preserve_data=(T,F) "
<< "Preserve old data if a file read fails, default=TRUE." << endl;
cerr << " -r, --rows=NROWS "
<< "Startup with this many rows of plot windows," << endl
<< " "
<< "default=2." << endl;
cerr << " -s, --skip_lines=NLINES "
<< "Skip NLINES at start of input file, default=0." << endl;
cerr << " -t, --trivial_columns=(T,F) "
<< "Remove columns with a single value, default=TRUE." << endl;
cerr << " -v, --nvars=NVARS "
<< "Input has NVARS values per point (only for row" << endl
<< " "
<< "major binary data)." << endl;
cerr << " -h, --help "
<< "Display this message and exit." << endl;
cerr << " -x, --expert "
<< "enable expert mode (bypass confirmations, etc.)" << endl;
cerr << " -O, --verbose "
<< "enable verbOse output with more diagnostics." << endl;
cerr << " -V, --version "
<< "Output version information and exit." << endl;
exit( -1);
}
//***************************************************************************
// make_help_about_window( *o) -- Create the 'Help|About' window.
void make_help_about_window( Fl_Widget *o)
{
if( about_window != NULL) about_window->hide();
// Create Help|About window
Fl::scheme( "plastic"); // optional
about_window = new Fl_Window( 360, 220, "About vp");
about_window->begin();
about_window->selection_color( FL_BLUE);
about_window->labelsize( 10);
// Write text to box label and align it inside box
Fl_Box* output_box = new Fl_Box( 5, 5, 350, 180);
output_box->box( FL_SHADOW_BOX);
output_box->color( 7);
output_box->selection_color( 52);
output_box->labelfont( FL_HELVETICA);
output_box->labelsize( 15);
output_box->align( FL_ALIGN_TOP|FL_ALIGN_CENTER|FL_ALIGN_INSIDE);
output_box->copy_label( about_string.c_str());
// Invoke a multi-purpose callback function to close window
Fl_Button* close = new Fl_Button( 240, 190, 60, 25, "&Close");
close->callback( (Fl_Callback*) close_help_window, about_window);
// Done creating the 'Help|About' window
// about_window->resizable( about_window);
about_window->resizable( NULL);
about_window->end();
about_window->show();
}
//***************************************************************************
// create_main_control_panel( main_x, main_y, main_w, main_h, cWindowLabel)
// -- Create the main control panel window.
void create_main_control_panel(
int main_x, int main_y, int main_w, int main_h, const char* cWindowLabel)
{
// Create main control panel window
Fl_Group::current(0);
Fl::scheme( "plastic"); // optional
Fl_Tooltip::delay(1.0);
Fl_Tooltip::hoverdelay(1.0);
Fl_Tooltip::size(12);
int clipped_h = min( main_h, Fl::h() - (top_frame + bottom_frame));
main_control_panel = new Fl_Window( main_x, main_y, main_w, clipped_h, cWindowLabel);
main_control_panel->resizable( main_control_panel);
// Add callback function to intercept 'Close' operations
main_control_panel->callback( (Fl_Callback*) cb_main_control_panel, main_control_panel);
// Make main menu bar and add the global widgets to control panel
make_main_menu_bar();
main_control_panel->add(main_menu_bar);
// All controls (except the main menu bar) in the main panel are inside an Fl_Scroll,
// because there are too many controls to all fit vertically on some small screens.
// Eventally, we will make the sub-panels independently expandable and reorganize
// the gui to alleviate this problem.
main_scroll = new Fl_Scroll( 0, main_menu_bar->h(), main_w, main_h - main_menu_bar->h());
main_scroll->box(FL_NO_BOX);
// Define a group to hold contents of the scolling area and make them
// resizable
main_scroll_group =
new Fl_Group( main_scroll->x(), main_scroll->y(), main_scroll->w(), main_scroll->h());
main_control_panel->add(main_scroll_group);
main_scroll_group->box(FL_NO_BOX);
// MCL XXX
// if I move this call to create_brushes() to the end of this routine, to just before
// the call to main_control_panel->end(), I get a core dump. That's' too bad......
create_brushes( brushes_x, brushes_y, main_w-6, brushes_h);
// the widgets at the bottom of the main panel. Seems they need to created here. :-?
make_global_widgets();
// Inside the main control panel, there is a tab widget, cpt,
// that contains the sub-panels (groups), one per plot.
cpt = new Fl_Tabs( tabs_widget_x, tabs_widget_y, main_w-6, tabs_widget_h);
cpt->selection_color( FL_BLUE);
cpt->labelsize( 10);
// main_control_panel->add(cpt);
main_scroll_group->add(cpt);
// Done creating main control panel (except for the tabbed
// sub-panels created by manage_plot_window_array)
main_scroll_group->end();
main_scroll->end();
main_control_panel->end();
Fl_Group::current(0);
}
//***************************************************************************
// cb_main_control_panel( *o, *user_data) -- Callback (and potentially
// close) the main control panel window. It is assumed that a pointer to
// the window will be passed as USER_DATA. WARNING: No error checking is
// done on USER_DATA!
void cb_main_control_panel( Fl_Widget *o, void* user_data)
{
if( expert_mode || make_confirmation_window( "Quit? Are you sure?") > 0) {
((Fl_Window*) user_data)->hide();
exit( 0);
}
}
//***************************************************************************
// create_brushes( w_x, w_y, w_w, w_h) -- Create bushes. Move this to class
// Brush?
void create_brushes( int w_x, int w_y, int w_w, int w_h)
{
// Fl_Group::current(0); // not a subwindow
// brushes_window = new Fl_Window( w_x, w_y, w_w, w_h, "brushes");
// brushes_window->resizable(brushes_window);
// Inside the main control panel, there is a Tabs widget
// that contains all the indidual brush's sub-panels.
brushes_tab = new Fl_Tabs( w_x, w_y, w_w, w_h);
brushes_tab->selection_color( FL_BLUE);
brushes_tab->labelsize( 10);
brushes_tab->callback( (Fl_Callback*) brushes_tab_cb);
// brushes_tab->clear_visible_focus(); // disbaled for future soloing...
// Be sure to make this a child of the main control panel window
// main_control_panel->add(brushes_tab);
main_scroll_group->add(brushes_tab);
Fl_Group::current(brushes_tab);
for (int i=0; i<NBRUSHES; i++) {
// create a brush (Fl_Group) corresponding to the tab
brushes[i] = new Brush(w_x, w_y+20, w_w-6, w_h-(20+6));
}
brushes_tab->end();
// we have to start with some brush active, and since
// brushes[0] is for "unselected" points, we start with brushes[1]
brushes_tab->value(brushes[1]);
brushes_tab_cb();
// brushes_window->end();
// brushes_window->show();
}
//***************************************************************************
// brushes_tab_cb() -- Callback to keep tab's colored labels drawn in the
// right color when they're selected. Move this to class Brush?
void brushes_tab_cb() {
brushes_tab->labelcolor(brushes_tab->value()->labelcolor());
brushes_tab->redraw_label();
}
//***************************************************************************
// create_broadcast_group () -- Create a special panel (really a group under
// a tab) with label "+" this group's widgets effect all the others (unless
// a plot's tab is "locked" - TBI). MCL XXX should this be a method of
// Control_Panel_Window or should it be a singleton?
void create_broadcast_group ()
{
Fl_Group::current(cpt);
Control_Panel_Window *cp = cps[nplots];
cp = new Control_Panel_Window( cp_widget_x, cp_widget_y, main_w - 6, cp_widget_h);
cp->label("all");
cp->labelsize( 10);
cp->resizable( cp);
cp->make_widgets( cp);
cp->end();
// this group's index is highest (and it has no associated plot window)
cp->index = nplots;
// this group's callbacks all broadcast any "event" to the other
// (unlocked) tabs groups. with a few exceptions... (for now)
for (int i=0; i<cp->children(); i++) {
Fl_Widget *wp = cp->child(i);
wp->callback( (Fl_Callback *)(Control_Panel_Window::broadcast_change), cp);
}
// MCL XXX these widgets cause crashes or misbehaviors in the broadcast
// panel, so disable them for now.
cp->sum_vs_difference->deactivate();
cp->no_transform->deactivate();
cp->cond_prop->deactivate();
cp->fluctuation->deactivate();
// Initially, this group has no axes (XXX or anything else, for that matter)
cp->varindex1->value(nvars); // initially == "-nothing-"
cp->varindex2->value(nvars); // initially == "-nothing-"
cp->varindex3->value(nvars); // initially == "-nothing-"
}
//***************************************************************************
// manage_plot_window_array( *o, *u) -- General-purpose method to create,
// manage, and reload the plot window array. It saves any existing axis
// information, deletes old tabs, creates new tabs, restores existing axis
// information, and loads new data into new plot windows.
// WARNING: This method is the 800-lb gorilla, on which much of the GUI
// depends! Like all gorillas, it is delicate, sensitive, and must be
// treated with a profound mixture of caution and respect. In particular,
// slight changes in the FLTK calls can lead to elusive segmentation faults!
// Test any changes carefully!
// WARNING: There is little protection against missing data, and gorillas
// can misbehave if they can't find their bananas!
// NOTE: Little attempt has been made to optimize this method for speed.
//
// There are four possible behaviors, which all must be recognized,
// identified, and treated differently:
// 1) INITIALIZE -- NULL argument. Set nplots_old = 0.
// 2) NEW_DATA -- Called from a menu or as part of a 'load saved state'
// operation. In this mode, new data is read into the VBOs, so all
// plot windows must be hidden and redrawn!
// 3) REFRESH_WINDOWS -- Called from a menu. In this mode, some windows
// may be preserved without the need for redrawing.
// 4) RELOAD -- Called from a button or menu. NOTE: These no longer happen,
// and this operation is no longer supported!
// Redrawing of plot windows is controlled by the value of NPLOTS_OLD. This
// is initialized to NPLOTS. For an INITIALIZE or NEW_DATA operation, it is
// then reset to zero, so that all existing plots will be hidden so they can
// be redrawn with new data as noted above.
// In some cases, it is desirable to restore window parameters such as
// axis labels or normalization schemes. Whether this happens is controlled
// by the 'do_restore_settings' flag.
void manage_plot_window_array( Fl_Widget *o, void* user_data)
{
// Define an enumeration to hold a list of operation types
enum operationType { INITIALIZE = 0, NEW_DATA, REFRESH_WINDOWS, RELOAD};
// Define and initialize the operationType switch, old number of plots,
// widget title, and pointers to the pMenu_ and pButton objects.
operationType thisOperation = INITIALIZE;
char widgetTitle[ 80];
char userData[ 80];
strcpy( widgetTitle, "");
strcpy( userData, "");
Fl_Menu_* pMenu_;
Fl_Button* pButton;
// Define and set flags and state variables to control the number of plots
// to be preserved and whether or not to restore their control panel
// settings and positions
int nplots_old = nplots;
int do_restore_settings = 0;
int do_restore_positions = 0;
// Determine how the method was invoked, and set flags and parameters
// accordingly. If method was called with a NULL arguments, assume this
// is an initialization operation and set the old number of plots to zero,
// otherwise identify the argument type via a dynamic cast, extract the
// widget title, and set the old and new numbers of plots accordingly.
// CASE 1: If the widget was NULL, this is an initialzation operation
if( o == NULL) {
thisOperation = INITIALIZE;
nplots_old = 0;
}
// CASE 2: If this was an Fl_Menu_ widget, default to a resize operation,
// then figure out what operation was requested and revise the switches
// and array descriptions accordingly
else if( (pMenu_ = dynamic_cast <Fl_Menu_*> (o))) {
thisOperation = REFRESH_WINDOWS;
nplots_old = nplots;
// Get widget title and user data, and make absolutely sure that these
// aren't NULL
strcpy( widgetTitle, "");
strcpy( userData, "");
if( ((Fl_Menu_*) o)->text() != NULL)
strcpy( widgetTitle, ((Fl_Menu_*) o)->text());
if( user_data != NULL) strcpy( userData, (char*) user_data);
// Examine widget title and user data to determine behavior on a case by
// case basis for reasons of clarity
if( strncmp( userData, "NEW_DATA", 8) == 0) {
thisOperation = NEW_DATA;
}
else if( strncmp( userData, "REFRESH_WINDOWS", 15) == 0) {
thisOperation = REFRESH_WINDOWS;
do_restore_settings = 1;
do_restore_positions = 1;
}
else if( strncmp( widgetTitle, "Open", 4) == 0 ||
strncmp( userData, "Open", 4) == 0 ||
strncmp( widgetTitle, "Load", 4) == 0) {
thisOperation = NEW_DATA;
do_restore_settings = 1;
do_restore_positions = 1;
}
else if( strncmp( widgetTitle, "Append", 6) == 0 ||
strncmp( widgetTitle, "Merge", 5) == 0 ||
strncmp( widgetTitle, "Clear all", 9) == 0) {
thisOperation = NEW_DATA;
do_restore_settings = 1;
do_restore_positions = 1;
}
else if( strncmp( widgetTitle, "Add Row ", 8) == 0) {
thisOperation = REFRESH_WINDOWS;
nrows++;
}
else if( strncmp( widgetTitle, "Add Colu", 8) == 0) {
thisOperation = REFRESH_WINDOWS;
ncols++;
}
else if( strncmp( widgetTitle, "Remove R", 8) == 0 && nrows>1) {
thisOperation = REFRESH_WINDOWS;
nrows--;
}
else if( strncmp( widgetTitle, "Remove C", 8) == 0 && ncols>1) {
thisOperation = REFRESH_WINDOWS;
ncols--;
}
else if( strncmp( widgetTitle, "Reload F", 8) == 0) {
thisOperation = REFRESH_WINDOWS;
do_restore_settings = 1;
do_restore_positions = 1;
// Reset brushes because load resets brush sizes
for( int j=0; j<NBRUSHES; j++) brushes[j]->reset();
}
else if( strncmp( widgetTitle, "Restore", 7) == 0) {
thisOperation = REFRESH_WINDOWS;
do_restore_settings = 1;
do_restore_positions = 1;
}
// When reading new data, invoke Fl_Gl_Window.hide() (instead of the
// destructor!) to destroy all plot windows along with their context,
// including VBOs. Then set nplots_old to zero because we'll need to
// redraw all of these plots. NOTE: If this is not done, and one
// attempts to preserve some panels without redrawing them, VBO usage
// could fail.
if( thisOperation == NEW_DATA) {
for( int i=0; i<nplots; i++) pws[i]->hide();
nplots_old = 0;
}
}
// CASE 3: If this was a button widget, assume it was a reload operation,
// since no other buttons can invoke this method. NOTE: This operation
// doesn't work and is no longer supported!
else if( (pButton = dynamic_cast <Fl_Button*> (o))) {
thisOperation = RELOAD;
nplots_old = nplots;
strcpy( widgetTitle, ((Fl_Menu_*) o)->label());
cerr << "manage_plot_window_array: WARNING, "
<< "RELOAD operation not supported!" << endl;
}
// DEFAULT: Default to a REFRESH_WINDOWS operation, in which all plots
// are preserved with new settings, and warn the user.
else {
thisOperation = REFRESH_WINDOWS;
nplots_old = nplots;
strcpy( widgetTitle, "default");
cerr << "manage_plot_window_array: WARNING, "
<< "defaulted to a REFRESH_WINDOW operation." << endl;
}
// DIAGNOSTIC
// std::vector<std::string> diag_stuff;
// diag_stuff.push_back( "INITIALIZE");
// diag_stuff.push_back( "REFRESH_WINDOWS");
// diag_stuff.push_back( "NEW_DATA");
// diag_stuff.push_back( "RELOAD");
// cout << "DIAGNOSTIC, manage_plot_window_array: widgetTitle(" << widgetTitle
// << ") userData(" << userData << ")" << endl;
// cout << "DIAGNOSTIC, manage_plot_window_array: thisOperation "
// << thisOperation << " (" << diag_stuff[ thisOperation].c_str()
// << ")" << endl;
// Determine how many plot windows are available to be saved. If this is an
// INITIALIZE operation, set this number to zero.
int nplots_save = nplots;
if( thisOperation == INITIALIZE) nplots_save = 0;
// Save an array of Plot_Window objects with the positions of the existing
// plot windows. NOTE: This must be an array rather than a pointer array
// to invoke the default constructor.
Plot_Window pws_save[ MAXPLOTS+1];
cout << "manage_plot_window_array: saving information for " << nplots_save
<< " windows" << endl;
for( int i=0; i<nplots_save; i++) {
pws[i]->make_state();
pws_save[i].copy_state( pws[i]);
}
// Save an array of Control_Panel_Window objects with axis, normalization,
// and transform style information of the existing control panels
Control_Panel_Window cps_save[ MAXPLOTS+1];
for( int i=0; i<nplots_save; i++) {
cps[i]->make_state();
cps_save[i].copy_state( cps[i]);
}
// Save position and size of control panel windows so these can be used to
// calculate sizes for laptop mode
int x_save = cp_widget_x;
int y_save = cp_widget_y;
int w_save = main_w - 6;
int h_save = main_h;
if( thisOperation != INITIALIZE) {
x_save = cps[0]->x();
y_save = cps[0]->y();
w_save = cps[0]->w();
h_save = cps[0]->h();
}
// Save an array of Brush objects with information of the existing brushes
Brush brushes_save[ NBRUSHES];
for( int i=0; i<NBRUSHES; i++) {
brushes[i]->make_state();
brushes_save[i].copy_state( brushes[i]);
}
// Recalculate the number of plots
nplots = nrows * ncols;
// Clear children of the control panel tab widget to delete old tabs
// cpt->clear(); // for some reason this crashes now, so we hack as follows:
for( int i=0; i<nplots_save+1; i++) {
cpt->remove(0);
}
// Create and add the virtual sub-panels, each group under a tab, one
// group per plot.
for( int i=0; i<nplots; i++) {
int row = i/ncols;
int col = i%ncols;
// Account for the 'borderless' option
if( borderless)
top_frame = bottom_frame = left_frame = right_frame = 1;
// Determine default plot window size for regular and laptop mode
// int pw_w =
// ( ( number_of_screens*Fl::w() -
// (main_w+left_frame+right_frame+right_safe+left_safe+20)) / ncols) -
// (left_frame + right_frame);
int scaled_main_w = w_save+6;
if( laptop_mode) scaled_main_w = (int) (laptop_scale*main_w);
int pw_w =
( ( number_of_screens*Fl::w() -
(scaled_main_w+left_frame+right_frame+right_safe+left_safe+20)) / ncols) -
(left_frame + right_frame);
int pw_h =
( (Fl::h() - (top_safe+bottom_safe))/ nrows) -
(top_frame + bottom_frame);
// Calculate default plot window positions
int pw_x =
left_safe + left_frame +
col * (pw_w + left_frame + right_frame);
int pw_y =
top_safe + top_frame +
row * (pw_h + top_frame + bottom_frame);
// Create a label for this tab
ostringstream oss;
oss << "" << i+1;
string labstr = oss.str();
// Set the pointer to the current group to the tab widget defined by
// create_control_panel and add a new virtual control panel under this
// tab widget
Fl_Group::current( cpt);
cps[i] = new Control_Panel_Window( cp_widget_x, cp_widget_y, main_w - 6, cp_widget_h);
cps[i]->index = i;
cps[i]->copy_label( labstr.c_str());
cps[i]->labelsize( 10);
cps[i]->resizable( cps[i]);
cps[i]->make_widgets( cps[i]);
// End the group here so that we can create new plot windows at the top
// level, then set the pointer to the current group to the top level.
cps[i]->end();
Fl_Group::current( 0);
// If this was an INITIALIZE, REFRESH_WINDOWS, or NEW_DATA operation,
// then create or restore the relevant windows. NOTE: If this code was
// executed during a reload operation (which is no longer supported!),
// it would cause a segmentation fault due to problems with the way the
// shown() and hide() calls work.
if( thisOperation == INITIALIZE ||
thisOperation == REFRESH_WINDOWS ||
thisOperation == NEW_DATA) {
if( i >= nplots_old) {
DEBUG(cout << "Creating new plot window " << i << endl);
pws[i] = new Plot_Window( pw_w, pw_h, i);
cps[i]->pw = pws[i];
pws[i]->cp = cps[i];
}
else {
pws[i]->index = i;
cps[i]->pw = pws[i];
pws[i]->cp = cps[i];
pws[i]->size( pw_w, pw_h);
}
pws[i]->copy_label( labstr.c_str());
pws[i]->position(pw_x, pw_y);
pws[i]->row = row;
pws[i]->column = col;
pws[i]->end();
}
// Always link the plot window and its associated virtual control panel
assert( (pws[i]->index == i) && (cps[i]->index == i));
cps[i]->pw = pws[i];
pws[i]->cp = cps[i];
// Always invoke Plot_Window::upper_triangle_incr to determine which
// variables to plot in new panels.
int ivar, jvar;
if( i==0) {
ivar = 0;
jvar = 1;
// If this is an initialize operation, then the plot window array is being
// created, the tabs should come up free of context. It also might be
// desirable that the first plot's tab be shown with its axes locked.
if( thisOperation == INITIALIZE) {
cps[i]->hide();
}
}
else Plot_Window::upper_triangle_incr( ivar, jvar, nvars);
#ifdef SERIALIZATION
// If there has been an explicit request to restore the saved control
// panel settings or the number of plots has changed, restore those
// settings, then generate any new settings that may be required.
if( do_restore_settings != 0 ||
nplots != (nplots_old && i<nplots_old)) {
// Make sure axis indices are in range
cps_save[i].restrict_axis_indices( nvars-1, nvars-1, nvars);
cps[i]->copy_state( &cps_save[i]);
cps[i]->load_state();
}
else {
cps[i]->varindex1->value(ivar);
cps[i]->varindex2->value(jvar);
cps[i]->varindex3->value(nvars);
}
#else // SERIALIZATION
cps[i]->varindex1->value(ivar);
cps[i]->varindex2->value(jvar);
cps[i]->varindex3->value(nvars);
#endif //SERIALIZATION
// KLUDGE: If there has been an explicit request to restore the saved
// control panel settings or the number of plots has changed, restore the
// brush settings, but only do this once, during the first iteration.
// XXX PRG: This seems unreliable. Is there a better place to put this?
if( i==0 && (do_restore_settings != 0 || nplots != nplots_old)) {
for( int j=0; j<NBRUSHES; j++) {
brushes[i]->copy_state( &brushes_save[i]);
brushes[i]->load_state();
}
}
// If this is an INITIALIZE, REFRESH_WINDOWS, or NEW_DATA operation,
// test for missing data, extract data, reset panels, and make them
// resizable. Otherwise it must be a RELOAD operation (which is no
// longer supported!), and we must invoke the relevant Plot_Window
// member functions to initialize and draw panels.
if( thisOperation == INITIALIZE ||
thisOperation == REFRESH_WINDOWS ||
thisOperation == NEW_DATA) {
if( npoints > 1) {
pws[i]->extract_data_points();
pws[i]->reset_view();
}
pws[i]->size_range( 10, 10);
pws[i]->resizable( pws[i]);
}
else { // RELOAD no longer supported!
pws[i]->initialize();
pws[i]->extract_data_points();
}
// OLD KLUDGE: If this is a "append", "merge", "reload file" or "restore
// panels" operation, restore old plot window positions. This should
// be controlled by a flag rather than examining WIDGETTITLE.
// if( strncmp( userData, "REFRESH_WINDOWS", 15) == 0 ||
// strncmp( widgetTitle, "Append", 6) == 0 ||
// strncmp( widgetTitle, "Merge", 5) == 0 ||
// strncmp( widgetTitle, "Reload", 6) == 0 ||
// strncmp( widgetTitle, "Restore", 7) == 0) {
// If the do_restore_positions flag was set by an 'append', 'merge',
// 'reload file', or 'restore panels' operation, restire old window
// positions.
if( do_restore_positions != 0) {
for( int i=0; i<nplots_save; i++) {
pws[ i]->copy_state( &pws_save[i]);
pws[ i]->load_state();
// DIAGNOSTIC
// cout << " window[ " << i << "/" << nplots_old
// << "]: ( " << pws[ i]->x()
// << ", " << pws[ i]->y()
// << ", " << pws[ i]->w()
// << ", " << pws[ i]->h() << ")" << endl;
}
}
// Account for the 'borderless' option
if( borderless) pws[i]->border(0);
// Make sure the window has been shown and check again to make absolutely
// sure it is resizable. NOTE: pws[i]->show() with no arguments is not
// sufficient when windows are created.
if( !pws[i]->shown()) {
DEBUG(cout << "showing plot window " << i << endl);
pws[i]->show( global_argc, global_argv);
}
pws[i]->resizable( pws[i]);
// Turn on the 'show' capability of Plot_Window::reset_view();
pws[i]->do_reset_view_with_show = 1;
}
// Set the color arrays to make sure points get drawn.
pws[0]->color_array_from_selection();
// Invoke Fl_Gl_Window::hide() (rather than the destructor, which may
// produce strange behavior) to rid of any superfluous plot windows
// along with their contexts.
if( nplots < nplots_old)
for( int i=nplots; i<nplots_old; i++) pws[i]->hide();
// Create a master control panel to encompass all the tabs
create_broadcast_group ();
// If this is laptop mode, shrink fonts and loop through children of the
// control palenl tab to rescale control panel windows, including the
// 'All' window, which may not be available through cp[nplots]
if( thisOperation != INITIALIZE && laptop_mode) {
shrink_widget_fonts( cpt, laptop_scale);
for( int i=0; i<cpt->children(); i++) {
(cpt->child(i))->resize( x_save, y_save, w_save, h_save);
}
}
}
//***************************************************************************
// cb_manage_plot_window_array( o) -- Idle callback to see if the plot
// windows need to be refreshed.
void cb_manage_plot_window_array( void* o)
{
// Examine and set flags
if( dfm.needs_restore_panels() <= 0) return;
dfm.needs_restore_panels( 0);
// DIAGNOSTIC
cout << "MAIN::cb_manage_plot_window_array: DIAGNOSTIC "
<< "manage_plot_window_array invoked via callback" << endl;
// NOTE: We need some way to revise the window scales. Is there also
// some way to avoid chosing random axes from the reduced set?
// for( int i=0; i<nplots; i++) pws[i]->extract_data_points();
// KLUDGE: make manage_plot_window_array think it's called from a menu.
// manage_plot_window_array( main_menu_bar, (void*) "REFRESH_WINDOWS");
manage_plot_window_array( main_menu_bar, (void*) "NEW_DATA");
}
//***************************************************************************
// make_main_menu_bar() -- Make main menu bar. NOTE: because the FLTK
// documentation recommends against manipulating the Fl_Menu_Item array
// directly, this is done via the add() method of Fl_Menu_.
void make_main_menu_bar()
{
// Instantiate the Fl_Menu_Bar object
main_menu_bar =
new Fl_Menu_Bar( 0, 0, main_w, 25);
// Add File menu items. NOTE: In some cases, the values of the title
// and user_data fields of the main_menu_bar object may be used to control
// the behavior of the manage_plot_window_array method.
main_menu_bar->add(
"File/Open data file ", 0,
(Fl_Callback *) read_data, (void*) "open data file");
main_menu_bar->add(
"File/Append more data ", 0,
(Fl_Callback *) read_data, (void*) "append more data");
main_menu_bar->add(
"File/Merge another file ", 0,
(Fl_Callback *) read_data, (void*) "merge another file", FL_MENU_DIVIDER);
main_menu_bar->add(
"File/Save all data ", 0,