libyui-mga-ncurses  1.1.0
NCMenu.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 /*-/
20 
21  File: NCMenu.cc
22 
23  Author: Angelo Naselli <anaselli@linux.it>
24 
25 /-*/
26 
27 #define YUILogComponent "ncurses"
28 #include <yui/YUILog.h>
29 #include "NCMenu.h"
30 #include <yui/ncurses/YNCursesUI.h>
31 
32 #include <yui/YMenuItem.h>
33 #include <yui/YSelectionWidget.h>
34 #include <yui/mga/YMGAMenuItem.h>
35 
36 
37 class NCMenuLine : public NCTableLine
38 {
39 
40 private:
41 
42  YMenuItem * yitem;
43 
44  NCMenuLine * nsibling;
45  NCMenuLine * fchild;
46 
47  mutable chtype * prefix;
48 
49 
50 public:
51 
52  NCMenuLine( YMenuItem * item )
53  : NCTableLine( 0 )
54  , yitem( item )
55  , nsibling( 0 )
56  , fchild( 0 )
57  , prefix( 0 )
58  {
59 
60  YMenuSeparator *separator = dynamic_cast<YMenuSeparator *>(yitem);
61  if (separator)
62  {
63  //Append( new NCTableCol( "", NCTableCol::SEPARATOR ) );
64  SetState( S_DISABELED );
65  }
66  else
67  {
68  YMGAMenuItem *mi = dynamic_cast<YMGAMenuItem*> (yitem);
69  YUI_CHECK_PTR(mi);
70 
71  if ( mi->hidden())
72  {
73  yuiDebug() << mi->label() << " hidden" << std::endl;
74  SetState(S_HIDDEN);
75  }
76  else if ( !mi->enabled() )
77  {
78  yuiDebug() << mi->label() << " disabled" << std::endl;
79  SetState( S_DISABELED );
80  }
81  else
82  {
83  SetState(S_HEADLINE);
84  }
85 
86  // leaving next even if managed into MGAPopupMenu
87  if ( yitem->hasChildren() )
88  { yuiDebug() << mi->label() << " has submenu" << std::endl;
89  Append( new NCTableCol( NCstring( yitem->label() + " ..." ) ) );
90  }
91  else
92  {
93  Append( new NCTableCol( NCstring( yitem->label() ) ) );
94  }
95  stripHotkeys();
96  }
97  }
98 
99  virtual ~NCMenuLine() {
100  delete [] prefix;
101  }
102 
103 public:
104 
105  YMenuItem * YItem() const {
106  return yitem;
107  }
108 
109 
110 
111  virtual void DrawAt( NCursesWindow & w, const wrect at,
112  NCTableStyle & tableStyle,
113  bool active ) const
114  {
115  YMenuSeparator *separator = dynamic_cast<YMenuSeparator *>(yitem);
116  if (separator)
117  {
118  w.move( at.Pos.L, at.Pos.C );
119  w.hline(ACS_HLINE, 0);
120  }
121  else
122  {
123  NClabel l(NCstring(yitem->label()));
124  l.stripHotkey();
125  yuiDebug() << yitem->label() << " hotcol: "<< l.hotpos() << " hotkey: " << l.hotkey() << std::endl;
126 
127  if ( !isSpecial() )
128  w.bkgdset( tableStyle.hotBG( vstate, NCTableCol::PLAIN ) );
129 
130  yuiDebug() << "tableStyle hotcol: " << tableStyle.listStyle().title << " bg: " << tableStyle.hotBG(vstate, tableStyle.HotCol()) << std::endl;
131 
132 
133  NCTableLine::DrawAt( w, at, tableStyle, active );
134  // NOTE I couldn't be able to fix hot char representation, so i had to force it
135  if (l.hasHotkey())
136  {
137  w.move(at.Pos.L, l.hotpos());
138  w.addch(l.hotkey() | A_UNDERLINE);
139  }
140  //w.move( at.Pos.L, at.Pos.C );
141  }
142  }
143 };
144 
145 
146 
147 
148 
149 
150 NCMenu::NCMenu( YWidget * parent )
151  : YTree( parent, "", FALSE, FALSE )
152  , NCPadWidget( parent )
153 {
154  yuiDebug() << std::endl;
155  // minimum size 3 line 8 coulmn
156  defsze = wsze(3, 8);
157 }
158 
159 
160 
161 NCMenu::~NCMenu()
162 {
163  yuiDebug() << std::endl;
164 }
165 
166 
167 
168 
169 // Return pointer to tree line at given index
170 inline const NCMenuLine * NCMenu::getTreeLine( unsigned idx ) const
171 {
172  if ( myPad() )
173  return dynamic_cast<const NCMenuLine *>( myPad()->GetLine( idx ) );
174  else
175  return 0;
176 }
177 
178 
179 
180 
181 // Modify tree line at given index
182 inline NCMenuLine * NCMenu::modifyTreeLine( unsigned idx )
183 {
184  if ( myPad() )
185  {
186  return dynamic_cast<NCMenuLine *>( myPad()->ModifyLine( idx ) );
187  }
188 
189  return 0;
190 }
191 
192 // Set preferred width
193 int NCMenu::preferredWidth()
194 {
195  // TODO
196  wsze sze = wsze::max( defsze, wsze( 0, labelWidth() + 2 ) );
197  return defsze.W + 4;// border and scroll;
198 }
199 
200 // Set preferred height
201 int NCMenu::preferredHeight()
202 {
203  //TODO
204  wsze sze = wsze::max( defsze, wsze( 0, labelWidth() + 2 ) );
205  return defsze.H;//6;
206 }
207 
208 void NCMenu::setSize( int newwidth, int newheight )
209 {
210  wRelocate( wpos( 0 ), wsze( newheight, newwidth ) );
211 }
212 
213 
214 // Enable/disable widget
215 void NCMenu::setEnabled( bool do_bv )
216 {
217  NCWidget::setEnabled( do_bv );
218  YWidget::setEnabled( do_bv );
219 }
220 // Return YMenuItem pointer for a current line
221 // (under the cursor)
222 YMenuItem * NCMenu::getCurrentItem() const
223 {
224  YMenuItem * yitem = 0;
225 
226  if ( myPad() && myPad()->GetCurrentLine() )
227  {
228  const NCMenuLine * cline = dynamic_cast<const NCMenuLine *>( myPad()->GetCurrentLine() );
229 
230  if ( cline && cline->isEnabeled())
231  yitem = cline->YItem();
232  }
233 
234  yuiDebug() << "-> " << ( yitem ? yitem->label().c_str() : "noitem" ) << std::endl;
235 
236  return yitem;
237 }
238 
239 void NCMenu::deselectAllItems()
240 {
241  YTree::deselectAllItems();
242 }
243 
244 
245 // Set current item (under the cursor) to selected
246 void NCMenu::selectItem( YItem *item, bool selected )
247 {
248  if ( !myPad() )
249  return;
250 
251  YMenuItem * treeItem = dynamic_cast<YMenuItem *>( item );
252  YUI_CHECK_PTR( treeItem );
253  YMenuItem *citem = getCurrentItem();
254 
255  //retrieve position of item
256  int at = treeItem->index();
257 
258 // NCMenuLine * cline = 0; // current line
259 
260  if ( !selected )
261  {
262  if ( treeItem == citem )
263  {
264  YTree::deselectAllItems();
265  }
266  else
267  {
268  YTree::selectItem ( treeItem, false );
269  }
270  }
271  else
272  {
273  YTree::selectItem( treeItem, selected );
274 
275  //this highlights selected item, possibly unpacks the tree
276  //should it be in currently hidden branch
277  myPad()->ShowItem( getTreeLine( at ) );
278  }
279 }
280 
281 
282 
283 
284 // Set current item (at given index) to selected
285 // (overloaded for convenience)
286 void NCMenu::selectItem( int index )
287 {
288  YItem * item = YTree::itemAt( index );
289 
290  if ( item )
291  {
292  selectItem( item, true );
293  }
294  else
295  YUI_THROW( YUIException( "Can't find selected item" ) );
296 }
297 
298 
299 
300 
301 void NCMenu::rebuildTree()
302 {
303  DelPad();
304  Redraw();
305 }
306 
307 
308 
309 
310 // Creates empty pad
311 NCPad * NCMenu::CreatePad()
312 {
313  wsze psze( defPadSze() );
314  NCPad * npad = new NCTreePad( psze.H, psze.W, *this );
315  npad->bkgd( listStyle().item.hint );
316  return npad;
317 }
318 
319 bool NCMenu::HasHotkey(int key)
320 {
321  yuiDebug() << key << std::endl;
322 
323  if ( key < 0 || UCHAR_MAX < key )
324  return false;
325 
326  for (YItemIterator it=itemsBegin(); it!=itemsEnd(); it++)
327  {
328  NClabel label = NCstring((*it)->label());
329  label.stripHotkey();
330  yuiDebug() << label << std::endl;
331  if (label.hasHotkey() && (tolower( key ) == tolower(label.hotkey())))
332  return true;
333  }
334 
335  return false;
336 }
337 
338 NCursesEvent NCMenu::wHandleHotkey( wint_t key )
339 {
340  yuiDebug() << "Key: " << key << std::endl;
341  if ( key >= 0 && key < UCHAR_MAX ) // < myPad()->setItemByKey( key ) )
342  {
343  int hkey = tolower( key );
344  for ( YItemIterator it = itemsBegin(); it < itemsEnd(); ++it )
345  {
346  YMGAMenuItem *mi = dynamic_cast<YMGAMenuItem*> (*it);
347  YUI_CHECK_PTR( mi );
348  if (mi->enabled())
349  {
350  NClabel l (NCstring( mi->label() ));
351  l.stripHotkey();
352  yuiDebug() << mi->label() << " " << l << " hotpos: " << l.hotpos() << " hkey: " << l.hotkey() << std::endl;
353  if (tolower(l.hotkey()) == hkey)
354  {
355  selectItem(mi->index());
356  return wHandleInput( KEY_RETURN );
357  }
358  }
359  else
360  {
361  yuiDebug() << mi->label() << " disabled" << std::endl;
362  }
363  }
364  }
365  return NCursesEvent::none;
366 }
367 
368 
369 
370 // Creates tree lines and appends them to TreePad
371 // (called recursively for each child of an item)
372 void NCMenu::CreateTreeLine( NCTreePad * pad, YItem * item )
373 {
374  //set item index explicitely, it is set to -1 by default
375  //which makes selecting items painful
376  item->setIndex( idx++ );
377 
378  YMenuItem * treeItem = dynamic_cast<YMenuItem *>( item );
379  YUI_CHECK_PTR( treeItem );
380 
381  // let's assume to have a menu enable scrolling for more than 10 lines
382  int h = defsze.H > 8 ? 10 : defsze.H + 1;
383  // let's assume to have a menu enable scrolling for more than 40 columns
384  int w = int(item->label().length()) <= defsze.W ? defsze.W : (item->label().length() > 39 ? 40 : item->label().length());
385  defsze = wsze( h, w );
386 
387  NCMenuLine * line = new NCMenuLine( treeItem );
388  pad->Append( line );
389 
390  if (item->selected())
391  {
392  //retrieve position of item
393  int at = treeItem->index();
394 
395  //this highlights selected item, possibly unpacks the tree
396  //should it be in currently hidden branch
397  pad->ShowItem( getTreeLine( at ) );
398  }
399 
400  //line->stripHotkeys();
401 }
402 
403 // Returns current item (pure virtual in YTree)
404 YMenuItem * NCMenu::currentItem()
405 {
406  return getCurrentItem();
407 }
408 
409 // Fills TreePad with lines (uses CreateTreeLines to create them)
410 void NCMenu::DrawPad()
411 {
412  if ( !myPad() )
413  {
414  yuiWarning() << "PadWidget not yet created" << std::endl;
415  return;
416  }
417 
418  idx = 0;
419  // YItemIterator iterates over the toplevel items
420  for ( YItemIterator it = itemsBegin(); it < itemsEnd(); ++it )
421  {
422  CreateTreeLine( myPad(), *it );
423  }
424 
425  idx = 0;
426  NCPadWidget::DrawPad();
427 }
428 
429 
430 
431 NCursesEvent NCMenu::wHandleInput( wint_t key )
432 {
433  NCursesEvent ret = NCursesEvent::none;
434  YMenuItem * oldCurrentItem = getCurrentItem();
435 
436  bool handled = handleInput( key ); // NCTreePad::handleInput()
437  const YItem * currentItem = getCurrentItem();
438 
439  if ( !currentItem )
440  return ret;
441  YMGAMenuItem *mi = dynamic_cast<YMGAMenuItem*>( getCurrentItem() );
442  if (!mi)
443  return ret;
444 
445  if (mi->enabled())
446  {
447  if ( ! handled )
448  {
449  switch ( key )
450  {
451  // KEY_SPACE is handled in NCMenuLine::handleInput
452  case KEY_RETURN:
453 
454  if ( notify() )
455  {
456  return NCursesEvent::Activated;
457  }
458  else
459  {
460  ret = NCursesEvent::button;
461  }
462  break;
463  }
464  }
465 
466  YTree::selectItem( const_cast<YItem *>( currentItem ), true );
467 
468  if ( notify() && immediateMode() && ( oldCurrentItem != currentItem ) )
469  ret = NCursesEvent::SelectionChanged;
470  }
471 
472  yuiDebug() << "Notify: " << ( notify() ? "true" : "false" ) <<
473  " Return event: " << ret.reason << std::endl;
474 
475  return ret;
476 }
477 
478 
479 
480 
481 // clears the table and the lists holding
482 // the values
483 void NCMenu::deleteAllItems()
484 {
485  YTree::deleteAllItems();
486  myPad()->ClearTable();
487 }
NCMenuLine
Definition: NCMenu.cc:38