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 00009 /*************************************************************************** 00010 * RCS INFORMATION: 00011 * 00012 * $RCSfile: VMDFltkMenu.C,v $ 00013 * $Author: johns $ $Locker: $ $State: Exp $ 00014 * $Revision: 1.29 $ $Date: 2019年01月17日 21:21:02 $ 00015 * 00016 *************************************************************************** 00017 * DESCRIPTION: 00018 * Class to manage FLTK menus within VMD. 00019 ***************************************************************************/ 00020 00021 #include <stdio.h> 00022 #include <stdlib.h> 00023 00024 #include <FL/Fl.H> 00025 #include <FL/Fl_Choice.H> 00026 #include "VMDFltkMenu.h" 00027 #include "VMDApp.h" 00028 #include "utilities.h" 00029 00030 void VMDFltkMenu::window_cb(Fl_Widget *w, void *) { 00031 VMDFltkMenu *m = (VMDFltkMenu *)w; 00032 m->app->menu_show(m->get_name(), 0); 00033 } 00034 00035 00036 VMDFltkMenu::VMDFltkMenu(const char *menuname,const char *title,VMDApp *vmdapp) 00037 : VMDMenu(menuname, vmdapp), Fl_Window(0,0,NULL) { 00038 _title=stringdup(title); 00039 Fl_Window::label(_title); 00040 #if defined(VMDMENU_WINDOW) 00041 Fl_Window::color(VMDMENU_WINDOW); 00042 #endif 00043 callback(window_cb); 00044 } 00045 00046 00047 VMDFltkMenu::~VMDFltkMenu() { 00048 delete [] _title; 00049 } 00050 00051 00052 void VMDFltkMenu::do_on() { 00053 Fl_Window::show(); 00054 } 00055 00056 00057 void VMDFltkMenu::do_off() { 00058 Fl_Window::hide(); 00059 } 00060 00061 00062 void VMDFltkMenu::move(int x, int y) { 00063 Fl_Widget::position(x,y); 00064 } 00065 00066 00067 void VMDFltkMenu::where(int &x, int &y) { 00068 x = Fl_Widget::x(); 00069 y = Fl_Widget::y(); 00070 } 00071 00072 00073 void fill_fltk_molchooser(Fl_Choice *choice, VMDApp *app, 00074 const char *extramenu) { 00075 int nummols = app->num_molecules(); 00076 int has_extra = (extramenu == NULL) ? 0 : 1; 00077 int need_full_regen = 0; 00078 char buf[1024]; 00079 00080 // compute number of items in the menu, not counting the ending NULL marker. 00081 int menusz = choice->size() - 1; 00082 00083 #if 1 00084 // Optimize the cases where the chooser must be regenerated: 00085 // If the chooser is empty, we need to regenerate. 00086 // If the chooser is larger than needed, we have to 00087 // re-create it from scratch since some molecule was 00088 // potentially deleted from the middle. 00089 // If the size remains fixed, then we need to regenerate 00090 // because we are getting called to rename an existing molecule or 00091 // to change its state. 00092 // If the size grows by exactly one, then we can just add the new 00093 // molecule, and we don't need to regen, otherwise we have to regen. 00094 if ((menusz <= has_extra) || 00095 (menusz >= (nummols + has_extra)) || 00096 ((nummols + has_extra) - menusz) > 1) { 00097 need_full_regen = 1; 00098 // printf("full regen: msz: %d sz: %d N: %d extra: %d\n", 00099 // choice->size(), menusz, nummols, has_extra); 00100 } else { 00101 // printf("add-newmol: msz: %d sz: %d N: %d extra: %d\n", 00102 // choice->size(), menusz, nummols, has_extra); 00103 } 00104 #else 00105 need_full_regen = 1; 00106 #endif 00107 00108 // either update (add an item) or completely regenerate the chooser contents 00109 if (need_full_regen) { 00110 choice->clear(); 00111 00112 if (has_extra) 00113 choice->add(extramenu); 00114 00115 for (int i=0; i<nummols; i++) { 00116 int id = app->molecule_id(i); 00117 const char *s = app->molecule_name(id); 00118 00119 // Truncate molecule name to first 25 chars... 00120 // We must ensure we never find menu items by name, or that we 00121 // truncate the search string identically to the way we do it here. 00122 sprintf(buf, "%d: %-.25s%s", id, s, 00123 app->molecule_is_displayed(id) ? "" : " (off)"); 00124 00125 // Fltk doesn't allow adding a menu item with the same name as 00126 // an existing item, so we use replace, which also avoids 00127 // problems with the escape characters interpreted by add() 00128 int ind = choice->add("foobar"); 00129 choice->replace(ind, buf); 00130 } 00131 } else { 00132 // we just need to add one new molecule... 00133 int i = nummols - 1; 00134 int id = app->molecule_id(i); 00135 const char *s = app->molecule_name(id); 00136 00137 // Truncate molecule name to first 25 chars... 00138 // We must ensure we never find menu items by name, or that we 00139 // truncate the search string identically to the way we do it here. 00140 sprintf(buf, "%d: %-.25s%s", id, s, 00141 app->molecule_is_displayed(id) ? "" : " (off)"); 00142 00143 // Fltk doesn't allow adding a menu item with the same name as 00144 // an existing item, so we use replace, which also avoids 00145 // problems with the escape characters interpreted by add() 00146 int ind = choice->add("foobar"); 00147 choice->replace(ind, buf); 00148 } 00149 } 00150 00151 00152 char * escape_fltk_menustring(const char * menustring) { 00153 char * newstr; 00154 int len = strlen(menustring); 00155 int i, j; 00156 00157 // don't bother being precise, these are just menu strings, and they're 00158 // going to be freed immediately, so allocate largest possible memory block 00159 // we'll ever need (every char being escape) and avoid running through the 00160 // string twice to accurately count the number of escaped characters. 00161 newstr = (char *) malloc(((len * 2) + 1) * sizeof(char)); 00162 if (newstr == NULL) 00163 return NULL; 00164 00165 i=0; 00166 j=0; 00167 while (menustring[i] != '0円') { 00168 // insert an escape character if necessary 00169 if (menustring[i] == '/' || 00170 menustring[i] == '\\' || 00171 menustring[i] == '_') { 00172 newstr[j] = '\\'; 00173 j++; 00174 } else if (menustring[i] == '&') { 00175 // FLTK won't escape '&' characters for some reason, so I skip 'em 00176 i++; 00177 continue; 00178 } 00179 00180 newstr[j] = menustring[i]; 00181 i++; 00182 j++; 00183 } 00184 newstr[j] = '0円'; // null terminate the string 00185 00186 return newstr; 00187 } 00188 00189 00190 // Find the menu index with a name that matches the string. 00191 // Only checks leaf node menu names, not full pathnames currently 00192 static int find_menu_from_string(const char *namestr, const Fl_Menu *m) { 00193 // don't crash and burn on a NULL name or menu object 00194 if (namestr == NULL || m == NULL) 00195 return -1; 00196 00197 // FLTK 1.1.7 XXX should do the same use the built-in 00198 // find_item() or item_pathname() routines to do the same work we do below 00199 00200 // FLTK 1.1.4 -- only checks leaf node menu names, ignores full pathname 00201 // find leaf menu name from full menu path 00202 const char *nstr; 00203 if ((nstr = strrchr(namestr, '/')) == NULL) 00204 nstr = namestr; 00205 else 00206 nstr++; 00207 00208 int i, val; 00209 for (val=-1, i=0; i<m->size()-1; i++) { 00210 const char *mstr = m[i].text; 00211 // compare leaf submenu item name against left color mode name 00212 if ((mstr != NULL) && (!strcmp(nstr, mstr))) { 00213 val=i; 00214 break; 00215 } 00216 } 00217 00218 return val; 00219 } 00220 00221 00222 // Set the chooser to the menu name matching the given string. 00223 // Only checks the leaf node menu names, not full pathnames currently 00224 void set_chooser_from_string(const char *namestr, class Fl_Choice *chooser) { 00225 int m = find_menu_from_string(namestr, chooser->menu()); 00226 00227 // don't set the menu if we can't find the string 00228 if (m >= 0) 00229 chooser->value(m); 00230 } 00231 00232 00233