00001 /*************************************************************************** 00002 *cr 00003 *cr (C) Copyright 1995-2019 The Board of Trustees of the 00004 *cr University of Illinois 00005 *cr All Rights Reserved 00006 *cr 00007 ***************************************************************************/ 00008 #include "Inform.h" 00009 #include "MainFltkMenu.h" 00010 #include "FL/Fl_Menu_Bar.H" 00011 #include "FL/Fl_Menu_Button.H" 00012 #include "FL/Fl_Menu_Item.H" 00013 #include "MolBrowser.h" 00014 #include "frame_selector.h" 00015 #include "FL/Fl_Radio_Button.H" 00016 #include "FL/Fl_Value_Slider.H" 00017 #include "FL/Fl_Int_Input.H" 00018 #include "TextEvent.h" 00019 00020 #if FL_MAJOR_VERSION <= 1 00021 #if FL_MINOR_VERSION < 1 00022 #include "FL/fl_file_chooser.H" 00023 #endif 00024 #endif 00025 00026 00027 #include "FL/forms.H" 00028 #include "VMDApp.h" 00029 #include "VMDMenu.h" 00030 #include "CommandQueue.h" 00031 #include "CmdMenu.h" 00032 #include "CmdAnimate.h" 00033 #include "Mouse.h" 00034 #include "TextEvent.h" 00035 #include "FPS.h" 00036 #include "Stage.h" 00037 #include "Axes.h" 00038 #include "Scene.h" 00039 #include "Animation.h" 00040 #include "DisplayDevice.h" 00041 #include "PickModeList.h" 00042 00043 #define EXT_MENU_NAME "Extensions" 00044 00045 00046 // Special main window callback to prevent ESC from closing the 00047 // main window, and tie window closure via mouse to quitting VMD. 00048 void MainFltkMenu::vmd_main_window_cb(Fl_Widget * w, void *) { 00049 MainFltkMenu *m = (MainFltkMenu *)w; 00050 00051 if (Fl::event_key() == FL_Escape) return; // ignore Escape key 00052 00053 if (fl_show_question("Really Quit?", 0)) 00054 m->app->VMDexit("",0,0); 00055 00056 // Normal code executed by all other windows is: 00057 // m->app->menu_show(m->get_name(), 0); 00058 } 00059 00060 // callback for all pulldown menu items that just raise a form 00061 static void menu_cb(Fl_Widget *w, void *v) { 00062 VMDApp *app = (VMDApp *)(w->user_data()); 00063 const char *name = (const char *)v; 00064 app->menu_show(name, 0); 00065 app->menu_show(name, 1); 00066 } 00067 00068 static void loadnew_cb(Fl_Widget *w, void *v) { 00069 VMDApp *app = (VMDApp *)(w->user_data()); 00070 app->menu_select_mol("files", -1); 00071 app->menu_show("files", 0); 00072 app->menu_show("files", 1); 00073 } 00074 00075 void MainFltkMenu::loadfile_cb(Fl_Widget *w, void *v) { 00076 VMDApp *app = (VMDApp *)(w->user_data()); 00077 int selmol = ((MainFltkMenu *) v)->get_selected_molecule(); 00078 app->menu_select_mol("files", selmol); 00079 app->menu_show("files", 0); 00080 app->menu_show("files", 1); 00081 } 00082 00083 void MainFltkMenu::savefile_cb(Fl_Widget *w, void *v) { 00084 VMDApp *app = (VMDApp *)(w->user_data()); 00085 int selmol = ((MainFltkMenu *) v)->get_selected_molecule(); 00086 app->menu_select_mol("save", selmol); 00087 app->menu_show("save", 0); 00088 app->menu_show("save", 1); 00089 } 00090 00091 static void render_cb(Fl_Widget *w, void *v) { 00092 VMDApp *app = (VMDApp *)(w->user_data()); 00093 app->menu_show("render", 0); 00094 app->menu_show("render", 1); 00095 } 00096 00097 static void savestate_cb(Fl_Widget *w, void *) { 00098 VMDApp *app = (VMDApp *)(w->user_data()); 00099 if (!app->save_state()) { 00100 fl_alert("Save State failed."); 00101 } 00102 } 00103 00104 static void logfile_cb(Fl_Widget *w, void *) { 00105 VMDApp *app = (VMDApp *)(w->user_data()); 00106 char *file = app->vmd_choose_file( 00107 "Enter filename for VMD session log:", // Title 00108 "*.vmd", // extension 00109 "VMD files", // label 00110 1 // do_save 00111 ); 00112 if (!file) return; 00113 char *buf = new char[strlen(file)+13]; 00114 sprintf(buf, "logfile {%s}", file); 00115 app->commandQueue->runcommand(new TclEvalEvent(buf)); 00116 delete [] buf; 00117 delete [] file; 00118 } 00119 00120 static void logconsole_cb(Fl_Widget *w, void *) { 00121 VMDApp *app = (VMDApp *)(w->user_data()); 00122 const char *buf = "logfile console"; 00123 app->commandQueue->runcommand(new TclEvalEvent(buf)); 00124 } 00125 00126 static void logoff_cb(Fl_Widget *w, void *) { 00127 VMDApp *app = (VMDApp *)(w->user_data()); 00128 const char *buf = "logfile off"; 00129 app->commandQueue->runcommand(new TclEvalEvent(buf)); 00130 } 00131 00132 static void quit_cb(Fl_Widget *w, void *) { 00133 VMDApp *app = (VMDApp *)(w->user_data()); 00134 if (fl_show_question("Really Quit?", 0)) 00135 app->VMDexit("",0,0); 00136 } 00137 00138 static void aa_cb(Fl_Widget *w, void *) { 00139 VMDApp *app = (VMDApp *)(w->user_data()); 00140 app->display_set_aa( 00141 ((Fl_Menu_ *)w)->mvalue()->value()); 00142 } 00143 00144 static void depthcue_cb(Fl_Widget *w, void *) { 00145 VMDApp *app = (VMDApp *)(w->user_data()); 00146 app->display_set_depthcue( 00147 ((Fl_Menu_ *)w)->mvalue()->value()); 00148 } 00149 00150 #if !defined(VMDLEANGUI) 00151 static void culling_cb(Fl_Widget *w, void *) { 00152 VMDApp *app = (VMDApp *)(w->user_data()); 00153 app->display_set_culling( 00154 ((Fl_Menu_ *)w)->mvalue()->value()); 00155 } 00156 #endif 00157 00158 static void fps_cb(Fl_Widget *w, void *) { 00159 VMDApp *app = (VMDApp *)(w->user_data()); 00160 app->display_set_fps( 00161 ((Fl_Menu_ *)w)->mvalue()->value()); 00162 } 00163 00164 static void light_cb(Fl_Widget *w, void *v) { 00165 VMDApp *app = (VMDApp *)(w->user_data()); 00166 int *whichlight = (int *)v; 00167 int turnon = ((Fl_Menu_ *)w)->mvalue()->value(); 00168 app->light_on(*whichlight, turnon); 00169 } 00170 00171 static void stage_cb(Fl_Widget *w, void *v) { 00172 Fl_Menu_ *m = (Fl_Menu_ *)w; 00173 VMDApp *app = (VMDApp *)v; 00174 app->stage_set_location(m->text()); 00175 } 00176 00177 static void axes_cb(Fl_Widget *w, void *v) { 00178 Fl_Menu_ *m = (Fl_Menu_ *)w; 00179 VMDApp *app = (VMDApp *)v; 00180 app->axes_set_location(m->text()); 00181 } 00182 00183 static void backgroundmode_cb(Fl_Widget *w, void *v) { 00184 Fl_Menu_ *m = (Fl_Menu_ *)w; 00185 VMDApp *app = (VMDApp *)(w->user_data()); 00186 if (!strcmp("Gradient", m->text())) { 00187 app->display_set_background_mode(1); 00188 } else { 00189 app->display_set_background_mode(0); 00190 } 00191 } 00192 00193 static void stereo_cb(Fl_Widget *w, void *v) { 00194 Fl_Menu_ *m = (Fl_Menu_ *)w; 00195 VMDApp *app = (VMDApp *)v; 00196 app->display_set_stereo(m->text()); 00197 } 00198 00199 static void stereoswap_cb(Fl_Widget *w, void *v) { 00200 Fl_Menu_ *m = (Fl_Menu_ *)w; 00201 VMDApp *app = (VMDApp *)v; 00202 if (!strcmp("On", m->text())) { 00203 app->display_set_stereo_swap(1); 00204 } else { 00205 app->display_set_stereo_swap(0); 00206 } 00207 } 00208 00209 #if !defined(VMDLEANGUI) 00210 static void cachemode_cb(Fl_Widget *w, void *v) { 00211 Fl_Menu_ *m = (Fl_Menu_ *)w; 00212 VMDApp *app = (VMDApp *)v; 00213 app->display_set_cachemode(m->text()); 00214 } 00215 #endif 00216 00217 static void rendermode_cb(Fl_Widget *w, void *v) { 00218 Fl_Menu_ *m = (Fl_Menu_ *)w; 00219 VMDApp *app = (VMDApp *)v; 00220 app->display_set_rendermode(m->text()); 00221 } 00222 00223 static void resetview_cb(Fl_Widget *w, void *) { 00224 VMDApp *app = (VMDApp *)(w->user_data()); 00225 app->scene_stoprotation(); 00226 app->scene_resetview(); 00227 } 00228 00229 static void stoprotation_cb(Fl_Widget *w, void *) { 00230 VMDApp *app = (VMDApp *)(w->user_data()); 00231 app->scene_stoprotation(); 00232 } 00233 00234 static void proj_cb(Fl_Widget *w, void *) { 00235 Fl_Menu_ *m = (Fl_Menu_ *)w; 00236 VMDApp *app = (VMDApp *)(w->user_data()); 00237 app->display_set_projection(m->text()); 00238 } 00239 00240 static void mouse_cb(Fl_Widget *w, void *v) { 00241 VMDApp *app = (VMDApp *)(w->user_data()); 00242 app->mouse_set_mode(*((int *)v), -1); 00243 } 00244 00245 static void move_light_cb(Fl_Widget *w, void *v) { 00246 VMDApp *app = (VMDApp *)(w->user_data()); 00247 app->mouse_set_mode(Mouse::LIGHT, *((int *)v) ); 00248 } 00249 00250 static void help_cb(Fl_Widget *w, void *v) { 00251 VMDApp *app = (VMDApp *)(w->user_data()); 00252 app->commandQueue->runcommand(new HelpEvent((const char*)v)); 00253 } 00254 00255 // edit menu callbacks 00256 static void mol_top_cb(Fl_Widget *w, void *v) { 00257 VMDApp *app = (VMDApp *)w->user_data(); 00258 MolBrowser *browser = (MolBrowser *)v; 00259 for (int i=0; i<browser->size(); i++) { 00260 if (browser->selected(i+1)) { 00261 app->molecule_make_top(app->molecule_id(i)); 00262 break; 00263 } 00264 } 00265 } 00266 00267 static void mol_active_cb(Fl_Widget *w, void *v) { 00268 VMDApp *app = (VMDApp *)w->user_data(); 00269 MolBrowser *browser = (MolBrowser *)v; 00270 for (int i=0; i<browser->size(); i++) { 00271 if (browser->selected(i+1)) { 00272 int molid = app->molecule_id(i); 00273 app->molecule_activate(molid, !app->molecule_is_active(molid)); 00274 } 00275 } 00276 } 00277 00278 static void mol_displayed_cb(Fl_Widget *w, void *v) { 00279 VMDApp *app = (VMDApp *)w->user_data(); 00280 MolBrowser *browser = (MolBrowser *)v; 00281 for (int i=0; i<browser->size(); i++) { 00282 if (browser->selected(i+1)) { 00283 int molid = app->molecule_id(i); 00284 app->molecule_display(molid, !app->molecule_is_displayed(molid)); 00285 } 00286 } 00287 } 00288 00289 static void mol_fixed_cb(Fl_Widget *w, void *v) { 00290 VMDApp *app = (VMDApp *)w->user_data(); 00291 MolBrowser *browser = (MolBrowser *)v; 00292 for (int i=0; i<browser->size(); i++) { 00293 if (browser->selected(i+1)) { 00294 int molid = app->molecule_id(i); 00295 app->molecule_fix(molid, !app->molecule_is_fixed(molid)); 00296 } 00297 } 00298 } 00299 00300 00301 static void mol_rename_cb(Fl_Widget *w, void *v) { 00302 VMDApp *app = (VMDApp *)w->user_data(); 00303 MolBrowser *browser = (MolBrowser *)v; 00304 int molid=-1; 00305 for (int i=0; i<browser->size(); i++) 00306 if (browser->selected(i+1)) { 00307 molid = app->molecule_id(i); 00308 break; 00309 } 00310 if (molid < 0) return; 00311 00312 // this code snippet is replicated in MolBrowser.C: 00313 const char *oldname = app->molecule_name(molid); 00314 const char *newname = fl_input("Enter a new name for molecule %d:", 00315 oldname, molid); 00316 if (newname) app->molecule_rename(molid, newname); 00317 } 00318 00319 00320 static void mol_cancel_cb(Fl_Widget *w, void *v) { 00321 VMDApp *app = (VMDApp *)w->user_data(); 00322 MolBrowser *browser = (MolBrowser *)v; 00323 for (int i=0; i<browser->size(); i++) { 00324 if (browser->selected(i+1)) { 00325 int molid = app->molecule_id(i); 00326 app->molecule_cancel_io(molid); 00327 } 00328 } 00329 } 00330 00331 static void mol_delete_ts_cb(Fl_Widget *w, void *v) { 00332 VMDApp *app = (VMDApp *)w->user_data(); 00333 MolBrowser *browser = (MolBrowser *)v; 00334 int molid=-1; 00335 for (int i=0; i<browser->size(); i++) 00336 if (browser->selected(i+1)) { 00337 molid = app->molecule_id(i); 00338 break; 00339 } 00340 if (molid < 0) return; 00341 00342 // this code snippet is replicated in MolBrowser.C: 00343 int numframes = app->molecule_numframes(molid); 00344 if (!numframes) { 00345 fl_alert("Molecule %d has no frames to delete!", molid); 00346 } else { 00347 const char *molname = app->molecule_name(molid); 00348 int first=0, last=numframes-1, stride=0; 00349 int ok = frame_delete_selector(molname, last, &first, &last, &stride); 00350 if (ok) app->molecule_deleteframes(molid, first, last, stride); 00351 } 00352 } 00353 00354 static void mol_delete_cb(Fl_Widget *w, void *v) { 00355 VMDApp *app = (VMDApp *)w->user_data(); 00356 MolBrowser *browser = (MolBrowser *)v; 00357 ResizeArray<int> idlist; 00358 for (int i=0; i<browser->size(); i++) { 00359 if (browser->selected(i+1)) { 00360 idlist.append(app->molecule_id(i)); 00361 } 00362 } 00363 for (int j=0; j<idlist.num(); j++) 00364 app->molecule_delete(idlist[j]); 00365 } 00366 00367 static void loadstate_cb(Fl_Widget *w, void *v) { 00368 VMDApp *app = (VMDApp *)w->user_data(); 00369 char *file = app->vmd_choose_file( 00370 "Enter filename containing VMD saved state:", // Title 00371 "*.vmd", // extension 00372 "VMD files", // label 00373 0 // do_save 00374 ); 00375 if (!file) return; 00376 char *buf = new char[strlen(file)+10]; 00377 sprintf(buf, "play {%s}", file); 00378 app->commandQueue->runcommand(new TclEvalEvent(buf)); 00379 delete [] buf; 00380 delete [] file; 00381 } 00382 00383 00384 // the menu behavior describes whether or not the menu item require a 00385 // molecule(s) to exist or be selected in the main browser or not, in order 00386 // to be active. The fields need to be in the same order as they appear in 00387 // the menu description. 00388 00389 static const MenuBehavior file_menu_behavior[] = { 00390 MENU_ALWAYS_ON, // new 00391 MENU_NEED_UNIQUE_SEL, // load file 00392 MENU_NEED_UNIQUE_SEL, // save file 00393 MENU_ALWAYS_ON, // load state 00394 MENU_ALWAYS_ON, // save state 00395 MENU_ALWAYS_ON, // log tcl commands to console 00396 MENU_ALWAYS_ON, // log tcl commands to file 00397 MENU_ALWAYS_ON, // logging off 00398 MENU_ALWAYS_ON, // render 00399 MENU_ALWAYS_ON // quit 00400 }; 00401 00402 // Note: the user_data (i.e. callback argument) for all file_menu items 00403 // will be reset to the "this" MainFltkMenu object instance. 00404 static const Fl_Menu_Item init_file_menuitems[] = { 00405 {"New Molecule...", 0, loadnew_cb}, 00406 {"Load Data Into Molecule...", 0, NULL /*set later*/}, 00407 {"Save Coordinates...", 0, NULL /*set later*/, NULL, FL_MENU_DIVIDER}, 00408 {"Load Visualization State...", 0, loadstate_cb}, 00409 {"Save Visualization State...", 0, savestate_cb, NULL, FL_MENU_DIVIDER}, 00410 {"Log Tcl Commands to Console", 0, logconsole_cb, NULL}, 00411 {"Log Tcl Commands to File...", 0, logfile_cb, NULL}, 00412 {"Turn Off Logging", 0, logoff_cb, NULL, FL_MENU_DIVIDER}, 00413 {"Render...", 0, render_cb, NULL, FL_MENU_DIVIDER}, 00414 {"Quit", 0, quit_cb}, 00415 {NULL} 00416 }; 00417 00418 static const MenuBehavior molecule_menu_behavior[] = { 00419 MENU_NEED_UNIQUE_SEL, // top 00420 MENU_NEED_SEL, // active 00421 MENU_NEED_SEL, // displayed 00422 MENU_NEED_SEL, // fixed 00423 MENU_NEED_UNIQUE_SEL, // rename 00424 MENU_NEED_UNIQUE_SEL, // delete ts 00425 MENU_NEED_SEL, // cancel file i/o 00426 MENU_NEED_SEL // delete mol 00427 }; 00428 00429 // Note: the user_data (i.e. callback argument) for all molecule_menu items 00430 // will be reset to this->browser. 00431 static const Fl_Menu_Item init_molecule_menuitems[] = { 00432 {"Make Top", 0, mol_top_cb, }, 00433 {"Toggle Active", 0, mol_active_cb, }, 00434 {"Toggle Displayed", 0, mol_displayed_cb, }, 00435 {"Toggle Fixed", 0, mol_fixed_cb, NULL, FL_MENU_DIVIDER}, 00436 {"Rename...", 0, mol_rename_cb }, 00437 {"Delete Frames...", 0, mol_delete_ts_cb }, 00438 {"Abort File I/O", 0, mol_cancel_cb, }, 00439 {"Delete Molecule", 0, mol_delete_cb }, 00440 {NULL} 00441 }; 00442 00443 00444 static const MenuBehavior browserpopup_menu_behavior[] = { 00445 MENU_ALWAYS_ON, // new 00446 MENU_NEED_UNIQUE_SEL, // load file 00447 MENU_NEED_UNIQUE_SEL, // save file 00448 MENU_NEED_UNIQUE_SEL, // rename 00449 MENU_NEED_UNIQUE_SEL, // delete ts 00450 MENU_NEED_SEL, // cancel file i/o 00451 MENU_NEED_SEL // delete mol 00452 }; 00453 00454 // Note: the user_data (i.e. callback argument) for all molecule_menu items 00455 // will be reset to this->browser. 00456 static const Fl_Menu_Item init_browserpopup_menuitems[] = { 00457 // Here: user_data will be set to (MainFltkMenu*) this. 00458 {"New Molecule...", 0, loadnew_cb }, 00459 {"Load Data Into Molecule...", 0, NULL /* set later */}, 00460 {"Save Coordinates...", 0, NULL /* set later */, NULL, FL_MENU_DIVIDER}, 00461 // Here: user_data will be set to this->browser 00462 {"Rename...", 0, mol_rename_cb }, 00463 {"Delete Frames...", 0, mol_delete_ts_cb }, 00464 {"Abort File I/O", 0, mol_cancel_cb, }, 00465 {"Delete Molecule", 0, mol_delete_cb }, 00466 {NULL} 00467 }; 00468 00469 static const Fl_Menu_Item graphics_menuitems[] = { 00470 {"Representations...", 0, menu_cb, (void *)"graphics"}, 00471 {"Colors...", 0, menu_cb, (void *)"color"}, 00472 {"Materials...", 0, menu_cb, (void *)"material"}, 00473 {"Labels...", 0, menu_cb, (void *)"labels", FL_MENU_DIVIDER}, 00474 {"Tools...", 0, menu_cb, (void *)"tool"}, 00475 {0} 00476 }; 00477 00478 static int cbdata[] = { 00479 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22 00480 }; 00481 00482 00483 enum DispMenu { 00484 DM_RESETVIEW=0, 00485 DM_STOPROTATION, 00486 DM_PERSPECTIVE, 00487 DM_ORTHOGRAPHIC, 00488 DM_ANTIALIASING, 00489 DM_DEPTHCUEING, 00490 #if !defined(VMDLEANGUI) 00491 DM_CULLING, 00492 #endif 00493 DM_FPS, 00494 DM_LIGHT0, 00495 DM_LIGHT1, 00496 DM_LIGHT2, 00497 DM_LIGHT3, 00498 DM_AXES, 00499 DM_BACKGROUND, 00500 DM_STAGE, 00501 DM_STEREO, 00502 DM_STEREOEYESWAP, 00503 #if !defined(VMDLEANGUI) 00504 DM_CACHEMODE, 00505 #endif 00506 DM_RENDERMODE, 00507 DM_DISPSETTINGS, 00508 DM_LASTMENUITEM 00509 }; 00510 00511 static const Fl_Menu_Item init_display_menuitems[] = { 00512 {"Reset View", '=', resetview_cb}, 00513 {"Stop Rotation", 0, stoprotation_cb, NULL, FL_MENU_DIVIDER}, 00514 {"Perspective", 0, proj_cb, NULL, FL_MENU_RADIO }, 00515 {"Orthographic", 0, proj_cb, NULL, FL_MENU_RADIO | FL_MENU_DIVIDER}, 00516 {"Antialiasing", 0, aa_cb, NULL, FL_MENU_TOGGLE | FL_MENU_INACTIVE}, 00517 {"Depth Cueing", 0, depthcue_cb, NULL, FL_MENU_TOGGLE | FL_MENU_INACTIVE}, 00518 #if !defined(VMDLEANGUI) 00519 {"Culling", 0, culling_cb, NULL, FL_MENU_TOGGLE | FL_MENU_INACTIVE}, 00520 #endif 00521 {"FPS Indicator", 0, fps_cb, NULL, FL_MENU_TOGGLE | FL_MENU_DIVIDER}, 00522 {"Light 0", 0, light_cb, cbdata+0, FL_MENU_TOGGLE}, 00523 {"Light 1", 0, light_cb, cbdata+1, FL_MENU_TOGGLE}, 00524 {"Light 2", 0, light_cb, cbdata+2, FL_MENU_TOGGLE}, 00525 {"Light 3", 0, light_cb, cbdata+3, FL_MENU_TOGGLE | FL_MENU_DIVIDER}, 00526 {"Axes", 0, NULL, NULL, FL_SUBMENU_POINTER}, 00527 {"Background", 0, backgroundmode_cb, NULL, FL_SUBMENU_POINTER}, 00528 {"Stage", 0, NULL, NULL, FL_SUBMENU_POINTER | FL_MENU_DIVIDER}, 00529 {"Stereo", 0, NULL, NULL, FL_SUBMENU_POINTER}, 00530 {"Stereo Eye Swap", 0, NULL, NULL, FL_SUBMENU_POINTER | FL_MENU_DIVIDER}, 00531 #if !defined(VMDLEANGUI) 00532 {"Cachemode", 0, NULL, NULL, FL_SUBMENU_POINTER}, 00533 #endif 00534 {"Rendermode", 0, NULL, NULL, FL_SUBMENU_POINTER | FL_MENU_DIVIDER}, 00535 {"Display Settings...", 0, menu_cb, (void *)"display"}, 00536 {0} 00537 }; 00538 00539 // forward declaration 00540 static void cb_cb(Fl_Widget *w, void *v); 00541 00542 // These are the items that appear in the mouse submenu. 00543 // If items are added or removed from this menu, the update_mousemode method 00544 // must be updated to reflect the new positions if the items. 00545 static const Fl_Menu_Item init_mouse_menuitems[] = { 00546 {"Rotate Mode", 'r', mouse_cb, cbdata+Mouse::ROTATION,FL_MENU_RADIO|FL_MENU_VALUE}, 00547 {"Translate Mode",'t',mouse_cb,cbdata+Mouse::TRANSLATION,FL_MENU_RADIO}, 00548 {"Scale Mode", 's', mouse_cb, cbdata+Mouse::SCALING, FL_MENU_RADIO | FL_MENU_DIVIDER}, 00549 {"Center", 'c', mouse_cb, cbdata+Mouse::CENTER, FL_MENU_RADIO}, 00550 {"Query", '0', mouse_cb, cbdata+Mouse::QUERY, FL_MENU_RADIO}, 00551 00552 {"Label",0,cb_cb,0, FL_SUBMENU | FL_MENU_TOGGLE }, 00553 {"Atoms", '1', mouse_cb, cbdata+Mouse::LABELATOM, FL_MENU_RADIO}, 00554 {"Bonds", '2', mouse_cb, cbdata+Mouse::LABELBOND, FL_MENU_RADIO}, 00555 {"Angles", '3', mouse_cb, cbdata+Mouse::LABELANGLE, FL_MENU_RADIO}, 00556 {"Dihedrals", '4', mouse_cb, cbdata+Mouse::LABELDIHEDRAL, FL_MENU_RADIO}, 00557 {0}, 00558 {"Move",0,cb_cb,0, FL_SUBMENU | FL_MENU_TOGGLE}, 00559 {"Atom", '5', mouse_cb, cbdata+Mouse::MOVEATOM, FL_MENU_RADIO}, 00560 {"Residue", '6', mouse_cb, cbdata+Mouse::MOVERES, FL_MENU_RADIO}, 00561 {"Fragment", '7', mouse_cb, cbdata+Mouse::MOVEFRAG, FL_MENU_RADIO}, 00562 {"Molecule", '8', mouse_cb, cbdata+Mouse::MOVEMOL, FL_MENU_RADIO}, 00563 {"Rep", '9', mouse_cb, cbdata+Mouse::MOVEREP, FL_MENU_RADIO}, 00564 {0}, 00565 {"Force",0,cb_cb,0, FL_SUBMENU | FL_MENU_TOGGLE}, 00566 {"Atom", '%', mouse_cb, cbdata+Mouse::FORCEATOM, FL_MENU_RADIO}, 00567 {"Residue", '^', mouse_cb, cbdata+Mouse::FORCERES, FL_MENU_RADIO}, 00568 {"Fragment", '&', mouse_cb, cbdata+Mouse::FORCEFRAG, FL_MENU_RADIO}, 00569 {0}, 00570 {"Move Light", 0,cb_cb,0, FL_SUBMENU | FL_MENU_TOGGLE}, 00571 {"0", 0, move_light_cb, cbdata+0, FL_MENU_RADIO}, 00572 {"1", 0, move_light_cb, cbdata+1, FL_MENU_RADIO}, 00573 {"2", 0, move_light_cb, cbdata+2, FL_MENU_RADIO}, 00574 {"3", 0, move_light_cb, cbdata+3, FL_MENU_RADIO}, 00575 {0}, 00576 {"Add/Remove Bonds", 0, mouse_cb, cbdata+Mouse::ADDBOND, FL_MENU_RADIO}, 00577 {"Pick", 'p', mouse_cb, cbdata+Mouse::PICK, FL_MENU_RADIO}, 00578 {0} 00579 }; 00580 00581 static const Fl_Menu_Item init_help_menuitems[] = { 00582 {"Quick Help", 0, help_cb, (void*) "quickhelp"}, 00583 {"User's Guide", 0, help_cb, (void*) "userguide"}, 00584 {"Tutorial", 0, help_cb, (void*) "tutorial", FL_MENU_DIVIDER}, 00585 {"Homepage", 0, help_cb, (void*) "homepage"}, 00586 {"FAQ", 0, help_cb, (void*) "faq"}, 00587 {"Mailing List", 0, help_cb, (void*) "maillist"}, 00588 {"Script Library", 0, help_cb, (void*) "scripts"}, 00589 {"Plugin Library", 0, help_cb, (void*) "plugins", FL_MENU_DIVIDER}, 00590 {"3D Renderers", 0, 0, 0, FL_SUBMENU}, 00591 {"POV-Ray", 0, help_cb, (void*) "povray"}, 00592 {"Radiance", 0, help_cb, (void*) "radiance"}, 00593 {"Raster3D", 0, help_cb, (void*) "raster3D"}, 00594 {"Rayshade", 0, help_cb, (void*) "rayshade"}, 00595 {"Tachyon", 0, help_cb, (void*) "tachyon"}, 00596 {"VRML", 0, help_cb, (void*) "vrml"}, 00597 {0}, 00598 {"Auxiliary Programs", 0, 0, 0, FL_SUBMENU}, 00599 {"BioCoRE", 0, help_cb, (void*) "biocore"}, 00600 {"MSMS", 0, help_cb, (void*) "msms"}, 00601 {"NanoShaper", 0, help_cb, (void*) "nanoshaper"}, 00602 {"NAMD", 0, help_cb, (void*) "namd"}, 00603 {"Tcl/Tk", 0, help_cb, (void*) "tcl"}, 00604 {"Python", 0, help_cb, (void*) "python"}, 00605 {0}, 00606 {0} 00607 }; 00608 00609 // turn the item on if any of its children are on; otherwise restore it 00610 // to its off state. 00611 static void cb_cb(Fl_Widget *w, void *v) { 00612 Fl_Menu_Item *titleitem = (Fl_Menu_Item*) ((Fl_Menu_ *)w)->mvalue(); 00613 const Fl_Menu_Item *item; 00614 for (item = titleitem+1; item->label(); item++) 00615 if (item->value()) { 00616 titleitem->set(); 00617 return; 00618 } 00619 titleitem->clear(); 00620 } 00621 00622 void MainFltkMenu::frameslider_cb(Fl_Widget *w, void *v) { 00623 Fl_Valuator *val = (Fl_Valuator *)w; 00624 MainFltkMenu *self = (MainFltkMenu *)v; 00625 // If the right mouse button is active, update frame only on release... 00626 //if (Fl::event_button() == FL_RIGHT_MOUSE) { XXX wrong way to do it 00627 if (Fl::event_state(FL_BUTTON3)) { 00628 if (!Fl::event_state()) { 00629 self->app->animation_set_frame((int)val->value()); 00630 } else { 00631 // but still update the value displayed in the current frame. 00632 char buf[10]; 00633 sprintf(buf, "%d", (int)val->value()); 00634 self->curframe->value(buf); 00635 } 00636 } else { 00637 self->app->animation_set_frame((int)val->value()); 00638 } 00639 } 00640 00641 static void curframe_cb(Fl_Widget *w, void *v) { 00642 Fl_Input *inp = (Fl_Input *)w; 00643 VMDApp *app = (VMDApp *)v; 00644 int val = atoi(inp->value()); 00645 int max = app->molecule_numframes(app->molecule_top()); 00646 if (val < 0) val = 0; 00647 if (val >= max) val = max-1; 00648 app->animation_set_frame(val); 00649 } 00650 00651 static void start_cb(Fl_Widget *, void *v) { 00652 VMDApp *app = (VMDApp *)v; 00653 app->animation_set_frame(-1); 00654 } 00655 00656 static void stop_cb(Fl_Widget *, void *v) { 00657 VMDApp *app = (VMDApp *)v; 00658 app->animation_set_frame(-2); 00659 } 00660 00661 static void prev_cb(Fl_Widget *, void *v) { 00662 VMDApp *app = (VMDApp *)v; 00663 app->animation_set_dir(Animation::ANIM_REVERSE1); 00664 } 00665 00666 static void next_cb(Fl_Widget *, void *v) { 00667 VMDApp *app = (VMDApp *)v; 00668 app->animation_set_dir(Animation::ANIM_FORWARD1); 00669 } 00670 00671 static void forward_cb(Fl_Widget *w, void *v) { 00672 Fl_Button *button = (Fl_Button *)w; 00673 VMDApp *app = (VMDApp *)v; 00674 if (button->value()) 00675 app->animation_set_dir(Animation::ANIM_FORWARD); 00676 else 00677 app->animation_set_dir(Animation::ANIM_PAUSE); 00678 } 00679 00680 static void reverse_cb(Fl_Widget *w, void *v) { 00681 Fl_Button *button = (Fl_Button *)w; 00682 VMDApp *app = (VMDApp *)v; 00683 if (button->value()) 00684 app->animation_set_dir(Animation::ANIM_REVERSE); 00685 else 00686 app->animation_set_dir(Animation::ANIM_PAUSE); 00687 } 00688 00689 static void style_cb(Fl_Widget *w, void *v) { 00690 Fl_Choice *choice = (Fl_Choice *)w; 00691 VMDApp *app = (VMDApp *)v; 00692 app->animation_set_style(choice->value()); 00693 } 00694 00695 static void step_cb(Fl_Widget *w, void *v) { 00696 Fl_Counter *counter = (Fl_Counter *)w; 00697 VMDApp *app = (VMDApp *)v; 00698 app->animation_set_stride((int)counter->value()); 00699 } 00700 00701 static void speed_cb(Fl_Widget *w, void *v) { 00702 Fl_Slider *slider = (Fl_Slider *)w; 00703 VMDApp *app = (VMDApp *)v; 00704 app->animation_set_speed((float) slider->value()); 00705 } 00706 00707 void MainFltkMenu::zoom_cb(Fl_Widget *w, void *v) { 00708 Fl_Button *b = (Fl_Button *)w; 00709 MainFltkMenu *self = (MainFltkMenu *)v; 00710 int numframes = self->app->molecule_numframes(self->app->molecule_top()); 00711 if (numframes < 1) return; 00712 double full_range = (double)numframes; 00713 if (b->value()) { 00714 // turn on zoom: recenter the range around the current value of the slider 00715 double pixel_range = 100; 00716 if (full_range > pixel_range) { 00717 double curval = self->frameslider->value(); 00718 double curfrac = curval/full_range; 00719 self->frameslider->range(curval - pixel_range*curfrac, 00720 curval + pixel_range*(1.0-curfrac)); 00721 self->frameslider->color(VMDMENU_SLIDER_BG, VMDMENU_SLIDER_FG); 00722 self->frameslider->redraw(); 00723 } 00724 } else { 00725 // turn off zoom; make the range equal to the number of frames 00726 self->frameslider->range(0, full_range-1); 00727 self->frameslider->color(VMDMENU_SLIDER_BG, VMDMENU_SLIDER_FG); 00728 self->frameslider->redraw(); 00729 } 00730 } 00731 00732 void MainFltkMenu::update_mousemode(Command *cmd) { 00733 int mode = ((CmdMouseMode *)cmd)->mouseMode; 00734 int setting = ((CmdMouseMode *)cmd)->mouseSetting; 00735 00736 Fl_Menu_Item *items = mouse_menuitems; 00737 int menulen = sizeof(init_mouse_menuitems)/sizeof(Fl_Menu_Item); 00738 for (int j=0; j<menulen; j++) // replaced hard-coded <=29 with <menulen 00739 items[j].clear(); 00740 00741 switch(mode) { 00742 case Mouse::ROTATION: items[ 0].setonly(); break; 00743 case Mouse::TRANSLATION: items[ 1].setonly(); break; 00744 case Mouse::SCALING: items[ 2].setonly(); break; 00745 case Mouse::QUERY: items[ 4].setonly(); break; 00746 case Mouse::CENTER: items[ 3].setonly(); break; 00747 case Mouse::LABELATOM: items[ 6].setonly(); break; 00748 case Mouse::LABELBOND: items[ 7].setonly(); break; 00749 case Mouse::LABELANGLE: items[ 8].setonly(); break; 00750 case Mouse::LABELDIHEDRAL: items[ 9].setonly(); break; 00751 case Mouse::MOVEATOM: items[12].setonly(); break; 00752 case Mouse::MOVERES: items[13].setonly(); break; 00753 case Mouse::MOVEFRAG: items[14].setonly(); break; 00754 case Mouse::MOVEMOL: items[15].setonly(); break; 00755 case Mouse::MOVEREP: items[16].setonly(); break; 00756 case Mouse::FORCEATOM: items[19].setonly(); break; 00757 case Mouse::FORCERES: items[20].setonly(); break; 00758 case Mouse::FORCEFRAG: items[21].setonly(); break; 00759 case Mouse::ADDBOND: items[29].setonly(); break; 00760 case Mouse::PICK: items[30].setonly(); break; 00761 case Mouse::LIGHT: 00762 switch (setting) { 00763 case 0: items[24].setonly(); break; 00764 case 1: items[25].setonly(); break; 00765 case 2: items[26].setonly(); break; 00766 case 3: items[27].setonly(); break; 00767 } 00768 } 00769 if (mode >= Mouse::PICK) { 00770 items[0].setonly(); // check "rotate" mouse mode 00771 if (mode == Mouse::LABELATOM || mode == Mouse::LABELBOND || \ 00772 mode == Mouse::LABELANGLE || mode == Mouse::LABELDIHEDRAL) 00773 items[5].set(); 00774 else if (mode == Mouse::MOVEATOM || mode == Mouse::MOVERES || \ 00775 mode == Mouse::MOVEMOL || mode == Mouse::MOVEREP) 00776 items[11].set(); 00777 else if (mode == Mouse::FORCEATOM || mode == Mouse::FORCERES || mode == Mouse::FORCEFRAG) 00778 items[18].set(); 00779 } else if (mode == Mouse::LIGHT) { 00780 if (setting >= 0 && setting <= 3) items[23].set(); 00781 } 00782 } 00783 00784 void MainFltkMenu::update_dispmode() { 00785 // XXX the implementation here is ugly because older FLTK APIs 00786 // lack the value(int) methods, and we can only call set/clear(). 00787 // With FLTK 1.3.x we could instead do things somewhat more cleanly, 00788 // display_menuitems[DM_ANTIALIASING].value((app->display->aa_enabled()!=0)); 00789 00790 // match the active projection string and set radio button state 00791 const char *projname = app->display->get_projection(); 00792 for (int ii=DM_PERSPECTIVE; ii<=DM_ORTHOGRAPHIC; ii++) { 00793 if (!strupcmp(projname, display_menuitems[ii].label())) { 00794 display_menuitems[ii].setonly(); 00795 break; 00796 } 00797 } 00798 00799 // update antialiasing on/off state 00800 if (app->display->aa_enabled()) 00801 display_menuitems[DM_ANTIALIASING].set(); 00802 else 00803 display_menuitems[DM_ANTIALIASING].clear(); 00804 00805 // update depth cueing on/off state 00806 if (app->display->cueing_enabled()) 00807 display_menuitems[DM_DEPTHCUEING].set(); 00808 else 00809 display_menuitems[DM_DEPTHCUEING].clear(); 00810 00811 #if !defined(VMDLEANGUI) 00812 // update backface culling on/off state 00813 if (app->display->culling_enabled()) 00814 display_menuitems[DM_CULLING].set(); 00815 else 00816 display_menuitems[DM_CULLING].clear(); 00817 #endif 00818 00819 // update display FPS on/off state 00820 if (app->fps->displayed()) 00821 display_menuitems[DM_FPS].set(); 00822 else 00823 display_menuitems[DM_FPS].clear(); 00824 00825 // update light 0,1,2,3 on/off states 00826 for (int j=0; j<4; j++) 00827 if (app->scene->light_active(j)) 00828 display_menuitems[DM_LIGHT0+j].set(); 00829 else 00830 display_menuitems[DM_LIGHT0+j].clear(); 00831 00832 // set active submenu states for axes, background, stage, 00833 // stereo mode, stereo eye swap, display list caching, and rendering mode 00834 axes_menuitems[app->axes->location()].setonly(); 00835 backgroundmode_menuitems[app->scene->background_mode()].setonly(); 00836 stage_menuitems[app->stage->location()].setonly(); 00837 stereo_menuitems[app->display->stereo_mode()].setonly(); 00838 stereoswap_menuitems[app->display->stereo_swap()].setonly(); 00839 #if !defined(VMDLEANGUI) 00840 cachemode_menuitems[app->display->cache_mode()].setonly(); 00841 #endif 00842 rendermode_menuitems[app->display->render_mode()].setonly(); 00843 } 00844 00845 00846 // Add some extra space at the bottom of the menu for the OSX resizing tab; 00847 // otherwise it obscures buttons on the menu. 00848 #if defined(__APPLE__) 00849 #define MAINFLTKMENUHEIGHT 205 00850 #else 00851 #define MAINFLTKMENUHEIGHT 190 00852 #endif 00853 00854 #if 0 00855 // original main window width used for fixed-width non-antialiased fonts 00856 #define MAINFLTKMENUWIDTH 450 00857 #else 00858 // main window width needed for antialiased fonts via Xft-enabled FLTK builds 00859 #define MAINFLTKMENUWIDTH 470 00860 #endif 00861 00862 00863 MainFltkMenu::MainFltkMenu(VMDApp *vmdapp) 00864 : VMDFltkMenu("main", "VMD Main", vmdapp) { 00865 // set initial window size 00866 size(MAINFLTKMENUWIDTH, MAINFLTKMENUHEIGHT); 00867 00868 // set resizable in y but not in x... 00869 size_range(MAINFLTKMENUWIDTH, MAINFLTKMENUHEIGHT, MAINFLTKMENUWIDTH, 0); 00870 00871 command_wanted(Command::MOL_NEW); 00872 command_wanted(Command::MOL_DEL); 00873 command_wanted(Command::MOL_ACTIVE); 00874 command_wanted(Command::MOL_ON); 00875 command_wanted(Command::MOL_RENAME); 00876 command_wanted(Command::MOL_FIX); 00877 command_wanted(Command::MOL_TOP); 00878 command_wanted(Command::MOL_VOLUME); 00879 command_wanted(Command::ANIM_JUMP); 00880 command_wanted(Command::ANIM_NEW_FRAME); 00881 command_wanted(Command::ANIM_NEW_NUM_FRAMES); 00882 command_wanted(Command::MOUSE_MODE); 00883 command_wanted(Command::MENU_TK_ADD); 00884 command_wanted(Command::MENU_TK_REMOVE); 00885 command_wanted(Command::ANIM_STYLE); 00886 command_wanted(Command::ANIM_SKIP); 00887 command_wanted(Command::ANIM_SPEED); 00888 command_wanted(Command::ANIM_DIRECTION); 00889 command_wanted(Command::ANIM_JUMP); 00890 00891 command_wanted(Command::DISP_DEPTHCUE); 00892 command_wanted(Command::DISP_CULLING); 00893 command_wanted(Command::DISP_ANTIALIAS); 00894 command_wanted(Command::DISP_FPS); 00895 command_wanted(Command::DISP_LIGHT_ON); 00896 command_wanted(Command::CMD_STAGE); 00897 command_wanted(Command::CMD_AXES); 00898 command_wanted(Command::DISP_BACKGROUNDGRADIENT); 00899 command_wanted(Command::DISP_PROJ); 00900 command_wanted(Command::DISP_STEREO); 00901 command_wanted(Command::DISP_STEREOSWAP); 00902 command_wanted(Command::DISP_CACHEMODE); 00903 command_wanted(Command::DISP_RENDERMODE); 00904 00905 browser = new MolBrowser(vmdapp, this, 0, 60, MAINFLTKMENUWIDTH, 90); 00906 00907 // ******** CREATE MENUS ********* 00908 // We make copies of the static data because we will be changing the state 00909 // and contents of some menus and menu items. 00910 00911 int menulen; 00912 Fl_Menu_Item nullitem = {NULL}; 00913 00914 // create menu instances and fill in user_data fields for menu callback use. 00915 menulen = sizeof(init_file_menuitems)/sizeof(Fl_Menu_Item); 00916 file_menuitems = new Fl_Menu_Item[menulen]; 00917 int j; 00918 for (j=0; j<menulen; j++) { 00919 file_menuitems[j] = init_file_menuitems[j]; 00920 file_menuitems[j].user_data(this); 00921 } 00922 // these are set here because the are private functions 00923 file_menuitems[1].callback(loadfile_cb); 00924 file_menuitems[2].callback(savefile_cb); 00925 00926 menulen = sizeof(init_molecule_menuitems)/sizeof(Fl_Menu_Item); 00927 molecule_menuitems = new Fl_Menu_Item[menulen]; 00928 for (j=0; j<menulen; j++) { 00929 molecule_menuitems[j] = init_molecule_menuitems[j]; 00930 molecule_menuitems[j].user_data(browser); 00931 } 00932 00933 00934 // This is the popup menu in the molbrowser window (mix of file and molecule menus) 00935 menulen = sizeof(init_browserpopup_menuitems)/sizeof(Fl_Menu_Item); 00936 browserpopup_menuitems = new Fl_Menu_Item[menulen]; 00937 for (j=0; j<3; j++) { 00938 browserpopup_menuitems[j] = init_browserpopup_menuitems[j]; 00939 browserpopup_menuitems[j].user_data(this); 00940 } 00941 for (j=3; j<menulen; j++) { 00942 browserpopup_menuitems[j] = init_browserpopup_menuitems[j]; 00943 browserpopup_menuitems[j].user_data(browser); 00944 } 00945 // these are set here because the are private functions 00946 browserpopup_menuitems[1].callback(loadfile_cb); 00947 browserpopup_menuitems[2].callback(savefile_cb); 00948 00949 00950 menulen = sizeof(init_display_menuitems)/sizeof(Fl_Menu_Item); 00951 display_menuitems = new Fl_Menu_Item[menulen]; 00952 for (j=0; j<menulen; j++) 00953 display_menuitems[j] = init_display_menuitems[j]; 00954 if (app->display->aa_available()) display_menuitems[DM_ANTIALIASING].activate(); 00955 if (app->display->cueing_available()) display_menuitems[DM_DEPTHCUEING].activate(); 00956 #if !defined(VMDLEANGUI) 00957 if (app->display->culling_available()) display_menuitems[DM_CULLING].activate(); 00958 #endif 00959 00960 menulen = app->axes->locations(); 00961 axes_menuitems_storage = new Fl_Menu_Item[menulen+2]; 00962 axes_menuitems_storage[0] = nullitem; // pad the beginning of the array 00963 // to prevent an Fltk crash 00964 axes_menuitems = axes_menuitems_storage+1; 00965 for (j=0; j<menulen; j++) { 00966 Fl_Menu_Item item = {app->axes->loc_description(j), 0, axes_cb, app, FL_MENU_RADIO}; 00967 axes_menuitems[j] = item; 00968 } 00969 axes_menuitems[menulen] = nullitem; 00970 display_menuitems[DM_AXES].user_data(axes_menuitems); 00971 00972 menulen = 2; 00973 backgroundmode_menuitems_storage = new Fl_Menu_Item[menulen+2]; 00974 backgroundmode_menuitems_storage[0] = nullitem; 00975 backgroundmode_menuitems = backgroundmode_menuitems_storage+1; 00976 { 00977 Fl_Menu_Item item = { "Solid Color", 0, backgroundmode_cb, app, FL_MENU_RADIO}; 00978 backgroundmode_menuitems[0] = item; 00979 } 00980 { 00981 Fl_Menu_Item item = { "Gradient", 0, backgroundmode_cb, app, FL_MENU_RADIO}; 00982 backgroundmode_menuitems[1] = item; 00983 } 00984 backgroundmode_menuitems[menulen] = nullitem; 00985 display_menuitems[DM_BACKGROUND].user_data(backgroundmode_menuitems); 00986 00987 menulen = app->stage->locations(); 00988 stage_menuitems_storage = new Fl_Menu_Item[menulen+2]; 00989 stage_menuitems_storage[0] = nullitem; 00990 stage_menuitems = stage_menuitems_storage+1; 00991 for (j=0; j<menulen; j++) { 00992 Fl_Menu_Item item = {app->stage->loc_description(j), 0, stage_cb, app, FL_MENU_RADIO}; 00993 stage_menuitems[j] = item; 00994 } 00995 stage_menuitems[menulen] = nullitem; 00996 display_menuitems[DM_STAGE].user_data(stage_menuitems); 00997 00998 menulen = app->display->num_stereo_modes(); 00999 stereo_menuitems_storage = new Fl_Menu_Item[menulen+2]; 01000 stereo_menuitems_storage[0] = nullitem; 01001 stereo_menuitems = stereo_menuitems_storage+1; 01002 for (j=0; j<menulen; j++) { 01003 Fl_Menu_Item item = {app->display->stereo_name(j), 0, stereo_cb, vmdapp, FL_MENU_RADIO}; 01004 stereo_menuitems[j] = item; 01005 } 01006 stereo_menuitems[menulen] = nullitem; 01007 display_menuitems[DM_STEREO].user_data(stereo_menuitems); 01008 01009 menulen = 2; 01010 stereoswap_menuitems_storage = new Fl_Menu_Item[menulen+2]; 01011 stereoswap_menuitems_storage[0] = nullitem; 01012 stereoswap_menuitems = stereoswap_menuitems_storage+1; 01013 for (j=0; j<menulen; j++) { 01014 const char * StereoSwap[] = { "Off", "On" }; 01015 Fl_Menu_Item item = {StereoSwap[j], 0, stereoswap_cb, vmdapp, FL_MENU_RADIO}; 01016 stereoswap_menuitems[j] = item; 01017 } 01018 stereoswap_menuitems[menulen] = nullitem; 01019 display_menuitems[DM_STEREOEYESWAP].user_data(stereoswap_menuitems); 01020 01021 #if !defined(VMDLEANGUI) 01022 menulen = app->display->num_cache_modes(); 01023 cachemode_menuitems_storage = new Fl_Menu_Item[menulen+2]; 01024 cachemode_menuitems_storage[0] = nullitem; 01025 cachemode_menuitems = cachemode_menuitems_storage+1; 01026 for (j=0; j<menulen; j++) { 01027 Fl_Menu_Item item = {app->display->cache_name(j), 0, cachemode_cb, vmdapp, FL_MENU_RADIO}; 01028 cachemode_menuitems[j] = item; 01029 } 01030 cachemode_menuitems[menulen] = nullitem; 01031 display_menuitems[DM_CACHEMODE].user_data(cachemode_menuitems); 01032 #endif 01033 01034 menulen = app->display->num_render_modes(); 01035 rendermode_menuitems_storage = new Fl_Menu_Item[menulen+2]; 01036 rendermode_menuitems_storage[0] = nullitem; 01037 rendermode_menuitems = rendermode_menuitems_storage+1; 01038 for (j=0; j<menulen; j++) { 01039 Fl_Menu_Item item = {app->display->render_name(j), 0, rendermode_cb, vmdapp, FL_MENU_RADIO}; 01040 rendermode_menuitems[j] = item; 01041 } 01042 rendermode_menuitems[menulen] = nullitem; 01043 display_menuitems[DM_RENDERMODE].user_data(rendermode_menuitems); 01044 01045 update_dispmode(); 01046 01047 menulen = sizeof(init_mouse_menuitems)/sizeof(Fl_Menu_Item); 01048 mouse_menuitems_storage = new Fl_Menu_Item[menulen+2]; 01049 mouse_menuitems_storage[0] = nullitem; 01050 mouse_menuitems = mouse_menuitems_storage+1; 01051 for (j=0; j<menulen; j++) 01052 mouse_menuitems[j] = init_mouse_menuitems[j]; 01053 01054 01055 // ******** CREATE MENU BAR ********* 01056 menubar = new Fl_Menu_Bar(0, 0, MAINFLTKMENUWIDTH, 30); 01057 #if defined(VMDMENU_WINDOW) 01058 menubar->color(VMDMENU_WINDOW); 01059 #endif 01060 menubar->add("File",0,0,(void *)file_menuitems,FL_SUBMENU_POINTER); 01061 menubar->add("Molecule",0,0,(void *)molecule_menuitems,FL_SUBMENU_POINTER); 01062 menubar->add("Graphics",0,0,(void *)graphics_menuitems, FL_SUBMENU_POINTER); 01063 menubar->add("Display",0,0,(void*)display_menuitems, FL_SUBMENU_POINTER); 01064 menubar->add("Mouse",0,0,(void *)mouse_menuitems, FL_SUBMENU_POINTER); 01065 menubar->add(EXT_MENU_NAME,0,0, NULL, FL_SUBMENU); 01066 menubar->add("Help",0,0,(void *)init_help_menuitems, FL_SUBMENU_POINTER); 01067 menubar->user_data(vmdapp); 01068 menubar->selection_color(VMDMENU_MENU_SEL); 01069 01070 // ******** CREATE CONTROLS ********* 01071 Fl_Group::current()->resizable(browser); 01072 01073 Fl_Button *b; 01074 int bwidth = 20, bheight = 20; 01075 b = new Fl_Button(0, 150, bwidth, bheight, "@4->|"); 01076 VMDFLTKTOOLTIP(b, "Jump to beginning") 01077 b->labeltype(FL_SYMBOL_LABEL); 01078 b->callback(start_cb, app); 01079 01080 reverse = new Fl_Button(0, 150+bheight, bwidth, bheight, "@<"); 01081 VMDFLTKTOOLTIP(reverse, "Play in reverse") 01082 reverse->labeltype(FL_SYMBOL_LABEL); 01083 reverse->type(FL_TOGGLE_BUTTON); 01084 reverse->callback(reverse_cb, app); 01085 01086 b = new Fl_Button(bwidth, 150+bheight, bwidth, bheight, "@<|"); 01087 VMDFLTKTOOLTIP(b, "Step in reverse") 01088 b->labeltype(FL_SYMBOL_LABEL); 01089 b->callback(prev_cb, app); 01090 01091 b = new Fl_Button(MAINFLTKMENUWIDTH-bwidth, 150, bwidth, bheight, "@->|"); 01092 VMDFLTKTOOLTIP(b, "Jump to end") 01093 b->labeltype(FL_SYMBOL_LABEL); 01094 b->callback(stop_cb, app); 01095 01096 forward = new Fl_Button(MAINFLTKMENUWIDTH-bwidth, 150+bheight, 01097 bwidth, bheight, "@>"); 01098 VMDFLTKTOOLTIP(forward, "Play forward") 01099 forward->labeltype(FL_SYMBOL_LABEL); 01100 forward->type(FL_TOGGLE_BUTTON); 01101 forward->callback(forward_cb, app); 01102 01103 b = new Fl_Button(MAINFLTKMENUWIDTH-2*bwidth, 150+bheight, 01104 bwidth, bheight, "@|>"); 01105 VMDFLTKTOOLTIP(b, "Step forward") 01106 b->labeltype(FL_SYMBOL_LABEL); 01107 b->callback(next_cb, app); 01108 01109 curframe = new Fl_Int_Input(bwidth, 150, 2*bwidth, bheight); 01110 VMDFLTKTOOLTIP(curframe, "Set current frame") 01111 curframe->textsize(12); 01112 curframe->callback(curframe_cb, app); 01113 curframe->when(FL_WHEN_ENTER_KEY); 01114 curframe->selection_color(VMDMENU_VALUE_SEL2); 01115 01116 frameslider = new Fl_Slider(3*bwidth, 150, 01117 MAINFLTKMENUWIDTH-4*bwidth, bheight); 01118 VMDFLTKTOOLTIP(frameslider, "Drag to set current frame") 01119 frameslider->type(FL_HOR_NICE_SLIDER); 01120 frameslider->step(1,1); 01121 frameslider->callback(frameslider_cb, this); 01122 frameslider->color(VMDMENU_SLIDER_BG, VMDMENU_SLIDER_FG); 01123 frameslider->when(FL_WHEN_CHANGED | FL_WHEN_RELEASE); 01124 01125 step = new Fl_Counter(220,150+bheight, 45,bheight, "step"); 01126 VMDFLTKTOOLTIP(step, "Animation step size") 01127 step->labelsize(12); 01128 step->type(FL_SIMPLE_COUNTER); 01129 step->step(1,1); 01130 step->minimum(1); 01131 step->value(1); 01132 step->callback(step_cb, app); 01133 step->align(FL_ALIGN_LEFT); 01134 01135 style = new Fl_Choice(120, 150+bheight, 65, bheight); 01136 VMDFLTKTOOLTIP(style, "Set animation looping mode") 01137 style->textsize(12); 01138 style->selection_color(VMDMENU_MENU_SEL); 01139 style->box(FL_THIN_UP_BOX); 01140 for (int s=0; s<Animation::ANIM_TOTAL_STYLES; s++) 01141 style->add(animationStyleName[s]); 01142 01143 // XXX The Animation class starts with ANIM_LOOP as its style, so that's 01144 // what we do, too. 01145 style->value(1); 01146 style->callback(style_cb, app); 01147 01148 zoom = new Fl_Check_Button(80, 150+bheight-2, bwidth+5, bheight+5, "zoom"); 01149 VMDFLTKTOOLTIP(zoom, "Zoom in slider onto 100-frame subrange centered on current frame") 01150 zoom->labelsize(12); 01151 zoom->align(FL_ALIGN_LEFT); 01152 zoom->value(0); 01153 //zoom->selection_color(FL_RED); 01154 zoom->color(VMDMENU_CHECKBOX_BG, VMDMENU_CHECKBOX_FG); 01155 zoom->callback(zoom_cb, this); 01156 01157 speed = new Fl_Slider(315, 150+bheight, 90, bheight, "speed"); 01158 VMDFLTKTOOLTIP(speed, "Drag slider to change animation speed") 01159 speed->labelsize(12); 01160 speed->type(FL_HORIZONTAL); 01161 speed->color(VMDMENU_SLIDER_BG, VMDMENU_SLIDER_FG); 01162 speed->value(1.0); 01163 speed->callback(speed_cb, app); 01164 speed->align(FL_ALIGN_LEFT); 01165 01166 guistate = UNDEFINED; 01167 update_gui_state(); 01168 01169 callback(vmd_main_window_cb); // override default FLTK/VMD global handlers 01170 01171 Fl_Window::end(); 01172 } 01173 01174 int MainFltkMenu::act_on_command(int type, Command *cmd) { 01175 if (type == Command::MOL_NEW) { 01176 // XXX force set of anim style to the current GUI setting 01177 // when new molecules are loaded, since they get the default otherwise 01178 app->animation_set_style(style->value()); 01179 } 01180 01181 if (type == Command::MOL_ACTIVE || 01182 type == Command::MOL_ON || 01183 type == Command::MOL_FIX || 01184 type == Command::MOL_NEW || 01185 type == Command::MOL_RENAME || 01186 type == Command::MOL_VOLUME || 01187 type == Command::ANIM_NEW_NUM_FRAMES || 01188 type == Command::MOL_DEL || 01189 type == Command::MOL_TOP 01190 ) { 01191 browser->update(); 01192 } 01193 01194 if (type == Command::MOL_TOP || 01195 type == Command::MOL_DEL || // XXX ought to emit a MOL_TOP too, IMHO 01196 type == Command::MOL_NEW || 01197 type == Command::MOL_VOLUME || 01198 type == Command::ANIM_JUMP || 01199 type == Command::ANIM_NEW_NUM_FRAMES || 01200 type == Command::ANIM_NEW_FRAME) { 01201 int id = app->molecule_top(); 01202 int frame = app->molecule_frame(id); 01203 if (type != Command::ANIM_NEW_FRAME) { 01204 int max = app->molecule_numframes(id); 01205 frameslider->range(0, max-1); 01206 } 01207 frameslider->value(frame); 01208 char buf[20]; 01209 sprintf(buf, "%d", frame); 01210 curframe->value(buf); 01211 if (type == Command::ANIM_JUMP) { 01212 forward->value(0); 01213 reverse->value(0); 01214 } 01215 } else if (type == Command::MOUSE_MODE) { 01216 update_mousemode(cmd); 01217 } else if (type == Command::DISP_DEPTHCUE || type == Command::DISP_CULLING 01218 || type == Command::DISP_ANTIALIAS || type == Command::DISP_FPS 01219 || type == Command::DISP_LIGHT_ON || type == Command::CMD_STAGE 01220 || type == Command::CMD_AXES || type == Command::DISP_PROJ 01221 || type == Command::DISP_BACKGROUNDGRADIENT 01222 || type == Command::DISP_STEREO || type == Command::DISP_STEREOSWAP 01223 || type == Command::DISP_CACHEMODE 01224 || type == Command::DISP_RENDERMODE) { 01225 update_dispmode(); 01226 } else if (type == Command::MENU_TK_ADD) { 01227 char *shortpath = ((CmdMenuExtensionAdd *)cmd)->menupath; 01228 char *longpath = new char[strlen(EXT_MENU_NAME)+strlen(shortpath)+2]; 01229 sprintf(longpath, "%s/%s",EXT_MENU_NAME,((CmdMenuExtensionAdd *)cmd)->menupath); 01230 char *menuname = stringdup(((CmdMenuExtensionAdd *)cmd)->name); 01231 menubar->add(longpath, 0, menu_cb, menuname); 01232 delete[] longpath; 01233 } else if (type == Command::MENU_TK_REMOVE) { 01234 const Fl_Menu_Item *menubase = menubar->menu(); 01235 int remove_menu_index = 0; 01236 int m; 01237 01238 for (m=0; m<menubase->size(); m++) 01239 if (!strcmp(menubase[m].label(), EXT_MENU_NAME)) break; 01240 const Fl_Menu_Item *extmenu = menubase+m; 01241 for (m=1; m<extmenu[1].size(); m++) 01242 if (extmenu[m].user_data() && !strcmp((char*)extmenu[m].user_data(), ((CmdMenuExtensionRemove*)cmd)->name)) { 01243 remove_menu_index = extmenu-menubase+m; 01244 break; 01245 } 01246 if (remove_menu_index) menubar->remove(remove_menu_index); 01247 } else if (type == Command::ANIM_STYLE) { 01248 style->value((int)((CmdAnimStyle *)cmd)->newStyle); 01249 } else if (type == Command::ANIM_SKIP) { 01250 step->value(((CmdAnimSkip *)cmd)->newSkip); 01251 } else if (type == Command::ANIM_SPEED) { 01252 // XXX should put some kind of scaling in here to improve the dynamic 01253 // range of the slider. Also put the inverse scaling in speed_cb. 01254 double val = ((CmdAnimSpeed *)cmd)->newSpeed; 01255 speed->value(val); 01256 } else if (type == Command::ANIM_DIRECTION) { 01257 Animation::AnimDir newDir = ((CmdAnimDir *)cmd)->newDir; 01258 forward->value(newDir == Animation::ANIM_FORWARD); 01259 reverse->value(newDir == Animation::ANIM_REVERSE); 01260 } else { 01261 return TRUE; 01262 } 01263 01264 return FALSE; 01265 } 01266 01267 MainFltkMenu::~MainFltkMenu() { 01268 delete[] file_menuitems; 01269 delete[] molecule_menuitems; 01270 delete[] display_menuitems; 01271 delete[] axes_menuitems_storage; 01272 delete[] backgroundmode_menuitems_storage; 01273 delete[] stage_menuitems_storage; 01274 delete[] stereo_menuitems_storage; 01275 delete[] stereoswap_menuitems_storage; 01276 #if !defined(VMDLEANGUI) 01277 delete[] cachemode_menuitems_storage; 01278 #endif 01279 delete[] rendermode_menuitems_storage; 01280 delete[] mouse_menuitems_storage; 01281 delete[] browserpopup_menuitems; 01282 } 01283 01284 int MainFltkMenu::get_selected_molecule() { 01285 for (int j=0; j<browser->size(); j++) 01286 if (browser->selected(j+1)) 01287 return j; 01288 01289 return -1; 01290 } 01291 01293 void MainFltkMenu::update_menu_state(Fl_Menu_Item* mymenuitems, const MenuBehavior* mymenu_behavior) { 01294 int j; 01295 01296 switch (guistate) { 01297 case MANY_SELECTED_MOL: 01298 for (j=0; mymenuitems[j].label(); j++) { 01299 if (mymenu_behavior[j] == MENU_NEED_UNIQUE_SEL) mymenuitems[j].deactivate(); 01300 else mymenuitems[j].activate(); 01301 } 01302 break; 01303 case ONE_SELECTED_MOL: 01304 for (j=0; mymenuitems[j].label(); j++) 01305 mymenuitems[j].activate(); 01306 break; 01307 case NO_SELECTED_MOL: 01308 for (j=0; mymenuitems[j].label(); j++) { 01309 if (mymenu_behavior[j] & MENU_NEED_SEL) mymenuitems[j].deactivate(); 01310 else mymenuitems[j].activate(); 01311 } 01312 break; 01313 case UNDEFINED: //gets rid of g++ compiler warning 01314 break; 01315 } 01316 } 01317 01318 01319 void MainFltkMenu::update_gui_state() { 01320 char has_selected_mol = 0; 01321 int old_guistate = guistate; 01322 01323 for (int item=1; item<=browser->size(); item++) { 01324 if (browser->selected(item)) { 01325 has_selected_mol++; 01326 if (has_selected_mol >= 2) break; 01327 } 01328 } 01329 01330 if (has_selected_mol == 2) guistate = MANY_SELECTED_MOL; 01331 else if (has_selected_mol == 1) guistate = ONE_SELECTED_MOL; 01332 else if (!has_selected_mol) guistate = NO_SELECTED_MOL; 01333 01334 // (de)activate the Molecule menu items 01335 if (old_guistate != guistate) { 01336 update_menu_state(file_menuitems, file_menu_behavior); 01337 update_menu_state(molecule_menuitems, molecule_menu_behavior); 01338 update_menu_state(browserpopup_menuitems, browserpopup_menu_behavior); 01339 } 01340 01341 } 01342 01343 01344 01345 void MainFltkMenu::draw() { 01346 #if defined(ARCH_MACOSX) || defined(ARCH_MACOSXX86) || defined(ARCH_MACOSXX86_64) 01347 size(MAINFLTKMENUWIDTH, h()); 01348 #endif 01349 Fl_Window::draw(); 01350 } 01351