libyui-ncurses  2.55.0
NCTree.cc
1 /*
2  Copyright (C) 2000-2012 Novell, Inc
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: NCTree.cc
20 
21  Author: Michael Andres <ma@suse.de>
22 
23 /-*/
24 
25 #define YUILogComponent "ncurses"
26 #include <yui/YUILog.h>
27 #include "NCTree.h"
28 #include "YNCursesUI.h"
29 
30 #include <yui/TreeItem.h>
31 #include <yui/YSelectionWidget.h>
32 
33 
34 class NCTreeLine : public NCTableLine
35 {
36 
37 private:
38 
39  YTreeItem * yitem;
40  const unsigned level;
41 
42  NCTreeLine * parent;
43  NCTreeLine * nsibling;
44  NCTreeLine * fchild;
45 
46  mutable chtype * prefix;
47  bool multiSel;
48  unsigned prefixLen() const { return level + 3; }
49 
50 public:
51 
52  NCTreeLine( NCTreeLine * p, YTreeItem * item, bool multiSelection )
53  : NCTableLine( 0 )
54  , yitem( item )
55  , level( p ? p->level + 1 : 0 )
56  , parent( p )
57  , nsibling( 0 )
58  , fchild( 0 )
59  , prefix( 0 )
60  , multiSel( multiSelection )
61  {
62  if ( parent )
63  {
64  if ( parent->fchild )
65  {
66  NCTreeLine * s = parent->fchild;
67 
68  for ( ; s->nsibling; s = s->nsibling )
69  ;
70 
71  s->nsibling = this;
72  }
73  else
74  {
75  parent->fchild = this;
76  }
77 
78  if ( !parent->yitem->isOpen() )
79  {
80  SetState( S_HIDDEN );
81  }
82  }
83 
84  if ( !multiSel )
85  {
86  Append( new NCTableCol( NCstring( std::string( prefixLen(), ' ' )
87  + yitem->label() ) ) );
88  }
89  else
90  {
91  Append( new NCTableCol( NCstring( std::string( prefixLen(), ' ' ) + "[ ] "
92  + yitem->label() ) ) );
93  }
94  }
95 
96  virtual ~NCTreeLine() { delete [] prefix; }
97 
98 public:
99 
100  YTreeItem * YItem() const { return yitem; }
101 
102  unsigned Level() const { return level; }
103 
104  virtual bool isVisible() const
105  {
106  return !parent || ( !isHidden() && parent->isVisible() );
107  }
108 
109 
110  virtual int ChangeToVisible()
111  {
112  if ( isVisible() )
113  return 0;
114 
115  if ( parent )
116  {
117  parent->ChangeToVisible();
118 
119  for ( NCTreeLine * c = parent->fchild; c; c = c->nsibling )
120  {
121  c->ClearState( S_HIDDEN );
122  c->YItem()->setOpen( true );
123  }
124  }
125  else
126  {
127  ClearState( S_HIDDEN );
128  yitem->setOpen( true );
129  }
130 
131  return 1;
132  }
133 
134 
135  virtual unsigned Hotspot( unsigned & at ) const
136  {
137  at = Level();
138  return 6;
139  }
140 
141 
142  virtual int handleInput( wint_t key )
143  {
144  if ( !fchild )
145  return 0;
146 
147  switch ( key )
148  {
149  case KEY_IC:
150  case '+':
151  if ( fchild->isVisible() )
152  return 0;
153 
154  break;
155 
156  case KEY_DC:
157  case '-':
158  if ( !fchild->isVisible() )
159  return 0;
160 
161  break;
162 
163  case KEY_SPACE:
164  // case KEY_RETURN: see bug 67350
165 
166  break;
167 
168  default:
169  return 0;
170 
171  break;
172  }
173 
174  if ( fchild->isVisible() )
175  {
176  yitem->setOpen( false );
177  yuiMilestone() << "Closing item " << yitem->label() << std::endl;
178 
179  for ( NCTreeLine * c = fchild; c; c = c->nsibling )
180  c->SetState( S_HIDDEN );
181  }
182  else
183  {
184  yitem->setOpen( true );
185  yuiMilestone() << "Opening item " << yitem->label() << std::endl;
186 
187  for ( NCTreeLine * c = fchild; c; c = c->nsibling )
188  c->ClearState( S_HIDDEN );
189  }
190 
191  return 1;
192  }
193 
194 
195  virtual void DrawAt( NCursesWindow & w, const wrect at,
196  NCTableStyle & tableStyle,
197  bool active ) const
198  {
199 
200  NCTableLine::DrawAt( w, at, tableStyle, active );
201 
202  if ( !isSpecial() )
203  w.bkgdset( tableStyle.getBG( vstate, NCTableCol::SEPARATOR ) );
204 
205  if ( ! prefix )
206  {
207  prefix = new chtype[prefixLen()];
208  chtype * tagend = &prefix[prefixLen()-1];
209  *tagend-- = ACS_HLINE;
210  *tagend-- = fchild ? ACS_TTEE : ACS_HLINE;
211 
212  if ( parent )
213  {
214  *tagend-- = nsibling ? ACS_LTEE : ACS_LLCORNER;
215 
216  for ( NCTreeLine * p = parent; p; p = p->parent )
217  {
218  *tagend-- = p->nsibling ? ACS_VLINE : ( ' '&A_CHARTEXT );
219  }
220  }
221  else
222  {
223  *tagend-- = ACS_HLINE;
224  }
225  }
226 
227  w.move( at.Pos.L, at.Pos.C );
228 
229  unsigned i = 0;
230 
231  for ( ; i < prefixLen(); ++i )
232  w.addch( prefix[i] );
233 
234  w.move( at.Pos.L, at.Pos.C + prefixLen() - 2 );
235 
236  if ( fchild && !isSpecial() )
237  w.bkgdset( tableStyle.highlightBG( vstate, NCTableCol::HINT,
238  NCTableCol::SEPARATOR ) );
239 
240  if ( fchild && !fchild->isVisible() )
241  w.addch( '+' );
242  else
243  w.addch( prefix[prefixLen() - 2] );
244  }
245 };
246 
247 
248 
249 
250 
251 
252 NCTree::NCTree( YWidget * parent, const std::string & nlabel, bool multiselection, bool recursiveselection )
253  : YTree( parent, nlabel, multiselection, recursiveselection )
254  , NCPadWidget( parent )
255  , multiSel ( multiselection )
256 {
257  yuiDebug() << std::endl;
258 
259  if ( multiselection && recursiveselection )
260  yuiMilestone() << "NCTree recursive multi selection ON" << std::endl;
261  else if ( multiselection )
262  yuiMilestone() << "NCTree multi selection ON" << std::endl;
263 
264  setLabel( nlabel );
265 }
266 
267 
268 
269 NCTree::~NCTree()
270 {
271  yuiDebug() << std::endl;
272 }
273 
274 
275 
276 
277 // Return pointer to tree line at given index
278 inline const NCTreeLine * NCTree::getTreeLine( unsigned idx ) const
279 {
280  if ( myPad() )
281  return dynamic_cast<const NCTreeLine *>( myPad()->GetLine( idx ) );
282  else
283  return 0;
284 }
285 
286 
287 
288 
289 // Modify tree line at given index
290 inline NCTreeLine * NCTree::modifyTreeLine( unsigned idx )
291 {
292  if ( myPad() )
293  {
294  return dynamic_cast<NCTreeLine *>( myPad()->ModifyLine( idx ) );
295  }
296 
297  return 0;
298 }
299 
300 
301 
302 
303 // Set preferred width
304 int NCTree::preferredWidth()
305 {
306  wsze sze = wsze::max( defsze, wsze( 0, labelWidth() + 2 ) );
307  return sze.W;
308 }
309 
310 
311 
312 
313 // Set preferred height
314 int NCTree::preferredHeight()
315 {
316  wsze sze = wsze::max( defsze, wsze( 0, labelWidth() + 2 ) );
317  return sze.H;
318 }
319 
320 
321 
322 
323 // Enable/disable widget
324 void NCTree::setEnabled( bool do_bv )
325 {
326  NCWidget::setEnabled( do_bv );
327  YWidget::setEnabled( do_bv );
328 }
329 
330 
331 
332 
333 void NCTree::setSize( int newwidth, int newheight )
334 {
335  wRelocate( wpos( 0 ), wsze( newheight, newwidth ) );
336 }
337 
338 
339 
340 
341 // Return YTreeItem pointer for a current line
342 // (under the cursor)
343 YTreeItem * NCTree::getCurrentItem() const
344 {
345  YTreeItem * yitem = 0;
346 
347  if ( myPad() && myPad()->GetCurrentLine() )
348  {
349  const NCTreeLine * cline = dynamic_cast<const NCTreeLine *>( myPad()->GetCurrentLine() );
350 
351  if ( cline )
352  yitem = cline->YItem();
353  }
354 
355  yuiDebug() << "-> " << ( yitem ? yitem->label().c_str() : "noitem" ) << std::endl;
356 
357  return yitem;
358 }
359 
360 void NCTree::deselectAllItems()
361 {
362  if ( multiSel)
363  {
364  YItemCollection selectedItems = YTree::selectedItems();
365 
366  for ( YItemConstIterator it = selectedItems.begin(); it != selectedItems.end(); ++it )
367  {
368  selectItem( *it, false );
369  }
370  }
371 
372  YTree::deselectAllItems();
373 }
374 
375 
376 // Set current item (under the cursor) to selected
377 void NCTree::selectItem( YItem *item, bool selected )
378 {
379  if ( !myPad() )
380  return;
381 
382  YTreeItem * treeItem = dynamic_cast<YTreeItem *>( item );
383  YUI_CHECK_PTR( treeItem );
384  YTreeItem *citem = getCurrentItem();
385 
386  //retrieve position of item
387  int at = treeItem->index();
388 
389  NCTreeLine * cline = 0; // current line
390  NCTableCol * ccol = 0; // current column
391 
392  if ( multiSel )
393  {
394  cline = modifyTreeLine( at );
395  if ( cline )
396  {
397  ccol = cline->GetCol(0);
398  }
399  }
400 
401  if ( !selected )
402  {
403  if ( !multiSel && (treeItem == citem) )
404  {
405  YTree::deselectAllItems();
406  }
407  else
408  {
409  YTree::selectItem ( treeItem, false );
410  if ( ccol )
411  {
412  ccol->SetLabel( NCstring( std::string( cline->Level() + 3, ' ' ) + "[ ] "
413  + item->label() ) );
414  }
415  }
416  }
417  else
418  {
419  YTree::selectItem( treeItem, selected );
420 
421  if ( multiSel && ccol )
422  {
423  ccol->SetLabel( NCstring( std::string( cline->Level() + 3, ' ' ) + "[x] "
424  + item->label() ) );
425  }
426 
427  //this highlights selected item, possibly unpacks the tree
428  //should it be in currently hidden branch
429  myPad()->ShowItem( getTreeLine( at ) );
430  }
431 }
432 
433 
434 
435 
436 // Set current item (at given index) to selected
437 // (overloaded for convenience)
438 void NCTree::selectItem( int index )
439 {
440  YItem * item = YTree::itemAt( index );
441 
442  if ( item )
443  {
444  selectItem( item, true );
445  }
446  else
447  YUI_THROW( YUIException( "Can't find selected item" ) );
448 }
449 
450 
451 
452 void NCTree::setLabel( const std::string & nlabel )
453 {
454  YTree::setLabel( nlabel );
455  NCPadWidget::setLabel( NCstring( nlabel ) );
456 }
457 
458 
459 
460 void NCTree::rebuildTree()
461 {
462  DelPad();
463  Redraw();
464 }
465 
466 
467 
468 
469 // Creates empty pad
470 NCPad * NCTree::CreatePad()
471 {
472  wsze psze( defPadSze() );
473  NCPad * npad = new NCTreePad( psze.H, psze.W, *this );
474  npad->bkgd( listStyle().item.plain );
475  return npad;
476 }
477 
478 
479 // Creates tree lines and appends them to TreePad
480 // (called recursively for each child of an item)
481 void NCTree::CreateTreeLines( NCTreeLine * parentLine, NCTreePad * pad, YItem * item )
482 {
483  //set item index explicitely, it is set to -1 by default
484  //which makes selecting items painful
485  item->setIndex( idx++ );
486 
487  YTreeItem * treeItem = dynamic_cast<YTreeItem *>( item );
488  YUI_CHECK_PTR( treeItem );
489 
490  NCTreeLine * line = new NCTreeLine( parentLine, treeItem, multiSel );
491  pad->Append( line );
492 
493  if (item->selected())
494  {
495  //retrieve position of item
496  int at = treeItem->index();
497  NCTreeLine * cline = 0; // current line
498  NCTableCol * ccol = 0; // current column
499  if ( multiSel )
500  {
501  cline = modifyTreeLine( at );
502  if ( cline )
503  {
504  ccol = cline->GetCol(0);
505  }
506  if ( ccol )
507  {
508  ccol->SetLabel( NCstring( std::string( cline->Level() + 3, ' ' ) + "[x] "
509  + item->label() ) );
510  }
511  }
512  //this highlights selected item, possibly unpacks the tree
513  //should it be in currently hidden branch
514  pad->ShowItem( getTreeLine( at ) );
515  }
516  // iterate over children
517 
518  for ( YItemIterator it = item->childrenBegin(); it < item->childrenEnd(); ++it )
519  {
520  CreateTreeLines( line, pad, *it );
521  }
522 }
523 
524 // Returns current item (pure virtual in YTree)
525 YTreeItem * NCTree::currentItem()
526 {
527  return getCurrentItem();
528 }
529 
530 // Fills TreePad with lines (uses CreateTreeLines to create them)
531 void NCTree::DrawPad()
532 {
533  if ( !myPad() )
534  {
535  yuiWarning() << "PadWidget not yet created" << std::endl;
536  return;
537  }
538 
539  idx = 0;
540  // YItemIterator iterates over the toplevel items
541  for ( YItemIterator it = itemsBegin(); it < itemsEnd(); ++it )
542  {
543  CreateTreeLines( 0, myPad(), *it );
544  }
545 
546  idx = 0;
547  NCPadWidget::DrawPad();
548 }
549 
550 
551 
552 NCursesEvent NCTree::wHandleInput( wint_t key )
553 {
554  NCursesEvent ret = NCursesEvent::none;
555  YTreeItem * oldCurrentItem = getCurrentItem();
556 
557  bool handled = handleInput( key ); // NCTreePad::handleInput()
558  const YItem * currentItem = getCurrentItem();
559 
560  if ( !currentItem )
561  return ret;
562 
563  if ( multiSel )
564  {
565  if ( ! handled )
566  {
567  switch ( key )
568  {
569  // KEY_SPACE is handled in NCTreeLine::handleInput
570  case KEY_RETURN:
571 
572  if ( currentItem->selected() )
573  selectItem( const_cast<YItem *>(currentItem), false );
574  else
575  selectItem( const_cast<YItem *>(currentItem), true );
576 
577  if ( notify() )
578  {
579  return NCursesEvent::ValueChanged;
580  }
581  break;
582  }
583  }
584  }
585  else
586  {
587  if ( ! handled )
588  {
589  switch ( key )
590  {
591  // KEY_SPACE is handled in NCTreeLine::handleInput
592  case KEY_RETURN:
593 
594  if ( notify() )
595  {
596  return NCursesEvent::Activated;
597  }
598  break;
599  }
600  }
601 
602  YTree::selectItem( const_cast<YItem *>( currentItem ), true );
603  }
604 
605  if ( notify() && immediateMode() && ( oldCurrentItem != currentItem ) )
606  ret = NCursesEvent::SelectionChanged;
607 
608  yuiDebug() << "Notify: " << ( notify() ? "true" : "false" ) <<
609  " Return event: " << ret.reason << std::endl;
610 
611  return ret;
612 }
613 
614 
616 {
617  // send an activation event for this widget
618  NCursesEvent event = NCursesEvent::Activated;
619  event.widget = this;
620  YNCursesUI::ui()->sendEvent(event);
621 }
622 
623 
624 // clears the table and the lists holding
625 // the values
626 void NCTree::deleteAllItems()
627 {
628  YTree::deleteAllItems();
629  myPad()->ClearTable();
630 }
Definition: NCPad.h:94
virtual void activate()
Activate the item selected in the tree.
Definition: NCTree.cc:615
virtual void setEnabled(bool do_bv)
Pure virtual to make sure every widget implements it.
Definition: NCTree.cc:324
virtual NCTreePad * myPad() const
Return the current pad.
Definition: NCTree.h:53
virtual void setEnabled(bool do_bv)=0
Pure virtual to make sure every widget implements it.
Definition: NCWidget.cc:391
C++ class for windows.
Definition: ncursesw.h:904
int bkgd(const chtype ch)
Set the background property and apply it to the window.
Definition: ncursesw.h:1442
int move(int y, int x)
Move cursor the this position.
Definition: ncursesw.h:1154
int addch(const char ch)
Put attributed character to the window.
Definition: ncursesw.h:1227
void bkgdset(chtype ch)
Set the background property.
Definition: ncursesw.h:1447
void sendEvent(NCursesEvent event)
Send an event to the UI.
Definition: YNCursesUI.cc:455
static YNCursesUI * ui()
Access the global Y2NCursesUI.
Definition: YNCursesUI.h:93
Definition: position.h:110
Definition: position.h:155