summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Melcher <github@matthiasm.com>2025-11-22 01:06:21 +0100
committerMatthias Melcher <github@matthiasm.com>2025-11-22 01:06:21 +0100
commit2049fe36370e50ed6a4a147942131e18f6a8533b (patch)
treef3afd2265abbe6ad2fb19bda77b28a54fc312543
parent56955a33a30662ce2dd621228abb9089c0a814fb (diff)
Declutter draw and handle methods.
Refactoring long methods into smaller parts that are easier to understand.
-rw-r--r--src/Fl_Menu.cxx499
1 files changed, 303 insertions, 196 deletions
diff --git a/src/Fl_Menu.cxx b/src/Fl_Menu.cxx
index eea95d7bc..1703f8141 100644
--- a/src/Fl_Menu.cxx
+++ b/src/Fl_Menu.cxx
@@ -126,7 +126,25 @@ struct Menu_State
bool next_item(menu_index_t menu);
// handle FL_SHORTCUT in any of the menu windows
- bool handle_shortcut();
+ int handle_shortcut();
+
+ // move menu item selection left
+ int handle_left();
+
+ // move menu item selection right
+ int handle_right();
+
+ // handle activation of the selected menu item
+ int handle_select();
+
+ // handle menu cancellation
+ int handle_cancel();
+
+ // handle any keyboard event from the menu windows
+ int handle_keyboard_event();
+
+ // handle all mouse events from the menu windows
+ int handle_mouse_events(int e);
};
// Global state of menu windows and popup windows.
@@ -220,6 +238,15 @@ class Menu_Window : public Menu_Window_Basetype
// Draw a single menu item in this window
void draw_entry(const Fl_Menu_Item*, int i, int erase);
+ // Draw the submenu arrow
+ void draw_submenu_arrow(const Fl_Rect& bbox);
+
+ // Draw the benu item shortcut text.
+ void draw_shortcut(const Fl_Rect& bbox, const Fl_Menu_Item* mi);
+
+ // Draw the menu item divider.
+ void draw_divider(const Fl_Rect& bbox);
+
// Main event handler
int handle_part1(int);
@@ -308,8 +335,8 @@ public:
//
/* Find out if any menu window is under the mouse.
- \return 1 if the coordinates are inside any of the menuwindows
- */
+ \return 1 if the coordinates are inside any of the menuwindows
+*/
bool Menu_State::is_inside(int mx, int my) {
for (menu_index_t i=num_menus-1; i>=0; i--) {
if (menu_window[i]->is_inside(mx, my)) {
@@ -320,10 +347,10 @@ bool Menu_State::is_inside(int mx, int my) {
}
/* Remember this item in the state machine.
- \param[in] i current menu item
- \param[in] m index into menu window array
- \param[in] n index into visible item in that menu window
- */
+ \param[in] i current menu item
+ \param[in] m index into menu window array
+ \param[in] n index into visible item in that menu window
+*/
void Menu_State::set_current_item(const Fl_Menu_Item* i, menu_index_t m, item_index_t n) {
current_item = i;
current_menu_ix = m;
@@ -333,7 +360,7 @@ void Menu_State::set_current_item(const Fl_Menu_Item* i, menu_index_t m, item_in
/* Find and store a menu item in the state machine.
\param[in] m index into menu window array
\param[in] n index into visible item in that menu window
- */
+*/
void Menu_State::set_current_item(menu_index_t m, item_index_t n) {
current_item = (n >= 0) ? menu_window[m]->menu->next(n) : 0;
current_menu_ix = m;
@@ -344,7 +371,7 @@ void Menu_State::set_current_item(menu_index_t m, item_index_t n) {
If the event button is FL_Down, increment once, else go to the bottom of the menu.
\param[in] menu index into menu window list
\return `true` if an item was found, `false` if the menu wrapped
- */
+*/
bool Menu_State::next_item(menu_index_t menu) { // go to next item in menu menu if possible
// `menu` is -1 if no item is currently selected, so use the first menu
if (menu < 0)
@@ -369,10 +396,10 @@ bool Menu_State::next_item(menu_index_t menu) { // go to next item in menu menu
}
/* Go up to the previous selectable menu item.
- If the event button is FL_Up, decrement once, else go to the top of the menu.
- \param[in] menu index into menu window list
- \return `true` if an item was found, `false` if the menu wrapped
- */
+ If the event button is FL_Up, decrement once, else go to the top of the menu.
+ \param[in] menu index into menu window list
+ \return `true` if an item was found, `false` if the menu wrapped
+*/
bool Menu_State::prev_item(menu_index_t menu) { // previous item in menu menu if possible
// `menu` is -1 if no item is currently selected, so use the first menu
if (menu < 0)
@@ -397,9 +424,9 @@ bool Menu_State::prev_item(menu_index_t menu) { // previous item in menu menu if
}
/* Handle the FL_SHORTCUT event.
- \return true if the shortcut was found in the menu and handled.
- */
-bool Menu_State::handle_shortcut() {
+ \return 1 if the shortcut was found in the menu and handled.
+*/
+int Menu_State::handle_shortcut() {
for (menu_index_t mymenu = num_menus; mymenu--;) {
Menu_Window &mw = *(menu_window[mymenu]);
int item;
@@ -408,10 +435,194 @@ bool Menu_State::handle_shortcut() {
set_current_item(m, mymenu, item);
if (!m->submenu())
state = State::DONE;
- return true;
+ return 1;
}
}
- return false;
+ return 0;
+}
+
+/* Move menu item selection left.
+ \return 1
+*/
+int Menu_State::handle_left() {
+ if (in_menubar && current_menu_ix<=1) {
+ prev_item(0);
+ } else if (current_menu_ix>0) {
+ set_current_item(current_menu_ix-1, menu_window[current_menu_ix-1]->selected);
+ }
+ return 1;
+}
+
+/* Move menu item selection right.
+ \return 1
+*/
+int Menu_State::handle_right() {
+ if (in_menubar && (current_menu_ix<=0 || (current_menu_ix == num_menus-1))) {
+ next_item(0);
+ } else if (current_menu_ix < num_menus-1) {
+ next_item(current_menu_ix+1);
+ }
+ return 1;
+}
+
+/* Handle activation of the selected menu item.
+ \return 1
+*/
+int Menu_State::handle_select() {
+ // if the current item is a submenu with no callback,
+ // simulate FL_Right to enter the submenu
+ if ( current_item
+ && (!in_menubar || current_menu_ix > 0)
+ && current_item->selectable()
+ && current_item->submenu()
+ && !current_item->callback_)
+ {
+ return handle_right();
+ }
+ // Ignore keypresses over inactive items, mark KEYBOARD event as used.
+ if (current_item && !current_item->selectable())
+ return 1;
+ // Mark the menu 'done' which will trigger the callback
+ state = State::DONE;
+ return 1;
+}
+
+/* Handle menu cancellation.
+ \return 1
+*/
+int Menu_State::handle_cancel() {
+ set_current_item(0, -1, 0);
+ state = State::DONE;
+ return 1;
+}
+
+/* Handle any keyboard event from the menu windows.
+ \return 1 if the keyboard event was handled, else 0
+*/
+int Menu_State::handle_keyboard_event() {
+ switch (Fl::event_key()) {
+ case FL_BackSpace:
+ prev_item(current_menu_ix);
+ return 1;
+ case FL_Up:
+ if (in_menubar && current_menu_ix == 0) {
+ // Do nothing...
+ } else if (prev_item(current_menu_ix)) {
+ // Do nothing...
+ } else if (in_menubar && current_menu_ix==1) {
+ set_current_item(0, menu_window[0]->selected);
+ }
+ return 1;
+ case FL_Tab:
+ if (Fl::event_shift()) {
+ prev_item(current_menu_ix);
+ return 1;
+ }
+ if (in_menubar && current_menu_ix == 0)
+ return handle_right();
+ /* FALLTHROUGH */
+ case FL_Down:
+ if (current_menu_ix || !in_menubar) {
+ next_item(current_menu_ix);
+ } else if (current_menu_ix < num_menus-1) {
+ next_item(current_menu_ix+1);
+ }
+ return 1;
+ case FL_Right:
+ return handle_right();
+ case FL_Left:
+ return handle_left();
+ case FL_Enter:
+ case FL_KP_Enter:
+ case ' ':
+ return handle_select();
+ case FL_Escape:
+ return handle_cancel();
+ }
+ return 0;
+}
+
+/* Handle all mouse events from the menu windows.
+ \return 1 if the event was handled, else 0
+*/
+int Menu_State::handle_mouse_events(int e) {
+ switch (e) {
+ case FL_MOVE: {
+ static int use_part1_extra = Fl::screen_driver()->need_menu_handle_part1_extra();
+ if (use_part1_extra && state == State::DONE) {
+ return 1; // Fix for STR #2619
+ }
+ }
+ /* FALLTHROUGH */
+ case FL_ENTER:
+ case FL_PUSH:
+ case FL_DRAG:
+ {
+ int mx = Fl::event_x_root();
+ int my = Fl::event_y_root();
+ item_index_t item = 0;
+ menu_index_t mymenu = num_menus-1;
+ // Clicking or dragging outside menu cancels it...
+ if ((!in_menubar || mymenu) && !is_inside(mx, my)) {
+ set_current_item(0, -1, 0);
+ if (e==FL_PUSH)
+ state = State::DONE;
+ return 1;
+ }
+ for (mymenu = num_menus-1; ; mymenu--) {
+ item = menu_window[mymenu]->find_selected(mx, my);
+ if (item >= 0)
+ break;
+ if (mymenu <= 0) {
+ // buttons in menubars must be deselected if we move outside of them!
+ if (current_menu_ix==-1 && e==FL_PUSH) {
+ state = State::DONE;
+ return 1;
+ }
+ if (current_item && current_menu_ix==0 && !current_item->submenu()) {
+ if (e==FL_PUSH) {
+ state = State::DONE;
+ set_current_item(0, -1, 0);
+ }
+ return 1;
+ }
+ // all others can stay selected
+ return 0;
+ }
+ }
+ set_current_item(mymenu, item);
+ if (e == FL_PUSH) {
+ if (current_item && current_item->submenu() // this is a menu title
+ && item != menu_window[mymenu]->selected // and it is not already on
+ && !current_item->callback_) // and it does not have a callback
+ state = State::MENU_PUSHED;
+ else
+ state = State::PUSHED;
+ }
+ }
+ return 1;
+ case FL_RELEASE:
+ // Mouse must either be held down/dragged some, or this must be
+ // the second click (not the one that popped up the menu):
+ if ( !Fl::event_is_click()
+ || state == State::PUSHED
+ || (in_menubar && current_item && !current_item->submenu()) // button
+ ) {
+#if 0 // makes the check/radio items leave the menu up
+ const Fl_Menu_Item* m = current_item;
+ if (m && button && (m->flags & (FL_MENU_TOGGLE|FL_MENU_RADIO))) {
+ ((Fl_Menu_*)button)->picked(m);
+ p[menu_number]->redraw();
+ } else
+#endif
+ // do nothing if they try to pick an inactive item, or a submenu with no callback
+ if (!current_item || (current_item->selectable() &&
+ (!current_item->submenu() || current_item->callback_ || (in_menubar && current_menu_ix <= 0))))
+ state = State::DONE;
+ }
+ return 1;
+ }
+ return 0;
}
//
@@ -427,7 +638,8 @@ int Menu_Window::display_height_ = 0;
Construct a menu window that can render a list of menu items.
\param[in] m pointer to the first menu item in the array
\param[in] X, Y position relative to parent_
- \param[in] Wp, Hp initial minimum size
+ \param[in] Wp, Hp initial minimum size; if Wp is 0, the window will open on the
+ screen with X and Y, else it will open in the screen with the mouse pointer.
\param[in] picked pointer to the currently picked menu item, can be nullptr
\param[in] t pointer to the menutitle window
\param[in] in_menubar set if part of an Fl_Menu_Bar menu
@@ -474,7 +686,8 @@ Menu_Window::Menu_Window(const Fl_Menu_Item* m, int X, int Y, int Wp, int Hp,
}
if (!m1->text) break;
}
- num_items = j;}
+ num_items = j;
+ }
if (in_menubar) {
item_height = 0;
@@ -631,147 +844,18 @@ int Menu_Window::handle_part1(int e) {
Menu_State &pp = *menu_state;
switch (e) {
case FL_KEYBOARD:
- switch (Fl::event_key()) {
- case FL_BackSpace:
- BACKTAB:
- menu_state->prev_item(pp.current_menu_ix);
- return 1;
- case FL_Up:
- if (pp.in_menubar && pp.current_menu_ix == 0) {
- // Do nothing...
- } else if (menu_state->prev_item(pp.current_menu_ix)) {
- // Do nothing...
- } else if (pp.in_menubar && pp.current_menu_ix==1) {
- menu_state->set_current_item(0, pp.menu_window[0]->selected);
- }
- return 1;
- case FL_Tab:
- if (Fl::event_shift()) goto BACKTAB;
- if (pp.in_menubar && pp.current_menu_ix == 0) goto RIGHT;
- case FL_Down:
- if (pp.current_menu_ix || !pp.in_menubar) {
- menu_state->next_item(pp.current_menu_ix);
- } else if (pp.current_menu_ix < pp.num_menus-1) {
- menu_state->next_item(pp.current_menu_ix+1);
- }
- return 1;
- case FL_Right:
- RIGHT:
- if (pp.in_menubar && (pp.current_menu_ix<=0 || (pp.current_menu_ix == pp.num_menus-1))) {
- menu_state->next_item(0);
- } else if (pp.current_menu_ix < pp.num_menus-1) {
- menu_state->next_item(pp.current_menu_ix+1);
- }
- return 1;
- case FL_Left:
- if (pp.in_menubar && pp.current_menu_ix<=1) {
- menu_state->prev_item(0);
- } else if (pp.current_menu_ix>0) {
- menu_state->set_current_item(pp.current_menu_ix-1, pp.menu_window[pp.current_menu_ix-1]->selected);
- }
- return 1;
- case FL_Enter:
- case FL_KP_Enter:
- case ' ':
- // if the current item is a submenu with no callback,
- // simulate FL_Right to enter the submenu
- if ( pp.current_item
- && (!pp.in_menubar || pp.current_menu_ix > 0)
- && pp.current_item->activevisible()
- && pp.current_item->submenu()
- && !pp.current_item->callback_)
- {
- goto RIGHT;
- }
- // Ignore keypresses over inactive items, mark KEYBOARD event as used.
- if (pp.current_item && !pp.current_item->selectable())
- return 1;
- // Mark the menu 'done' which will trigger the callback
- pp.state = State::DONE;
- return 1;
- case FL_Escape:
- menu_state->set_current_item(0, -1, 0);
- pp.state = State::DONE;
- return 1;
- }
+ if (pp.handle_keyboard_event()) return 1;
break;
case FL_SHORTCUT:
- if (menu_state->handle_shortcut()) return 1;
+ if (pp.handle_shortcut()) return 1;
break;
- case FL_MOVE: {
- static int use_part1_extra = Fl::screen_driver()->need_menu_handle_part1_extra();
- if (use_part1_extra && pp.state == State::DONE) {
- return 1; // Fix for STR #2619
- }
- }
- /* FALLTHROUGH */
+ case FL_MOVE:
case FL_ENTER:
case FL_PUSH:
case FL_DRAG:
- {
- int mx = Fl::event_x_root();
- int my = Fl::event_y_root();
- item_index_t item = 0;
- menu_index_t mymenu = pp.num_menus-1;
- // Clicking or dragging outside menu cancels it...
- if ((!pp.in_menubar || mymenu) && !pp.is_inside(mx, my)) {
- menu_state->set_current_item(0, -1, 0);
- if (e==FL_PUSH)
- pp.state = State::DONE;
- return 1;
- }
- for (mymenu = pp.num_menus-1; ; mymenu--) {
- item = pp.menu_window[mymenu]->find_selected(mx, my);
- if (item >= 0)
- break;
- if (mymenu <= 0) {
- // buttons in menubars must be deselected if we move outside of them!
- if (pp.current_menu_ix==-1 && e==FL_PUSH) {
- pp.state = State::DONE;
- return 1;
- }
- if (pp.current_item && pp.current_menu_ix==0 && !pp.current_item->submenu()) {
- if (e==FL_PUSH) {
- pp.state = State::DONE;
- menu_state->set_current_item(0, -1, 0);
- }
- return 1;
- }
- // all others can stay selected
- return 0;
- }
- }
- menu_state->set_current_item(mymenu, item);
- if (e == FL_PUSH) {
- if (pp.current_item && pp.current_item->submenu() // this is a menu title
- && item != pp.menu_window[mymenu]->selected // and it is not already on
- && !pp.current_item->callback_) // and it does not have a callback
- pp.state = State::MENU_PUSHED;
- else
- pp.state = State::PUSHED;
- }
- }
- return 1;
case FL_RELEASE:
- // Mouse must either be held down/dragged some, or this must be
- // the second click (not the one that popped up the menu):
- if ( !Fl::event_is_click()
- || pp.state == State::PUSHED
- || (pp.in_menubar && pp.current_item && !pp.current_item->submenu()) // button
- ) {
-#if 0 // makes the check/radio items leave the menu up
- const Fl_Menu_Item* m = pp.current_item;
- if (m && button && (m->flags & (FL_MENU_TOGGLE|FL_MENU_RADIO))) {
- ((Fl_Menu_*)button)->picked(m);
- pp.p[pp.menu_number]->redraw();
- } else
-#endif
- // do nothing if they try to pick an inactive item, or a submenu with no callback
- if (!pp.current_item || (pp.current_item->selectable() &&
- (!pp.current_item->submenu() || pp.current_item->callback_ || (pp.in_menubar && pp.current_menu_ix <= 0))))
- pp.state = State::DONE;
- }
- return 1;
+ if (pp.handle_mouse_events(e)) return 1;
+ break;
}
return Fl_Window::handle(e);
}
@@ -913,65 +997,88 @@ bool Menu_Window::is_inside(int mx, int my) {
void Menu_Window::draw_entry(const Fl_Menu_Item* m, int n, int eraseit) {
if (!m) return; // this happens if -1 is selected item and redrawn
- int BW = Fl::box_dx(box()); // left box inset
- int xx = BW; // left side of drawing area
- int W = w(); // box width
- int ww = W-2*BW-1; // width of drawing area
- int yy = BW+1+n*item_height+Fl::menu_linespacing()/2-2; // top of text, no spacing
- int hh = item_height - Fl::menu_linespacing(); // height of text without spacing
+ Fl_Rect bbox {
+ Fl::box_dx(box()),
+ Fl::box_dy(box()) + 1 + n*item_height + Fl::menu_linespacing()/2 - 2,
+ w() - Fl::box_dw(box()) - 1/*sic*/,
+ item_height - Fl::menu_linespacing()
+ };
// Clear the entire item rect including the spacing
if (eraseit && n != selected) {
- fl_push_clip(xx+1, yy-(Fl::menu_linespacing()-2)/2, ww-2, hh+(Fl::menu_linespacing()-2));
+ fl_push_clip(bbox.x()+1, bbox.y()-(Fl::menu_linespacing()-2)/2,
+ bbox.w()-2, bbox.h()+(Fl::menu_linespacing()-2));
draw_box(box(), 0, 0, w(), h(), button ? button->color() : color());
fl_pop_clip();
}
// Draw the checkbox, radio box, the menu icon, and the label
- m->draw(xx, yy, ww, hh, button, n==selected);
+ m->draw(bbox.x(), bbox.y(), bbox.w(), bbox.h(), button, n==selected);
// Draw additional decorations on the right side of the label
- // the shortcuts and arrows assume fl_color() was left set by draw():
if (m->submenu()) {
- // Draw the submenu arrow
- // calculate the bounding box of the submenu pointer (arrow)
- int sz = ((hh-2) & (-2)) + 1 ; // must be odd for better centering
- if (sz > 13) sz = 13; // limit arrow size
- int x1 = xx + ww - sz - 2; // left border
- int y1 = yy + (hh-sz)/2 + 1; // top border
-
- // draw an arrow whose style depends on the active scheme
- fl_draw_arrow(Fl_Rect(x1, y1, sz, sz), FL_ARROW_SINGLE, FL_ORIENT_RIGHT, fl_color());
-
+ draw_submenu_arrow(bbox);
} else if (m->shortcut_) {
- // Draw the shortcut modifiers and key texts
- Fl_Font f = m->labelsize_ || m->labelfont_ ? (Fl_Font)m->labelfont_ :
- button ? button->textfont() : FL_HELVETICA;
- fl_font(f, m->labelsize_ ? m->labelsize_ :
- button ? button->textsize() : FL_NORMAL_SIZE);
- const char *k, *s = fl_shortcut_label(m->shortcut_, &k);
- if (fl_utf_nb_char((const unsigned char*)k, (int) strlen(k))<=4) {
- // right-align the modifiers and left-align the key
- char *buf = (char*)malloc(k-s+1);
- memcpy(buf, s, k-s); buf[k-s] = 0;
- fl_draw(buf, xx, yy, ww-shortcut_width, hh, FL_ALIGN_RIGHT);
- fl_draw( k, xx+ww-shortcut_width, yy, shortcut_width, hh, FL_ALIGN_LEFT);
- free(buf);
- } else {
- // right-align to the menu
- fl_draw(s, xx, yy, ww-4, hh, FL_ALIGN_RIGHT);
- }
+ draw_shortcut(bbox, m);
}
-
- // Draw the divider. It's part of the menu, but drawn in the spacing area.
if (m->flags & FL_MENU_DIVIDER) {
- fl_color(FL_DARK3);
- fl_xyline(BW-1, yy+hh+(Fl::menu_linespacing()-2)/2, W-2*BW+2);
- fl_color(FL_LIGHT3);
- fl_xyline(BW-1, yy+hh+((Fl::menu_linespacing()-2)/2+1), W-2*BW+2);
+ draw_divider(bbox);
}
}
+/* Draw the submenu arrow.
+ \param[in] bbox menu item bounding box
+ */
+void Menu_Window::draw_submenu_arrow(const Fl_Rect& bbox) {
+ // calculate the bounding box of the submenu pointer (arrow)
+ int sz = ((bbox.h()-2) & (-2)) + 1 ; // must be odd for better centering
+ if (sz > 13) sz = 13; // limit arrow size
+ int x1 = bbox.x() + bbox.w() - sz - 2; // left border
+ int y1 = bbox.y() + (bbox.h()-sz)/2 + 1; // top border
+
+ // draw an arrow whose style depends on the active scheme
+ fl_draw_arrow(Fl_Rect(x1, y1, sz, sz), FL_ARROW_SINGLE, FL_ORIENT_RIGHT, fl_color());
+}
+
+/* Draw the benu item shortcut text.
+ \param[in] bbox menu item bounding box
+ \param[in] m take the shortcut from this menu item
+ */
+// the shortcuts and arrows assume fl_color() was left set by draw():
+void Menu_Window::draw_shortcut(const Fl_Rect& bbox, const Fl_Menu_Item* m) {
+ // Draw the shortcut modifiers and key texts
+ Fl_Font f = m->labelsize_ || m->labelfont_ ? (Fl_Font)m->labelfont_ :
+ button ? button->textfont() : FL_HELVETICA;
+ fl_font(f, m->labelsize_ ? m->labelsize_ :
+ button ? button->textsize() : FL_NORMAL_SIZE);
+ const char *k, *s = fl_shortcut_label(m->shortcut_, &k);
+ if (fl_utf_nb_char((const unsigned char*)k, (int) strlen(k))<=4) {
+ // right-align the modifiers and left-align the key
+ char *buf = (char*)malloc(k-s+1);
+ memcpy(buf, s, k-s); buf[k-s] = 0;
+ fl_draw(buf, bbox.x(), bbox.y(),
+ bbox.w()-shortcut_width, bbox.h(), FL_ALIGN_RIGHT);
+ fl_draw( k, bbox.x()+bbox.w()-shortcut_width, bbox.y(),
+ shortcut_width, bbox.h(), FL_ALIGN_LEFT);
+ free(buf);
+ } else {
+ // right-align to the menu
+ fl_draw(s, bbox.x(), bbox.y(), bbox.w()-4, bbox.h(), FL_ALIGN_RIGHT);
+ }
+}
+
+/* Draw the divider. It's part of the menu, but drawn in the spacing area.
+ \param[in] bbox menu item bounding box
+*/
+void Menu_Window::draw_divider(const Fl_Rect& bbox) {
+ int y_offset = (Fl::menu_linespacing()-2)/2;
+ fl_color(FL_DARK3);
+ fl_xyline(bbox.x()-1, bbox.b() + y_offset, bbox.r());
+ fl_color(FL_LIGHT3);
+ fl_xyline(bbox.x()-1, bbox.b() + y_offset+1, bbox.r());
+}
+
+
/* Draw the menuwindow. If the damage flags are FL_DAMAGE_CHILD, only redraw
the old selected and the newly selected items.
*/