libyui-mga-ncurses  1.1.0
YMGANCMenuBar.cc
1 /*
2  * Copyright 2020 by Angelo Naselli <anaselli at linux dot it>
3  *
4  * This library is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License as
6  * published by the Free Software Foundation; either version 2.1 of the
7  * License, or (at your option) version 3.0 of the License. This library
8  * is distributed in the hope that it will be useful, but WITHOUT ANY
9  * WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
11  * License for more details. You should have received a copy of the GNU
12  * Lesser General Public License along with this library; if not, write
13  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
14  * Floor, Boston, MA 02110-1301 USA
15  */
16 
17 /*-/
18  *
19  * File: YMGANMenuBar.cc
20  *
21  * Author: Angelo Naselli <anaselli@linux.it>
22  *
23  * /-*/
24 
25 #define YUILogComponent "mga-ncurses"
26 #include <yui/YUILog.h>
27 #include <yui/mga/YMGAMenuBar.h>
28 
29 #include <yui/YTypes.h>
30 #include <yui/YShortcut.h>
31 #include <yui/ncurses/NCurses.h>
32 #include "YMGANCMenuBar.h"
33 #include "NCMGAPopupMenu.h"
34 #include <yui/ncurses/YNCursesUI.h>
35 #include <yui/mga/YMGAMenuItem.h>
36 #include <yui/ncurses/NCLabel.h>
37 
38 
39 struct __MBItem
40 {
41  YItem * item;
42  wchar_t hotkey;
43  wpos pos;
44 };
45 
47 {
48  std::vector<struct __MBItem*> items;
49  __MBItem *selected;
50  unsigned nextSerialNo;
51 
52 
53  __MBItem* getNext()
54  {
55  if (not selected)
56  {
57  for(__MBItem *i : items)
58  {
59  YMGAMenuItem * mi = dynamic_cast<YMGAMenuItem *>(i->item);
60  YUI_CHECK_NEW(mi);
61  if (mi->enabled() && !mi->hidden())
62  return i;
63  }
64  return NULL;
65  }
66 
67  bool found = false;
68  for (uint i=0; i < items.size(); i++)
69  {
70  if (items[i]->item == selected->item)
71  {
72  found = true;
73  }
74  if (found)
75  {
76  if (i+1 < items.size())
77  {
78  YMGAMenuItem * mi = dynamic_cast<YMGAMenuItem *>(items[i+1]->item);
79  YUI_CHECK_NEW(mi);
80  if (mi->enabled() && !mi->hidden())
81  return items[i+1];
82  }
83  }
84  }
85 
86  return selected;
87  };
88 
89  __MBItem* getPrevious()
90  {
91  if (not selected)
92  {
93  for(__MBItem *i : items)
94  {
95  YMGAMenuItem * mi = dynamic_cast<YMGAMenuItem *>(i->item);
96  YUI_CHECK_NEW(mi);
97  if (mi->enabled() && !mi->hidden())
98  return i;
99  }
100  return NULL;
101  }
102 
103  bool found = false;
104  for (uint i=items.size()-1; i >0 ; i--)
105  {
106  if (items[i]->item == selected->item)
107  {
108  found = true;
109  }
110  if (found)
111  {
112  YMGAMenuItem * mi = dynamic_cast<YMGAMenuItem *>(items[i-1]->item);
113  YUI_CHECK_NEW(mi);
114  if (mi->enabled() && !mi->hidden())
115  return items[i-1];
116  }
117  }
118 
119  return selected;
120  };
121 };
122 
123 YMGANCMenuBar::YMGANCMenuBar( YWidget * parent )
124 : YMGAMenuBar( parent )
125 , NCWidget( parent )
126 , d(new Private)
127 {
128  YUI_CHECK_NEW ( d );
129  d->selected = NULL;
130  d->nextSerialNo = 0;
131 
132  defsze= wsze(1,10);
133 
134  yuiDebug() << std::endl;
135 }
136 
137 
138 YMGANCMenuBar::~YMGANCMenuBar()
139 {
140  for (__MBItem *i : d->items)
141  delete i;
142  d->items.clear();
143 
144  delete d;
145 
146  yuiDebug() << std::endl;
147 }
148 
149 
150 int YMGANCMenuBar::preferredWidth()
151 {
152  return wGetDefsze().W;
153 }
154 
155 
156 int YMGANCMenuBar::preferredHeight()
157 {
158  return wGetDefsze().H;
159 }
160 
161 
162 
163 void YMGANCMenuBar::setSize( int newwidth, int newheight )
164 {
165  wRelocate( wpos( 0 ), wsze( newheight, newwidth ) );
166 }
167 
169 {
170  yuiDebug() << key << std::endl;
171 
172  if ( key < 0 || UCHAR_MAX < key )
173  return false;
174 
175  for (YItemIterator it=itemsBegin(); it!=itemsEnd(); it++)
176  {
177  NClabel label = NCstring((*it)->label());
178  label.stripHotkey();
179  yuiDebug() << label << std::endl;
180  if (label.hasHotkey() && (tolower( key ) == tolower(label.hotkey())))
181  return true;
182  }
183 
184  return false;
185 }
186 
187 NCursesEvent YMGANCMenuBar::wHandleHotkey( wint_t key )
188 {
189  yuiDebug() << key << std::endl;
190  NCursesEvent ret = NCursesEvent::none;
191  __MBItem *sel = NULL;
192  for (struct __MBItem *i : d->items)
193  {
194  if ( (tolower(i->hotkey)) == tolower(key))
195  {
196  sel = i;
197  break;
198  }
199  }
200  YUI_CHECK_NEW(sel);
201 
202  YMGAMenuItem *item = dynamic_cast<YMGAMenuItem *>(sel->item);
203  YUI_CHECK_NEW(item);
204  if (!item->enabled())
205  return NCursesEvent::none;
206 
207  d->selected = sel;
208  //Redraw();
209  ret = postMenu();
210 
211  return ret;
212 }
213 
214 NCursesEvent YMGANCMenuBar::wHandleInput( wint_t key )
215 {
216  yuiDebug() << "wHandleInput " << key << std::endl;
217  NCursesEvent ret = NCursesEvent::none;
218 
219  if (itemsBegin() != itemsEnd())
220  {
221  switch ( key )
222  {
223  case KEY_LEFT:
224  d->selected = d->getPrevious();
225  wRedraw();
226  break;
227  case KEY_RIGHT:
228  d->selected = d->getNext();
229  wRedraw();
230  break;
231  case KEY_HOTKEY:
232  case KEY_SPACE:
233  case KEY_RETURN:
234  case KEY_DOWN:
235  ret = postMenu();
236  break;
237  }
238  }
239 
240  return ret;
241 }
242 
243 
244 void YMGANCMenuBar::addItem(YItem* item)
245 {
246  if (itemsBegin() == itemsEnd())
247  defsze = wsze(0,0);
248 
249  YMGAMenuBar::addItem(item);
250 
251  __MBItem *it = new( __MBItem);
252  it->item = item;
253  d->items.push_back(it);
254 
255  NClabel label( NCstring( item->label() ));
256  label.stripHotkey();
257 
258  unsigned int h = defsze.H > 0 ? defsze.H : 0;
259  defsze = wsze( h < label.height() ? label.height() : h,
260  defsze.W + label.width()+5 );
261  yuiDebug() << "label: " << label << " defsze: " << defsze << std::endl;
262 
263  item->setIndex( ++(d->nextSerialNo) );
264 
265  if ( item->hasChildren() )
266  assignUniqueIndex( item->childrenBegin(), item->childrenEnd() );
267 }
268 
269 void YMGANCMenuBar::addItems(const YItemCollection& itemCollection)
270 {
271  YSelectionWidget::addItems(itemCollection);
272 
273  wRedraw();
274 }
275 
276 
277 void YMGANCMenuBar::wRedraw()
278 {
279  if ( !win )
280  return;
281 
282  if (itemsBegin() == itemsEnd())
283  {
284  win->deleteln();
285  return;
286  }
287 
288  int col=0;
289  for ( YItemConstIterator it = itemsBegin() ; it != itemsEnd(); ++it )
290  {
291  __MBItem *sel = NULL;
292  for (__MBItem *i : d->items)
293  {
294  if (i->item == *it)
295  {
296  sel = i;
297  break;
298  }
299  }
300  YUI_CHECK_NEW(sel);
301  if (!d->selected)
302  d->selected = sel;
303 
304  const NCstyle::StWidget & style( widgetStyle(d->selected != sel) );
305  // first item of any YMenuItem is the menu name
306  NClabel label( NCstring( (*it)->label() ));
307  label.stripHotkey();
308  YMGAMenuItem * mi = dynamic_cast<YMGAMenuItem *>(*it);
309  bool disabled = false;
310 
311  if (mi)
312  {
313  if (mi->hidden())
314  {
315  yuiDebug() << mi->label() << " hidden" << std::endl;
316  continue;
317  }
318  else if (!mi->enabled())
319  {
320  disabled = true;
321 
322  yuiDebug() << mi->label() << " disabled" << std::endl;
323  win->bkgdset(wStyle().disabledList.item.plain);
324  }
325  else
326  {
327  win->bkgdset( style.plain );
328  }
329 
330  }
331  win->printw( 0, col, "[" );
332  sel->pos = wpos( 0, col+1 );
333  sel->hotkey = label.hotkey();
334 
335  yuiDebug() << sel->item->label() << " pos: " << sel->pos << " hotkey: " << sel->hotkey << " defsize: " << defsze << std::endl;
336 
337  if (disabled)
338  label.drawAt( *win, wStyle().disabled, sel->pos, wsze( -1, label.width() + 3 ), NC::CENTER );
339  else
340  label.drawAt( *win, style, sel->pos, wsze( -1, label.width() + 3 ), NC::CENTER );
341  col = col + label.width() + 4;
342  win->printw( 0, col, "]" );
343 
344  haveUtf8() ? win->add_wch( 0, col - 1, WACS_DARROW )
345  : win->addch( 0, col - 1, ACS_DARROW );
346  col++;
347  if (col < win->width())
348  {
349  win->move(0, col);
350  win->bkgdset( style.plain );
351  win->clrtoeol();
352  }
353  }
354 
355  #if 0
356  win->vline( ' ', win->maxx() - col +1);
357  win->vline( 0, win->maxx() - 1, win->height(), ' ' );
358  if ( h > 1 )win->vline( 0, win->maxx() - 1, win->height(), ' ' );
359  {
360  win->box( wrect( 0, win->size() - wsze( 0, 1 ) ) );
361 }
362 #endif
363 }
364 
365 
366 NCursesEvent YMGANCMenuBar::postMenu()
367 {
368  if (!d->selected)
369  return NCursesEvent::none;
370 
371  YMGAMenuItem *item = dynamic_cast<YMGAMenuItem *>(d->selected->item);
372  YUI_CHECK_NEW(item);
373  if (!item->enabled())
374  return NCursesEvent::none;
375 
376  // add fix heigth of 1 (dont't use win->height() because win might be invalid, bnc#931154)
377  wpos at( ScreenPos() + wpos( 1, d->selected->pos.C ) );
378  yuiWarning() << " position " << ScreenPos() << " menu position " << at <<std::endl;
379 
380  NCMGAPopupMenu * dialog = new NCMGAPopupMenu( at, item->childrenBegin(), item->childrenEnd() );
381 
382  YUI_CHECK_NEW( dialog );
383 
384  int selection = dialog->post();
385 
386  if ( selection < 0 )
387  {
388  YDialog::deleteTopmostDialog();
389  return NCursesEvent::none;
390  }
391 
392  NCursesEvent ret = NCursesEvent::menu;
393  ret.selection = findMenuItem( selection );
394  yuiMilestone() << "selection " << selection << " " << (ret.selection ? ret.selection->label() : "---") << std::endl;
395  if (!ret.selection)
396  {
397  YDialog::deleteTopmostDialog();
398  return NCursesEvent::none;
399  }
400 
401  yuiMilestone() << dialog->isTopmostDialog() << std::endl;
402 
403  YDialog::deleteTopmostDialog();
404 
405  return ret;
406 
407 }
408 
409 
410 
411 YMenuItem * YMGANCMenuBar::findMenuItem( int index )
412 {
413  return findMenuItem( index, itemsBegin(), itemsEnd() );
414 }
415 
416 
417 YMenuItem * YMGANCMenuBar::findMenuItem( int wantedIndex, YItemConstIterator begin, YItemConstIterator end )
418 {
419  for ( YItemConstIterator it = begin; it != end; ++it )
420  {
421  YMenuItem * item = dynamic_cast<YMenuItem *> (*it);
422 
423  if ( item )
424  {
425  if ( item->index() == wantedIndex )
426  return item;
427 
428  if ( item->hasChildren() )
429  {
430  YMenuItem * result = findMenuItem( wantedIndex, item->childrenBegin(), item->childrenEnd() );
431 
432  if ( result )
433  return result;
434  }
435  }
436  }
437 
438  return 0;
439 }
440 
441 void YMGANCMenuBar::setEnabled( bool do_bv )
442 {
443  NCWidget::setEnabled( do_bv );
444 }
445 
446 
447 
448 
449 void YMGANCMenuBar::assignUniqueIndex( YItemIterator begin, YItemIterator end )
450 {
451  for ( YItemIterator it = begin; it != end; ++it )
452  {
453  YItem * item = *it;
454 
455  item->setIndex( ++(d->nextSerialNo) );
456 
457  if ( item->hasChildren() )
458  assignUniqueIndex( item->childrenBegin(), item->childrenEnd() );
459  }
460 }
461 
462 
463 void YMGANCMenuBar::enableItem(YItem* menu_item, bool enable)
464 {
465  YMGAMenuBar::enableItem(menu_item, enable);
466 }
467 
468 void YMGANCMenuBar::hideItem(YItem* menu_item, bool invisible)
469 {
470  YMGAMenuBar::hideItem(menu_item, invisible);
471 }
472 
474 {
475  for (__MBItem *i : d->items)
476  delete i;
477  d->items.clear();
478  d->selected = NULL;
479  d->nextSerialNo = 0;
480 
481  YSelectionWidget::deleteAllItems();
482  //wRedraw();
483 }
484 
485 
486 
487 static void resolveShortcutsConflictFlat(YItemConstIterator begin, YItemConstIterator end)
488 {
489  bool used[ sizeof( char ) << 8 ];
490  for ( unsigned i=0; i < sizeof( char ) << 8; i++ )
491  used[i] = false;
492  std::vector<YMenuItem*> conflicts;
493 
494  for ( YItemConstIterator it = begin; it != end; ++it )
495  {
496  YMenuItem * item = dynamic_cast<YMenuItem *> (*it);
497 
498  if ( item )
499  {
500  if ( item->hasChildren() )
501  {
502  resolveShortcutsConflictFlat(item->childrenBegin(), item->childrenEnd() );
503  }
504 
505  char shortcut = YShortcut::normalized(YShortcut::findShortcut(item->label()));
506 
507  if (shortcut == 0)
508  {
509  conflicts.push_back(item);
510  yuiMilestone() << "No or invalid shortcut found " << item->label() << endl;
511  }
512  else if (used[(unsigned)shortcut])
513  {
514  conflicts.push_back(item);
515  yuiWarning() << "Conflicting shortcut found " << item->label() << endl;
516  }
517  else
518  {
519  used[(unsigned)shortcut] = true;
520  }
521  }
522  else
523  {
524  yuiWarning() << "non menu item used in call " << (*it)->label() << endl;
525  }
526  }
527 
528  // cannot use YShortcut directly as YItem is not YWidget
529  for(YMenuItem *i: conflicts)
530  {
531  std::string clean = YShortcut::cleanShortcutString(i->label());
532  char new_c = 0;
533 
534  size_t index = 0;
535  for (; index < clean.size(); ++index)
536  {
537  char ch = YShortcut::normalized(clean[index]);
538  // ch is set to 0 by normalized if not valid
539  if (ch != 0 && !used[(unsigned)ch])
540  {
541  new_c = ch;
542  used[(unsigned)ch] = true;
543  break;
544  }
545  }
546 
547  if (new_c != 0)
548  {
549  clean.insert(index, 1, YShortcut::shortcutMarker());
550  yuiMilestone() << "New label used: " << clean << endl;
551  }
552  i->setLabel(clean);
553  }
554 }
__MBItem
Definition: YMGANCMenuBar.cc:40
YMGANCMenuBar::addItem
virtual void addItem(YItem *item)
Add an YMenuItem first item represents the menu name, other sub items menu entries.
Definition: YMGANCMenuBar.cc:244
YMGANCMenuBar::HasHotkey
virtual bool HasHotkey(int key)
Reimplemnted to check all the hotkeys from YMenuItems.
Definition: YMGANCMenuBar.cc:168
YMGANCMenuBar::deleteAllItems
virtual void deleteAllItems()
Delete all items.
Definition: YMGANCMenuBar.cc:473
YMGANCMenuBar::enableItem
virtual void enableItem(YItem *menu_item, bool enable=true)
Enable YMGAMenuItem (menu name or menu entry) to enable/disable it into menubar or menu.
Definition: YMGANCMenuBar.cc:463
YMGANCMenuBar::hideItem
virtual void hideItem(YItem *menu_item, bool invisible=true)
Hide YMGAMenuItem (menu name or menu entry) to hide/show it into menubar or menu.
Definition: YMGANCMenuBar.cc:468
YMGANCMenuBar::Private
Definition: YMGANCMenuBar.cc:47
YMGANCMenuBar::addItems
virtual void addItems(const YItemCollection &itemCollection)
Add multiple items.
Definition: YMGANCMenuBar.cc:269
NCMGAPopupMenu
Definition: NCMGAPopupMenu.h:37