libyui-ncurses
2.44.1
|
00001 /* 00002 Copyright (C) 2000-2012 Novell, Inc 00003 This library is free software; you can redistribute it and/or modify 00004 it under the terms of the GNU Lesser General Public License as 00005 published by the Free Software Foundation; either version 2.1 of the 00006 License, or (at your option) version 3.0 of the License. This library 00007 is distributed in the hope that it will be useful, but WITHOUT ANY 00008 WARRANTY; without even the implied warranty of MERCHANTABILITY or 00009 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 00010 License for more details. You should have received a copy of the GNU 00011 Lesser General Public License along with this library; if not, write 00012 to the Free Software Foundation, Inc., 51 Franklin Street, Fifth 00013 Floor, Boston, MA 02110-1301 USA 00014 */ 00015 00016 00017 /*-/ 00018 00019 File: NCTree.cc 00020 00021 Author: Michael Andres <ma@suse.de> 00022 00023 /-*/ 00024 00025 #define YUILogComponent "ncurses" 00026 #include <yui/YUILog.h> 00027 #include "NCTree.h" 00028 00029 #include <yui/TreeItem.h> 00030 #include <yui/YSelectionWidget.h> 00031 00032 00033 class NCTreeLine : public NCTableLine 00034 { 00035 00036 private: 00037 00038 YTreeItem * yitem; 00039 const unsigned level; 00040 00041 NCTreeLine * parent; 00042 NCTreeLine * nsibling; 00043 NCTreeLine * fchild; 00044 00045 mutable chtype * prefix; 00046 bool multiSel; 00047 unsigned prefixLen() const { return level + 3; } 00048 00049 public: 00050 00051 NCTreeLine( NCTreeLine * p, YTreeItem * item, bool multiSelection ) 00052 : NCTableLine( 0 ) 00053 , yitem( item ) 00054 , level( p ? p->level + 1 : 0 ) 00055 , parent( p ) 00056 , nsibling( 0 ) 00057 , fchild( 0 ) 00058 , prefix( 0 ) 00059 , multiSel( multiSelection ) 00060 { 00061 if ( parent ) 00062 { 00063 if ( parent->fchild ) 00064 { 00065 NCTreeLine * s = parent->fchild; 00066 00067 for ( ; s->nsibling; s = s->nsibling ) 00068 ; 00069 00070 s->nsibling = this; 00071 } 00072 else 00073 { 00074 parent->fchild = this; 00075 } 00076 00077 if ( !parent->yitem->isOpen() ) 00078 { 00079 SetState( S_HIDDEN ); 00080 } 00081 } 00082 00083 if ( !multiSel ) 00084 { 00085 Append( new NCTableCol( NCstring( std::string( prefixLen(), ' ' ) 00086 + yitem->label() ) ) ); 00087 } 00088 else 00089 { 00090 Append( new NCTableCol( NCstring( std::string( prefixLen(), ' ' ) + "[ ] " 00091 + yitem->label() ) ) ); 00092 } 00093 } 00094 00095 virtual ~NCTreeLine() { delete [] prefix; } 00096 00097 public: 00098 00099 YTreeItem * YItem() const { return yitem; } 00100 00101 unsigned Level() const { return level; } 00102 00103 virtual bool isVisible() const 00104 { 00105 return !parent || ( !isHidden() && parent->isVisible() ); 00106 } 00107 00108 00109 virtual int ChangeToVisible() 00110 { 00111 if ( isVisible() ) 00112 return 0; 00113 00114 if ( parent ) 00115 { 00116 parent->ChangeToVisible(); 00117 00118 for ( NCTreeLine * c = parent->fchild; c; c = c->nsibling ) 00119 { 00120 c->ClearState( S_HIDDEN ); 00121 c->YItem()->setOpen( true ); 00122 } 00123 } 00124 else 00125 { 00126 ClearState( S_HIDDEN ); 00127 yitem->setOpen( true ); 00128 } 00129 00130 return 1; 00131 } 00132 00133 00134 virtual unsigned Hotspot( unsigned & at ) const 00135 { 00136 at = Level(); 00137 return 6; 00138 } 00139 00140 00141 virtual int handleInput( wint_t key ) 00142 { 00143 if ( !fchild ) 00144 return 0; 00145 00146 switch ( key ) 00147 { 00148 case KEY_IC: 00149 case '+': 00150 if ( fchild->isVisible() ) 00151 return 0; 00152 00153 break; 00154 00155 case KEY_DC: 00156 case '-': 00157 if ( !fchild->isVisible() ) 00158 return 0; 00159 00160 break; 00161 00162 case KEY_SPACE: 00163 // case KEY_RETURN: see bug 67350 00164 00165 break; 00166 00167 default: 00168 return 0; 00169 00170 break; 00171 } 00172 00173 if ( fchild->isVisible() ) 00174 { 00175 yitem->setOpen( false ); 00176 yuiMilestone() << "Closing item " << yitem->label() << std::endl; 00177 00178 for ( NCTreeLine * c = fchild; c; c = c->nsibling ) 00179 c->SetState( S_HIDDEN ); 00180 } 00181 else 00182 { 00183 yitem->setOpen( true ); 00184 yuiMilestone() << "Opening item " << yitem->label() << std::endl; 00185 00186 for ( NCTreeLine * c = fchild; c; c = c->nsibling ) 00187 c->ClearState( S_HIDDEN ); 00188 } 00189 00190 return 1; 00191 } 00192 00193 00194 virtual void DrawAt( NCursesWindow & w, const wrect at, 00195 NCTableStyle & tableStyle, 00196 bool active ) const 00197 { 00198 00199 NCTableLine::DrawAt( w, at, tableStyle, active ); 00200 00201 if ( !isSpecial() ) 00202 w.bkgdset( tableStyle.getBG( vstate, NCTableCol::SEPARATOR ) ); 00203 00204 if ( ! prefix ) 00205 { 00206 prefix = new chtype[prefixLen()]; 00207 chtype * tagend = &prefix[prefixLen()-1]; 00208 *tagend-- = ACS_HLINE; 00209 *tagend-- = fchild ? ACS_TTEE : ACS_HLINE; 00210 00211 if ( parent ) 00212 { 00213 *tagend-- = nsibling ? ACS_LTEE : ACS_LLCORNER; 00214 00215 for ( NCTreeLine * p = parent; p; p = p->parent ) 00216 { 00217 *tagend-- = p->nsibling ? ACS_VLINE : ( ' '&A_CHARTEXT ); 00218 } 00219 } 00220 else 00221 { 00222 *tagend-- = ACS_HLINE; 00223 } 00224 } 00225 00226 w.move( at.Pos.L, at.Pos.C ); 00227 00228 unsigned i = 0; 00229 00230 for ( ; i < prefixLen(); ++i ) 00231 w.addch( prefix[i] ); 00232 00233 w.move( at.Pos.L, at.Pos.C + prefixLen() - 2 ); 00234 00235 if ( fchild && !isSpecial() ) 00236 w.bkgdset( tableStyle.highlightBG( vstate, NCTableCol::HINT, 00237 NCTableCol::SEPARATOR ) ); 00238 00239 if ( fchild && !fchild->isVisible() ) 00240 w.addch( '+' ); 00241 else 00242 w.addch( prefix[prefixLen() - 2] ); 00243 } 00244 }; 00245 00246 00247 00248 00249 00250 00251 NCTree::NCTree( YWidget * parent, const std::string & nlabel, bool multiselection, bool recursiveselection ) 00252 : YTree( parent, nlabel, multiselection, recursiveselection ) 00253 , NCPadWidget( parent ) 00254 , multiSel ( multiselection ) 00255 { 00256 yuiDebug() << std::endl; 00257 00258 if ( multiselection && recursiveselection ) 00259 yuiMilestone() << "NCTree recursive multi selection ON" << std::endl; 00260 else if ( multiselection ) 00261 yuiMilestone() << "NCTree multi selection ON" << std::endl; 00262 00263 setLabel( nlabel ); 00264 } 00265 00266 00267 00268 NCTree::~NCTree() 00269 { 00270 yuiDebug() << std::endl; 00271 } 00272 00273 00274 00275 00276 // Return pointer to tree line at given index 00277 inline const NCTreeLine * NCTree::getTreeLine( unsigned idx ) const 00278 { 00279 if ( myPad() ) 00280 return dynamic_cast<const NCTreeLine *>( myPad()->GetLine( idx ) ); 00281 else 00282 return 0; 00283 } 00284 00285 00286 00287 00288 // Modify tree line at given index 00289 inline NCTreeLine * NCTree::modifyTreeLine( unsigned idx ) 00290 { 00291 if ( myPad() ) 00292 { 00293 return dynamic_cast<NCTreeLine *>( myPad()->ModifyLine( idx ) ); 00294 } 00295 00296 return 0; 00297 } 00298 00299 00300 00301 00302 // Set preferred width 00303 int NCTree::preferredWidth() 00304 { 00305 wsze sze = wsze::max( defsze, wsze( 0, labelWidth() + 2 ) ); 00306 return sze.W; 00307 } 00308 00309 00310 00311 00312 // Set preferred height 00313 int NCTree::preferredHeight() 00314 { 00315 wsze sze = wsze::max( defsze, wsze( 0, labelWidth() + 2 ) ); 00316 return sze.H; 00317 } 00318 00319 00320 00321 00322 // Enable/disable widget 00323 void NCTree::setEnabled( bool do_bv ) 00324 { 00325 NCWidget::setEnabled( do_bv ); 00326 YWidget::setEnabled( do_bv ); 00327 } 00328 00329 00330 00331 00332 void NCTree::setSize( int newwidth, int newheight ) 00333 { 00334 wRelocate( wpos( 0 ), wsze( newheight, newwidth ) ); 00335 } 00336 00337 00338 00339 00340 // Return YTreeItem pointer for a current line 00341 // (under the cursor) 00342 YTreeItem * NCTree::getCurrentItem() const 00343 { 00344 YTreeItem * yitem = 0; 00345 00346 if ( myPad() ) 00347 { 00348 const NCTreeLine * cline = dynamic_cast<const NCTreeLine *>( myPad()->GetCurrentLine() ); 00349 00350 if ( cline ) 00351 yitem = cline->YItem(); 00352 } 00353 00354 yuiDebug() << "-> " << ( yitem ? yitem->label().c_str() : "noitem" ) << std::endl; 00355 00356 return yitem; 00357 } 00358 00359 void NCTree::deselectAllItems() 00360 { 00361 if ( multiSel) 00362 { 00363 YItemCollection selectedItems = YTree::selectedItems(); 00364 00365 for ( YItemConstIterator it = selectedItems.begin(); it != selectedItems.end(); ++it ) 00366 { 00367 selectItem( *it, false ); 00368 } 00369 } 00370 00371 YTree::deselectAllItems(); 00372 } 00373 00374 00375 // Set current item (under the cursor) to selected 00376 void NCTree::selectItem( YItem *item, bool selected ) 00377 { 00378 if ( !myPad() ) 00379 return; 00380 00381 YTreeItem * treeItem = dynamic_cast<YTreeItem *>( item ); 00382 YUI_CHECK_PTR( treeItem ); 00383 YTreeItem *citem = getCurrentItem(); 00384 00385 //retrieve position of item 00386 int at = treeItem->index(); 00387 00388 NCTreeLine * cline = 0; // current line 00389 NCTableCol * ccol = 0; // current column 00390 00391 if ( multiSel ) 00392 { 00393 cline = modifyTreeLine( at ); 00394 if ( cline ) 00395 { 00396 ccol = cline->GetCol(0); 00397 } 00398 } 00399 00400 if ( !selected ) 00401 { 00402 if ( !multiSel && (treeItem == citem) ) 00403 { 00404 YTree::deselectAllItems(); 00405 } 00406 else 00407 { 00408 YTree::selectItem ( treeItem, false ); 00409 if ( ccol ) 00410 { 00411 ccol->SetLabel( NCstring( std::string( cline->Level() + 3, ' ' ) + "[ ] " 00412 + item->label() ) ); 00413 } 00414 } 00415 } 00416 else 00417 { 00418 YTree::selectItem( treeItem, selected ); 00419 00420 if ( multiSel && ccol ) 00421 { 00422 ccol->SetLabel( NCstring( std::string( cline->Level() + 3, ' ' ) + "[x] " 00423 + item->label() ) ); 00424 } 00425 00426 //this highlights selected item, possibly unpacks the tree 00427 //should it be in currently hidden branch 00428 myPad()->ShowItem( getTreeLine( at ) ); 00429 } 00430 } 00431 00432 00433 00434 00435 // Set current item (at given index) to selected 00436 // (overloaded for convenience) 00437 void NCTree::selectItem( int index ) 00438 { 00439 YItem * item = YTree::itemAt( index ); 00440 00441 if ( item ) 00442 { 00443 selectItem( item, true ); 00444 } 00445 else 00446 YUI_THROW( YUIException( "Can't find selected item" ) ); 00447 } 00448 00449 00450 00451 void NCTree::setLabel( const std::string & nlabel ) 00452 { 00453 YTree::setLabel( nlabel ); 00454 NCPadWidget::setLabel( NCstring( nlabel ) ); 00455 } 00456 00457 00458 00459 void NCTree::rebuildTree() 00460 { 00461 DelPad(); 00462 Redraw(); 00463 } 00464 00465 00466 00467 00468 // Creates empty pad 00469 NCPad * NCTree::CreatePad() 00470 { 00471 wsze psze( defPadSze() ); 00472 NCPad * npad = new NCTreePad( psze.H, psze.W, *this ); 00473 npad->bkgd( listStyle().item.plain ); 00474 return npad; 00475 } 00476 00477 00478 // Creates tree lines and appends them to TreePad 00479 // (called recursively for each child of an item) 00480 void NCTree::CreateTreeLines( NCTreeLine * parentLine, NCTreePad * pad, YItem * item ) 00481 { 00482 //set item index explicitely, it is set to -1 by default 00483 //which makes selecting items painful 00484 item->setIndex( idx++ ); 00485 00486 YTreeItem * treeItem = dynamic_cast<YTreeItem *>( item ); 00487 YUI_CHECK_PTR( treeItem ); 00488 00489 NCTreeLine * line = new NCTreeLine( parentLine, treeItem, multiSel ); 00490 pad->Append( line ); 00491 00492 // iterate over children 00493 00494 for ( YItemIterator it = item->childrenBegin(); it < item->childrenEnd(); ++it ) 00495 { 00496 CreateTreeLines( line, pad, *it ); 00497 } 00498 } 00499 00500 // Returns current item (pure virtual in YTree) 00501 YTreeItem * NCTree::currentItem() 00502 { 00503 return getCurrentItem(); 00504 } 00505 00506 // Fills TreePad with lines (uses CreateTreeLines to create them) 00507 void NCTree::DrawPad() 00508 { 00509 if ( !myPad() ) 00510 { 00511 yuiWarning() << "PadWidget not yet created" << std::endl; 00512 return; 00513 } 00514 00515 idx = 0; 00516 // YItemIterator iterates over the toplevel items 00517 for ( YItemIterator it = itemsBegin(); it < itemsEnd(); ++it ) 00518 { 00519 CreateTreeLines( 0, myPad(), *it ); 00520 } 00521 00522 idx = 0; 00523 NCPadWidget::DrawPad(); 00524 } 00525 00526 00527 00528 NCursesEvent NCTree::wHandleInput( wint_t key ) 00529 { 00530 NCursesEvent ret = NCursesEvent::none; 00531 YTreeItem * oldCurrentItem = getCurrentItem(); 00532 00533 bool handled = handleInput( key ); // NCTreePad::handleInput() 00534 const YItem * currentItem = getCurrentItem(); 00535 00536 if ( !currentItem ) 00537 return ret; 00538 00539 if ( multiSel ) 00540 { 00541 if ( ! handled ) 00542 { 00543 switch ( key ) 00544 { 00545 // KEY_SPACE is handled in NCTreeLine::handleInput 00546 case KEY_RETURN: 00547 00548 if ( currentItem->selected() ) 00549 selectItem( const_cast<YItem *>(currentItem), false ); 00550 else 00551 selectItem( const_cast<YItem *>(currentItem), true ); 00552 00553 if ( notify() ) 00554 { 00555 return NCursesEvent::ValueChanged; 00556 } 00557 break; 00558 } 00559 } 00560 } 00561 else 00562 { 00563 if ( ! handled ) 00564 { 00565 switch ( key ) 00566 { 00567 // KEY_SPACE is handled in NCTreeLine::handleInput 00568 case KEY_RETURN: 00569 00570 if ( notify() ) 00571 { 00572 return NCursesEvent::Activated; 00573 } 00574 break; 00575 } 00576 } 00577 00578 YTree::selectItem( const_cast<YItem *>( currentItem ), true ); 00579 } 00580 00581 if ( notify() && immediateMode() && ( oldCurrentItem != currentItem ) ) 00582 ret = NCursesEvent::SelectionChanged; 00583 00584 yuiDebug() << "Notify: " << ( notify() ? "true" : "false" ) << 00585 " Return event: " << ret.reason << std::endl; 00586 00587 return ret; 00588 } 00589 00590 00591 00592 00593 00594 // clears the table and the lists holding 00595 // the values 00596 void NCTree::deleteAllItems() 00597 { 00598 YTree::deleteAllItems(); 00599 myPad()->ClearTable(); 00600 }