libyui-ncurses  2.57.2
NCMenuBar.cc
1 /*
2  Copyright (C) 2020 SUSE LLC
3  This library is free software; you can redistribute it and/or modify
4  it under the terms of the GNU Lesser General Public License as
5  published by the Free Software Foundation; either version 2.1 of the
6  License, or (at your option) version 3.0 of the License. This library
7  is distributed in the hope that it will be useful, but WITHOUT ANY
8  WARRANTY; without even the implied warranty of MERCHANTABILITY or
9  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
10  License for more details. You should have received a copy of the GNU
11  Lesser General Public License along with this library; if not, write
12  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
13  Floor, Boston, MA 02110-1301 USA
14 */
15 
16 
17 /*-/
18 
19  File: NCMenuBar.cc
20 
21  Author: Jose Iván López <jlopez@suse.de>
22 
23 /-*/
24 
25 
26 #include <algorithm>
27 
28 #define YUILogComponent "ncurses"
29 #include <yui/YUILog.h>
30 #include "NCMenuBar.h"
31 #include "YNCursesUI.h"
32 #include "NCPopupMenu.h"
33 
34 
35 // Margin to the left of the first toplevel item
36 #define LEFT_MARGIN 0
37 
38 // Spacing between toplevel items
39 #define SPACING 2
40 
41 
42 // Helper class that represents a top level menu
44 {
45  wpos position;
46  YMenuItem* item;
47 
48 
49  // Whether the menu can be selected
50  bool isSelectable() const
51  {
52  if ( ! item )
53  return false;
54 
55  return item->isEnabled();
56  }
57 
58 
59  // Whether the menu contains the given hot-key
60  bool hasHotkey( int key ) const
61  {
62  NClabel label = NCstring( item->label() );
63  label.stripHotkey();
64 
65  if ( ! label.hasHotkey() )
66  return false;
67 
68  return tolower( key ) == tolower( label.hotkey() );
69  }
70 
71 };
72 
73 
74 NCMenuBar::NCMenuBar( YWidget* parent ) :
75  YMenuBar( parent ), NCWidget( parent ), _menus()
76 {
77  defsze = wsze( 1, 10 );
78 }
79 
80 
82 {
83  clear();
84 }
85 
86 
87 void
89 {
90  for ( Menu* menu : _menus )
91  delete menu;
92 
93  _menus.clear();
94 
95  defsze = wsze( 1, 10 );
96 }
97 
98 
99 void
101 {
102  clear();
103 
104  int width = LEFT_MARGIN;
105 
106  for ( YItemIterator it = itemsBegin(); it != itemsEnd(); ++it )
107  {
108  YMenuItem * item = dynamic_cast<YMenuItem *>(*it);
109  YUI_CHECK_PTR( item );
110 
111  if ( ! item->isMenu() )
112  YUI_THROW( YUIException( "NCMenuBar: Only menus allowed on toplevel. ") );
113 
114  if ( ! item->isVisible() )
115  continue;
116 
117  Menu * menu = new Menu();
118  menu->item = item;
119  menu->position = wpos( 0, width );
120 
121  _menus.add( menu );
122  item->setUiItem( menu );
123 
124  NClabel label( NCstring( menu->item->label() ) );
125  label.stripHotkey();
126 
127  width += label.width() + SPACING;
128  }
129 
130  selectNextMenu();
131 
132  defsze = wsze( 1, width );
133 }
134 
135 
136 void
138 {
139  if ( !win )
140  return;
141 
142  for ( const Menu * menu : _menus )
143  {
144  const NCstyle::StWidget & style = menuStyle( menu );
145 
146  win->bkgdset( style.plain );
147 
148  NClabel label( NCstring( menu->item->label() ) );
149  label.stripHotkey();
150 
151  label.drawAt( *win, style, menu->position, wsze( -1, label.width() + SPACING ), NC::LEFT );
152  }
153 
154  if ( defsze.W < win->width() ) {
155  const NCstyle::StWidget & bkg_style( widgetStyle( true ) );
156 
157  win->move( 0, defsze.W );
158  win->bkgdset( bkg_style.plain );
159  win->clrtoeol();
160  }
161 }
162 
163 
166 {
167  wpos at(ScreenPos() + wpos( 1, selectedMenu()->position.C) );
168 
169  NCPopupMenu * dialog = new NCPopupMenu(
170  at,
171  selectedMenu()->item->childrenBegin(),
172  selectedMenu()->item->childrenEnd()
173  );
174 
175  YUI_CHECK_NEW( dialog );
176 
177  NCursesEvent event;
178  dialog->post( &event );
179 
180  YDialog::deleteTopmostDialog();
181 
182  return handlePostMenu( event );
183 }
184 
185 
186 void
187 NCMenuBar::setItemEnabled( YMenuItem * item, bool enabled )
188 {
189  YMenuWidget::setItemEnabled( item, enabled );
190  rebuildMenuTree();
191  wRedraw();
192 }
193 
194 
195 void
196 NCMenuBar::setItemVisible( YMenuItem * item, bool visible )
197 {
198  YMenuWidget::setItemVisible( item, visible );
199  rebuildMenuTree();
200  wRedraw();
201 }
202 
203 
204 void
205 NCMenuBar::activateItem( YMenuItem * item )
206 {
207  if ( item->isMenu() || item->isSeparator() )
208  return;
209 
210  NCursesEvent event = NCursesEvent::menu;
211 
212  event.widget = this;
213  event.selection = item;
214 
215  YNCursesUI::ui()->sendEvent( event );
216 }
217 
218 
221 {
222  NCursesEvent event = NCursesEvent::none;
223 
224  switch ( key )
225  {
226  case KEY_LEFT:
227  selectPreviousMenu();
228  wRedraw();
229  break;
230 
231  case KEY_RIGHT:
232  selectNextMenu();
233  wRedraw();
234  break;
235 
236  case KEY_BACKSPACE:
237  wRedraw();
238  break;
239 
240  case KEY_DOWN:
241  case KEY_SPACE:
242  case KEY_RETURN:
243  event = postMenu();
244  break;
245 
246  default:
247  event = NCWidget::wHandleInput(key);
248  break;
249  }
250 
251  return event;
252 }
253 
254 
255 int
257 {
258  return defsze.W;
259 }
260 
261 
262 int
264 {
265  return 1;
266 }
267 
268 
269 void
270 NCMenuBar::setSize( int newWidth, int newHeight )
271 {
272  wRelocate( wpos( 0 ), wsze( newHeight, newWidth ) );
273 }
274 
275 
276 void
277 NCMenuBar::setEnabled( bool enabled )
278 {
279  NCWidget::setEnabled( enabled );
280  YMenuBar::setEnabled( enabled );
281 }
282 
283 
284 bool
286 {
287  if ( !grabFocus() )
288  return YWidget::setKeyboardFocus();
289 
290  return true;
291 }
292 
293 
294 bool
296 {
297  if( key < 0 || UCHAR_MAX < key )
298  return false;
299 
300  return findMenuWithHotkey( key ) != _menus.end();
301 }
302 
303 
306 {
307  CyclicContainer<Menu>::Iterator menu = findMenuWithHotkey( key );
308 
309  if ( menu == _menus.end() )
310  return NCursesEvent::none;
311 
312  _menus.setCurrent( menu );
313 
314  wRedraw();
315  return postMenu();
316 }
317 
318 
319 void
321 {
322  // Any of the items might have its keyboard shortcut changed, but we don't
323  // know which one. So let's simply redraw the widget again.
324 
325  wRedraw();
326 }
327 
328 
330 NCMenuBar::handlePostMenu( const NCursesEvent & event )
331 {
332  NCursesEvent newEvent = NCursesEvent::none;
333 
334  if ( event == NCursesEvent::button )
335  {
336  newEvent = NCursesEvent::menu;
337  newEvent.selection = event.selection;
338  }
339  else if ( event == NCursesEvent::key )
340  {
341  if ( event.keySymbol == "CursorLeft" )
342  {
343  wHandleInput( KEY_LEFT );
344  newEvent = wHandleInput( KEY_DOWN );
345  }
346  else if ( event.keySymbol == "CursorRight" )
347  {
348  wHandleInput( KEY_RIGHT );
349  newEvent = wHandleInput( KEY_DOWN );
350  }
351  else if ( event.keySymbol == "BackSpace" )
352  {
353  newEvent = wHandleInput( KEY_BACKSPACE );
354  }
355  else if ( event.keySymbol == "Hotkey" )
356  {
357  newEvent = wHandleHotkey( event.detail );
358  }
359  }
360 
361  return newEvent;
362 }
363 
364 
366 NCMenuBar::selectedMenu()
367 {
368  return *_menus.current();
369 }
370 
371 
372 void
373 NCMenuBar::selectNextMenu()
374 {
375  _menus.setCurrent( _menus.next() );
376 }
377 
378 
379 void
380 NCMenuBar::selectPreviousMenu()
381 {
382  _menus.setCurrent( _menus.previous() );
383 }
384 
385 
387 NCMenuBar::findMenuWithHotkey( wint_t key )
388 {
389  return std::find_if( _menus.begin(), _menus.end(), [key](Menu * menu) {
390  return menu->hasHotkey( key );
391  });
392 }
393 
394 
395 const NCstyle::StWidget &
396 NCMenuBar::menuStyle( const Menu * menu )
397 {
398  if ( !menu->item->isEnabled() )
399  return wStyle().getWidget( NC::WSdisabled );
400 
401  bool non_active = ( menu != selectedMenu() );
402  return widgetStyle( non_active );
403 }
NCMenuBar::setItemVisible
virtual void setItemVisible(YMenuItem *item, bool visible)
show or hide an item.
Definition: NCMenuBar.cc:196
wsze
Screen dimension (screen size) in the order height, width: (H, W)
Definition: position.h:154
NCstring
A string with an optional hot key.
Definition: NCstring.h:36
YNCursesUI::ui
static YNCursesUI * ui()
Access the global Y2NCursesUI.
Definition: YNCursesUI.h:93
YNCursesUI::sendEvent
void sendEvent(NCursesEvent event)
Send an event to the UI.
Definition: YNCursesUI.cc:456
NCMenuBar::clear
void clear()
Clear all content.
Definition: NCMenuBar.cc:88
NCMenuBar::setEnabled
virtual void setEnabled(bool enabled)
Enable or disable this widget.
Definition: NCMenuBar.cc:277
NCMenuBar::preferredHeight
virtual int preferredHeight()
Return the preferred height for this widget.
Definition: NCMenuBar.cc:263
NCWidget::win
NCursesWindow * win
(owned)
Definition: NCWidget.h:103
NCMenuBar::rebuildMenuTree
virtual void rebuildMenuTree()
Rebuild the displayed menu tree from the internally stored YMenuItems.
Definition: NCMenuBar.cc:100
NCMenuBar::wHandleHotkey
virtual NCursesEvent wHandleHotkey(wint_t key)
Handle keyboard input.
Definition: NCMenuBar.cc:305
NCWidget::setEnabled
virtual void setEnabled(bool do_bv)=0
Pure virtual to make sure every widget implements it.
Definition: NCWidget.cc:392
NClabel
Multi-line string, with optional hotkey, drawable.
Definition: NCtext.h:82
NCWidget
Definition: NCWidget.h:46
NCMenuBar::Menu
Definition: NCMenuBar.cc:44
NCursesWindow::clrtoeol
int clrtoeol()
Clear to the end of the line.
Definition: ncursesw.h:1540
NCMenuBar::postMenu
NCursesEvent postMenu()
Open a menu dialog.
Definition: NCMenuBar.cc:165
NCstyle::StWidget
Definition: NCstyle.h:358
NCMenuBar::NCMenuBar
NCMenuBar(YWidget *parent)
Constructor.
Definition: NCMenuBar.cc:74
NCMenuBar::setSize
virtual void setSize(int newWidth, int newHeight)
Set the size of this widget.
Definition: NCMenuBar.cc:270
NCursesWindow::width
int width() const
Number of columns in this window.
Definition: ncursesw.h:1077
NCursesWindow::move
int move(int y, int x)
Move cursor the this position.
Definition: ncursesw.h:1157
NCMenuBar::HasHotkey
virtual bool HasHotkey(int key)
Whether any menu option has the given hot-key .
Definition: NCMenuBar.cc:295
NCMenuBar::shortcutChanged
virtual void shortcutChanged()
Notification that some shortcut was changed.
Definition: NCMenuBar.cc:320
wpos
Screen position pair in the order line, column: (L, C)
Definition: position.h:110
NCMenuBar::wHandleInput
virtual NCursesEvent wHandleInput(wint_t key)
Handle keyboard input.
Definition: NCMenuBar.cc:220
NCMenuBar::activateItem
virtual void activateItem(YMenuItem *item)
Support for the Rest API for UI testing:
Definition: NCMenuBar.cc:205
NCMenuBar::preferredWidth
virtual int preferredWidth()
Return the preferred width for this widget.
Definition: NCMenuBar.cc:256
NCMenuBar::setItemEnabled
virtual void setItemEnabled(YMenuItem *item, bool enabled)
Enable or disable an item.
Definition: NCMenuBar.cc:187
NCMenuBar::setKeyboardFocus
virtual bool setKeyboardFocus()
Set the keyboard focus to this widget.
Definition: NCMenuBar.cc:285
NCursesEvent
Definition: NCurses.h:73
NCMenuBar::~NCMenuBar
virtual ~NCMenuBar()
Destructor.
Definition: NCMenuBar.cc:81
NCursesWindow::bkgdset
void bkgdset(chtype ch)
Set the background property.
Definition: ncursesw.h:1450
NCMenuBar::wRedraw
virtual void wRedraw()
Reimplemented from NCWidget.
Definition: NCMenuBar.cc:137
CyclicContainer
Container class that allows cyclic navigation between its elements by moving to the next/previous ele...
Definition: CyclicContainer.h:40
NCPopupMenu
Definition: NCPopupMenu.h:33