diff --git a/CHANGES b/CHANGES index cc402681f35793b3d306f1fd301e78ba4db5be68..b91781f95adea4e3585b5e4fa3d6fa6c0a819835 100644 --- a/CHANGES +++ b/CHANGES @@ -157,6 +157,8 @@ CHANGES IN FLTK 1.3.0 - hide() and show() methods are now virtual from Fl_Widget, was only virtual since Fl_Window derived classes before. So now widget->hide() will work if widget is a window. + - New widgets: Fl_Tree, Fl_Table, Fl_Native_File_Chooser + - added Fl_Menu_ methods: insert(), find_index(), clear_submenu() CHANGES IN FLTK 1.1.9 diff --git a/FL/Fl_Menu_.H b/FL/Fl_Menu_.H index 5daec157d7b10c8e01815aae2e221a3086db5dce..8f3b64449ee55eabfb413d1bd3943e633ae0c485 100644 --- a/FL/Fl_Menu_.H +++ b/FL/Fl_Menu_.H @@ -66,6 +66,9 @@ public: const Fl_Menu_Item* picked(const Fl_Menu_Item*); const Fl_Menu_Item* find_item(const char *name); const Fl_Menu_Item* find_item(Fl_Callback*); + int find_index(const char *name) const; + int find_index(const Fl_Menu_Item *item) const; + int find_index(Fl_Callback *cb) const; const Fl_Menu_Item* test_shortcut() {return picked(menu()->test_shortcut());} void global(); @@ -77,14 +80,21 @@ public: const Fl_Menu_Item *menu() const {return menu_;} void menu(const Fl_Menu_Item *m); void copy(const Fl_Menu_Item *m, void* user_data = 0); + int insert(int index, const char*, int shortcut, Fl_Callback*, void* = 0, int = 0); int add(const char*, int shortcut, Fl_Callback*, void* = 0, int = 0); - /** See int Fl_Menu_::add(const char* label, int shortcut, Fl_Callback*, void *user_data=0, int flags=0)*/ + /** See int Fl_Menu_::add(const char* label, int shortcut, Fl_Callback*, void *user_data=0, int flags=0) */ int add(const char* a, const char* b, Fl_Callback* c, void* d = 0, int e = 0) { - return add(a,fl_old_shortcut(b),c,d,e);} + return add(a,fl_old_shortcut(b),c,d,e); + } + /** See int Fl_Menu_::insert(const char* label, int shortcut, Fl_Callback*, void *user_data=0, int flags=0) */ + int insert(int index, const char* a, const char* b, Fl_Callback* c, void* d = 0, int e = 0) { + return insert(index,a,fl_old_shortcut(b),c,d,e); + } int add(const char *); int size() const ; void size(int W, int H) { Fl_Widget::size(W, H); } void clear(); + int clear_submenu(int index); void replace(int,const char *); void remove(int); /** Changes the shortcut of item i to n. */ diff --git a/FL/Fl_Menu_Item.H b/FL/Fl_Menu_Item.H index 0ea5c792fbdc555c9555d0e545d736bee98d5de6..31620e16fbac03ffb823c301b197e275f264744c 100644 --- a/FL/Fl_Menu_Item.H +++ b/FL/Fl_Menu_Item.H @@ -386,6 +386,7 @@ struct FL_EXPORT Fl_Menu_Item { /** back compatibility only \deprecated. */ void uncheck() {flags &= ~FL_MENU_VALUE;} + int insert(int,const char*,int,Fl_Callback*,void* =0, int =0); int add(const char*, int shortcut, Fl_Callback*, void* =0, int = 0); /** See int add(const char*, int shortcut, Fl_Callback*, void*, int) */ diff --git a/src/Fl_Menu_.cxx b/src/Fl_Menu_.cxx index ce417bfa9ea6d89bc40ef92321e79974ec754df2..2e3405885f3018228be7c9475ba251f9d0989783 100644 --- a/src/Fl_Menu_.cxx +++ b/src/Fl_Menu_.cxx @@ -100,11 +100,13 @@ int Fl_Menu_::item_pathname(char *name, int namelen, const Fl_Menu_Item *findite } /** - Find menu item index, given a menu pathname such as "Edit/Copy". + Find the menu item for a given menu \p pathname, such as "Edit/Copy". - This method finds a menu item in a menu array, also traversing submenus, but + This method finds a menu item in the menu array, also traversing submenus, but not submenu pointers. + To get the menu item's index, use find_index(const char*) + \b Example: \code Fl_Menu_Bar *menubar = new Fl_Menu_Bar(..); @@ -120,25 +122,84 @@ int Fl_Menu_::item_pathname(char *name, int namelen, const Fl_Menu_Item *findite item->labelcolor(FL_GREEN); } \endcode - \returns The item found, or NULL if not found. - \see - \param name path and name of the menu item - \return NULL if not found - \see Fl_Menu_::find_item(Fl_Callback*), item_pathname() + \param pathname The path and name of the menu item + \returns The item found, or NULL if not found + \see find_index(const char*), find_item(Fl_Callback*), item_pathname() */ -const Fl_Menu_Item * Fl_Menu_::find_item(const char *name) { - char menupath[1024] = ""; // File/Export +const Fl_Menu_Item * Fl_Menu_::find_item(const char *pathname) { + int i = find_index(pathname); + return( (i==-1) ? 0 : (const Fl_Menu_Item*)(menu_+i)); +} +/** + Find the index the menu array for given \p item. + + A way to convert a menu item pointer into an index. + + Current implementation is fast and not expensive. + + \code + // Convert an index-to-item + int index = 12; + const Fl_Menu_Item *item = mymenu->menu() + index; + + // Convert an item-to-index + int index = mymenu->find_index(item); + if ( index == -1 ) { ..error.. } + \endcode + + \param item The *item to be found + \returns The index of the item, or -1 if not found. + \see menu() +*/ +int Fl_Menu_::find_index(const Fl_Menu_Item *item) const { + Fl_Menu_Item *max = menu_+size(); + if (item<menu_ || item>=max) return(-1); + return(item-menu_); +} + +/** + Find the index into the menu array for a given callback \p cb. + + This method finds a menu item's index position, also traversing submenus, but + not submenu pointers. This is useful if an application uses internationalisation + and a menu item can not be found using its label. This search is also much faster. + + \param cb Find the first item with this callback + \returns The index of the item with the specific callback, or -1 if not found + \see find_index(const char*) + */ +int Fl_Menu_::find_index(Fl_Callback *cb) const { + for ( int t=0; t < size(); t++ ) + if (menu_[t].callback_==cb) + return(t); + return(-1); +} + +/** + Find the menu item index for a given menu \p pathname, such as "Edit/Copy". + + This method finds a menu item's index position for the given menu pathname, + also traversing submenus, but not submenu pointers. + + To get the menu item pointer for a pathname, use find_item() + + \param pathname The path and name of the menu item index to find + \returns The index of the matching item, or -1 if not found. + \see item_pathname() + +*/ +int Fl_Menu_::find_index(const char *pathname) const { + char menupath[1024] = ""; // File/Export for ( int t=0; t < size(); t++ ) { Fl_Menu_Item *m = menu_ + t; - if (m->flags&FL_SUBMENU) { // IT'S A SUBMENU // we do not support searches through FL_SUBMENU_POINTER links if (menupath[0]) strlcat(menupath, "/", sizeof(menupath)); strlcat(menupath, m->label(), sizeof(menupath)); - if (!strcmp(menupath, name)) return m; + if (!strcmp(menupath, pathname)) return(t); } else { if (!m->label()) { // END OF SUBMENU? Pop back one level. @@ -147,21 +208,19 @@ const Fl_Menu_Item * Fl_Menu_::find_item(const char *name) { else menupath[0] = '\0'; continue; } - // IT'S A MENU ITEM char itempath[1024]; // eg. Edit/Copy strcpy(itempath, menupath); if (itempath[0]) strlcat(itempath, "/", sizeof(itempath)); strlcat(itempath, m->label(), sizeof(itempath)); - if (!strcmp(itempath, name)) return m; + if (!strcmp(itempath, pathname)) return(t); } } - - return (const Fl_Menu_Item *)0; + return(-1); } /** - Find menu item index given a callback. + Find the menu item for the given callback \p cb. This method finds a menu item in a menu array, also traversing submenus, but not submenu pointers. This is useful if an application uses @@ -169,8 +228,8 @@ const Fl_Menu_Item * Fl_Menu_::find_item(const char *name) { search is also much faster. \param cb find the first item with this callback - \return NULL if not found - \see Fl_Menu_::find_item(const char*) + \returns The item found, or NULL if not found + \see find_item(const char*) */ const Fl_Menu_Item * Fl_Menu_::find_item(Fl_Callback *cb) { for ( int t=0; t < size(); t++ ) { @@ -286,7 +345,7 @@ void Fl_Menu_::menu(const Fl_Menu_Item* m) { /** Sets the menu array pointer with a copy of m that will be automatically deleted. - If ud is not NULL, then all user data pointers are changed in the menus as well. + If userdata \p ud is not NULL, then all user data pointers are changed in the menus as well. See void Fl_Menu_::menu(const Fl_Menu_Item* m). */ void Fl_Menu_::copy(const Fl_Menu_Item* m, void* ud) { @@ -330,6 +389,47 @@ void Fl_Menu_::clear() { } } +/** + Clears the specified submenu pointed to by \p index of all menu items. + + This method is useful for clearing a submenu so that it can be + re-populated with new items. Example: a "File/Recent Files/..." submenu + that shows the last few files that have been opened. + + The specified \p index must point to a submenu. + + The submenu is cleared with remove(). + If the menu array was directly set with menu(x), then copy() + is done to make a private array. + + \warning Since this method can change the internal menu array, any menu + item pointers or indecies the application may have cached can become + stale, and should be recalculated/refreshed. + + \b Example: + \code + int index = menubar->find_index("File/Recent"); // get index of "File/Recent" submenu + if ( index != -1 ) menubar->clear_submenu(index); // clear the submenu + menubar->add("File/Recent/Aaa"); + menubar->add("File/Recent/Bbb"); + [..] + \endcode + + \param index The index of the submenu to be cleared + \returns 0 on success, -1 if the index is out of range or not a submenu + \see remove(int) + */ +int Fl_Menu_::clear_submenu(int index) { + if ( index < 0 || index >= size() ) return(-1); + if ( ! (menu_[index].flags & FL_SUBMENU) ) return(-1); + ++index; // advance to first item in submenu + while ( index < size() ) { // keep remove()ing top item until end is reached + if ( menu_[index].text == 0 ) break; // end of this submenu? done + remove(index); // remove items/submenus + } + return(0); +} + // // End of "$Id$". // diff --git a/src/Fl_Menu_add.cxx b/src/Fl_Menu_add.cxx index 96f4a443c11c3ea241dd06fdaeb010dee42fa829..036fc2d0de6f5265cf00489051751ceaccfcca74 100644 --- a/src/Fl_Menu_add.cxx +++ b/src/Fl_Menu_add.cxx @@ -53,11 +53,12 @@ extern Fl_Menu_* fl_menu_array_owner; // in Fl_Menu_.cxx // Insert a single Fl_Menu_Item into an array of size at offset n, // if this is local_array it will be reallocated if needed. -static Fl_Menu_Item* insert( - Fl_Menu_Item* array, int size, - int n, - const char *text, - int flags +static Fl_Menu_Item* array_insert( + Fl_Menu_Item* array, // array to modify + int size, // size of array + int n, // index of new insert position + const char *text, // text of new item (copy is made) + int flags // flags for new item ) { if (array == local_array && size >= local_array_alloc) { local_array_alloc = 2*size; @@ -106,6 +107,27 @@ int Fl_Menu_Item::add( Fl_Callback *cb, void *data, int myflags +) { + return(insert(-1,mytext,sc,cb,data,myflags)); // -1: append +} + +/** Inserts an item at position \p index. + + If \p index is -1, the item is added the same way as Fl_Menu_Item::add(). + + If 'mytext' contains any un-escaped front slashes (/), it's assumed + a menu pathname is being specified, and the value of \p index + will be ignored. + + In all other aspects, the behavior of insert() is the same as add(). +*/ +int Fl_Menu_Item::insert( + int index, + const char *mytext, + int sc, + Fl_Callback *cb, + void *data, + int myflags ) { Fl_Menu_Item *array = this; Fl_Menu_Item *m = this; @@ -133,17 +155,18 @@ int Fl_Menu_Item::add( item = buf; if (*p != '/') break; /* not a menu title */ - mytext = p+1; /* point at item title */ + index = -1; /* any submenu specified overrides insert position */ + mytext = p+1; /* point at item title */ /* find a matching menu title: */ for (; m->text; m = m->next()) if (m->flags&FL_SUBMENU && !compare(item, m->text)) break; if (!m->text) { /* create a new menu */ - int n = m-array; - array = insert(array, msize, n, item, FL_SUBMENU|flags1); + int n = (index==-1) ? m-array : index; + array = array_insert(array, msize, n, item, FL_SUBMENU|flags1); msize++; - array = insert(array, msize, n+1, 0, 0); + array = array_insert(array, msize, n+1, 0, 0); msize++; m = array+n; } @@ -156,11 +179,11 @@ int Fl_Menu_Item::add( if (!(m->flags&FL_SUBMENU) && !compare(m->text,item)) break; if (!m->text) { /* add a new menu item */ - int n = m-array; - array = insert(array, msize, n, item, myflags|flags1); + int n = (index==-1) ? m-array : index; + array = array_insert(array, msize, n, item, myflags|flags1); msize++; if (myflags & FL_SUBMENU) { // add submenu delimiter - array = insert(array, msize, n+1, 0, 0); + array = array_insert(array, msize, n+1, 0, 0); msize++; } m = array+n; @@ -179,17 +202,21 @@ int Fl_Menu_Item::add( /** Adds a new menu item. - \param[in] label The text label for the menu item. + \param[in] label The text label for the menu item. \param[in] shortcut Optional keyboard shortcut that can be an int or string; (FL_CTRL+'a') or "^a". Default 0 if none. \param[in] callback Optional callback invoked when user clicks the item. Default 0 if none. \param[in] userdata Optional user data passed as an argument to the callback. Default 0 if none. - \param[in] flags Optional flags that control the type of menu item; see below. Default is 0 for none. - \returns The index into the menu() array, where the entry was added. + \param[in] flags Optional flags that control the type of menu item; see below. Default is 0 for none. + \returns The index into the menu() array, where the entry was added. \par Description If the menu array was directly set with menu(x), then copy() is done to make a private array. \par + Since this method can change the internal menu array, any menu item + pointers or indecies the application may have cached can become stale, + and should be recalculated/refreshed. + \par A menu item's callback must not add() items to its parent menu during the callback. <B>Detailed Description of Parameters</B> @@ -219,23 +246,32 @@ int Fl_Menu_Item::add( \par This parameter is optional, and defaults to 0 to indicate no shortcut. \par - Shortcut can be 0L, or either a modifier/key combination (for example - FL_CTRL+'A') or a string describing the shortcut in one of two ways: + The shortcut can either be a raw integer value (eg. FL_CTRL+'A') + or a string (eg. "^c" or "^97"). + \par + Raw integer shortcuts can be a combination of keyboard chars (eg. 'A') + and optional keyboard modifiers (see Fl::event_state(), e.g. FL_SHIFT, etc). + \par + String shortcuts can be specified in one of two ways: + \par \verbatim [#+^]<ascii_value> e.g. "97", "^97", "+97", "#97" [#+^]<ascii_char> e.g. "a", "^a", "+a", "#a" \endverbatim + \par ..where \<ascii_value\> is a decimal value representing an - ascii character (eg. 97 is the ascii for 'a'), and the optional + ascii character (eg. 97 is the ascii code for 'a'), and the optional prefixes enhance the value that follows. Multiple prefixes must appear in the above order. + \par \verbatim # - Alt + - Shift ^ - Control \endverbatim - Text shortcuts are converted to integer shortcut by calling - unsigned int fl_old_shortcut(const char*). + \par + Internally, the text shortcuts are converted to integer values using + fl_old_shortcut(const char*). \par callback The callback to invoke when this menu item is selected. @@ -264,8 +300,48 @@ int Fl_Menu_Item::add( FL_MENU_DIVIDER // Creates divider line below this item. Also ends a group of radio buttons. \endcode + \todo Raw integer shortcut needs examples. + Dependent on responses to http://fltk.org/newsgroups.php?gfltk.development+v:10086 and results of STR#2344 */ int Fl_Menu_::add(const char *label,int shortcut,Fl_Callback *callback,void *userdata,int flags) { + return(insert(-1,label,shortcut,callback,userdata,flags)); // -1: append +} + +/** + Inserts a new menu item at the specified \p index position. + + If \p index is -1, the menu item is appended; same behavior as add(). + + The value of \p index will be ignored if \p label contains un-escaped + front-slashes (/) indicating a menu pathname as the point of insertion. + A menu pathname will override the value of \p index. + + For more details, see add(). Except for the \p index parameter, add() + has more detailed information on parameters and behavior, and is + functionally equivalent, + + \param[in] index The menu array's index position where the new item + is inserted. If -1, behavior is the same as add(). + \param[in] label The text label for the menu item. + \param[in] shortcut Optional keyboard shortcut. Can be an int (FL_CTRL+'a') + or a string ("^a"). Default is 0. + \param[in] callback Optional callback invoked when user clicks the item. + Default 0 if none. + \param[in] userdata Optional user data passed as an argument to the callback. + Default 0 if none. + \param[in] flags Optional flags that control the type of menu item; + see add() for more info. Default is 0 for none. + \returns The index into the menu() array, where the entry was added. + + */ +int Fl_Menu_::insert( + int index, + const char *label, + int shortcut, + Fl_Callback *callback, + void *userdata, + int flags +) { // make this widget own the local array: if (this != fl_menu_array_owner) { if (fl_menu_array_owner) { @@ -299,7 +375,7 @@ int Fl_Menu_::add(const char *label,int shortcut,Fl_Callback *callback,void *use } fl_menu_array_owner = this; } - int r = menu_->add(label,shortcut,callback,userdata,flags); + int r = menu_->insert(index,label,shortcut,callback,userdata,flags); // if it rellocated array we must fix the pointer: int value_offset = value_-menu_; menu_ = local_array; // in case it reallocated it