Name

Tree — Scrollable tree selection

Synopsis

Tree ( string  label ,
  itemList  items );

Parameters

string label

Optional Arguments

itemList items

the items contained in the tree

              
	itemList ::= 
		[ 
			item 
			[ , item ] 
			[ , item ] 
			... 
		] 
	item ::= 
		string | 
		`item( 
			[ `id( string  ),] 
			string 
			[ , true | false ] 
			[ , itemList ] 
		)  

The boolean parameter inside `item() indicates whether or not the respective tree item should be opened by default - if it has any subitems and if the respective UI is capable of closing and opening subtrees. If the UI cannot handle this, all subtrees will always be open.

Properties

string Label

the label above the Tree

itemId CurrentItem

the currently selected item Alias: Value

YTreeItem list

Items The items that are displayed

OpenItems a

map of open items - can only be queried, not set

CurrentBranch a

list of the current item and all its ancestors from the root item to the current item- either the respective IDs or, for items that don't have IDs, the item text. If no item is currently selected, 'nil' is returned. This property can only be queried, not set.

Description

A tree widget provides a selection from a hierarchical tree structure. The semantics are very much like those of a SelectionBox. Unlike the SelectionBox, however, tree items may have subitems that in turn may have subitems etc.

Each item has a label string, optionally preceded by an ID. If the item has subitems, they are specified as a list of items after the string.

The tree widget will not perform any sorting on its own: The items are always sorted by insertion order. The application needs to handle sorting itself, if desired.

Note: The Qt version of the Wizard widget also provides a built-in tree with an API that is (sometimes) easier to use.

Usage

 	`Tree( `id( `treeID ), "treeLabel", [ "top1", "top2", "top3" ] );

Examples

          {
    UI::OpenDialog(
	       `VBox(
		     `Tree(`id(`dest_dir),
				   "Select destination directory:",
				   [
				    `item(`id(`root), "/" , true,
					  [
					   `item(`id(`etc), "etc",
						 [
						  `item("opt"),
						  `item("SuSEconfig"),
						  `item("X11")
						 ]
						 ),
					   `item("usr", false, 
						 [
						  "bin",
						  "lib",
						  `item("share",
						       [
							"man",
							"info",
							"emacs"
							]
						       ),
						  `item(`id(`usr_local),"local"),
						  `item("X11R6",
							[
							 "bin",
							 "lib",
							 "share",
							 "man",
							 "etc"
							 ]
							)
						  ]
						 ),
					   `item(`id(`opt), "opt", true,
						 [
						  "kde",
						  "netscape",
						  "Office51"
						  ]
						 ),
					   `item("home"),
					   "work",
					   `item(`id(`other), "<other>")
					   ]
					  )
				   ] ),
		     `HBox(
			   `PushButton(`id(`sel_opt),		`opt(`hstretch), "/&opt" ),
			   `PushButton(`id(`sel_usr),		`opt(`hstretch), "/&usr" ),
			   `PushButton(`id(`sel_usr_local),	`opt(`hstretch), "/usr/&local" )
			   ),
		     `PushButton(`id(`ok), `opt(`default), "&OK")
		     )
	       );

    any id = nil;

    repeat
	{
	    id = UI::UserInput();

	    if      ( id == `sel_usr)		UI::ChangeWidget(`dest_dir, `CurrentItem, "usr"  );
	    else if ( id == `sel_usr_local)	UI::ChangeWidget(`dest_dir, `CurrentItem, `usr_local );
	    else if ( id == `sel_opt) 		UI::ChangeWidget(`dest_dir, `CurrentItem, `opt );
	} until ( id == `ok );

    // Get the input from the tree.
    //
    // Notice: The return value of UI::UserInput() does NOT return this value!
    // Rather, it returns the ID of the widget (normally the PushButton)
    // that caused UI::UserInput() to return.
    string dest_dir = (string) UI::QueryWidget(`dest_dir, `CurrentItem);

    if ( dest_dir == nil )
	dest_dir = "";

    // Close the dialog.
    // Remember to read values from the dialog's widgets BEFORE closing it!
    UI::CloseDialog();


    // Pop up a new dialog to echo the selection.
    UI::OpenDialog(
	       `VBox(
		     `Label("Selected destination directory: " + dest_dir),
		     `PushButton(`opt(`default), "&OK")
		     )
	       );
    UI::UserInput();

    UI::CloseDialog();
}

        
          {
    // Build a dialog with a tree for directory selection, three
    // buttons with common values and a label that directly echoes any
    // selected directory.
    //
    // The tree in this example uses the `notify option that makes
    // UI::UserInput() return immediately as soon as the user selects a
    // tree item rather than the default behaviour which waits for the
    // user to activate a button.
    
    UI::OpenDialog(
	       `VBox(
		     `Tree(`id(`dest_dir),
			   `opt(`notify),
				   "Select destination directory:",
				   [
				    `item(`id(`root), "/" , true,
					  [
					   `item(`id(`etc), "etc",
						 [
						  `item("opt"),
						  `item("SuSEconfig"),
						  `item("X11")
						 ]
						 ),
					   `item("usr", false, 
						 [
						  "bin",
						  "lib",
						  `item("share",
						       [
							"man",
							"info",
							"emacs"
							]
						       ),
						  `item(`id(`usr_local),"local"),
						  `item("X11R6",
							[
							 "bin",
							 "lib",
							 "share",
							 "man",
							 "etc"
							 ]
							)
						  ]
						 ),
					   `item(`id(`opt), "opt", true,
						 [
						  "kde",
						  "netscape",
						  "Office51"
						  ]
						 ),
					   `item("home"),
					   "work",
					   `item(`id(`other), "<other>")
					   ]
					  )
				   ] ),
		     `HBox(
			   `PushButton(`id(`sel_opt),		`opt(`hstretch), "/&opt" ),
			   `PushButton(`id(`sel_usr),		`opt(`hstretch), "/&usr" ),
			   `PushButton(`id(`sel_usr_local),	`opt(`hstretch), "/usr/&local" )
			   ),
		     `HBox(
			   `Label("Current Value:"),
			   `Label(`id(`echo), `opt(`outputField, `hstretch), "?????????")
			   ),
		     `PushButton(`id(`ok), `opt(`default), "&OK")
		     )
	       );

    any id = nil;

    repeat
	{
	    id = UI::UserInput();

	    if      ( id == `sel_usr)		UI::ChangeWidget(`dest_dir, `CurrentItem, "usr"  );
	    else if ( id == `sel_usr_local)	UI::ChangeWidget(`dest_dir, `CurrentItem, `usr_local );
	    else if ( id == `sel_opt) 		UI::ChangeWidget(`dest_dir, `CurrentItem, `opt );
	    else if ( id == `dest_dir)
	    {
		UI::ChangeWidget(`echo, `Value, "");
		any current_dir = UI::QueryWidget(`dest_dir, `CurrentItem);

		if ( current_dir != nil )
		    UI::ChangeWidget(`echo, `Value, sformat( "%1", current_dir ) );
	    }

	    y2milestone( "Items:\n%1", UI::QueryWidget(`dest_dir, `Items ) );
	    y2milestone( "OpenItems:\n%1", UI::QueryWidget(`dest_dir, `OpenItems ) );
	} until ( id == `ok );


    // Close the dialog.
    // Remember to read values from the dialog's widgets BEFORE closing it!
    UI::CloseDialog();

}

        
          {
    // Build a dialog with a tree for directory selection, three
    // buttons with common values and a label that directly echoes any
    // selected directory.
    //
    // The tree in this example uses the `notify option that makes
    // UI::UserInput() return immediately as soon as the user selects a
    // tree item rather than the default behaviour which waits for the
    // user to activate a button.
    
    UI::OpenDialog(
		   `MinWidth( 50,
			      `VBox(
				    `Tree(`id(`dest_dir),
					  `opt(`notify),
					  "Select destination directory:",
					  [
					   `item( "/" , true,
						  [
						   `item( "etc",
							  [
							   `item("opt"),
							   `item("SuSEconfig"),
							   `item("X11")
							   ]
							  ),
						   `item( "usr", false, 
							  [
							   "bin",
							   "lib",
							   `item("share",
								 [
								  "man",
								  "info",
								  "emacs"
								  ]
								 ),
							   `item( "local" ),
							   `item("X11R6",
								 [
								  "bin",
								  "lib",
								  "share",
								  "man",
								  "etc"
								  ]
								 )
							   ]
							  ),
						   `item( "opt", true,
							  [
							   "kde",
							   "netscape",
							   "Office51"
							   ]
							  ),
						   `item("home"),
						   "work",
						   `item( "<other>")
						   ]
						  )
					   ] ),
				    `HBox(
					  `PushButton(`id(`sel_opt),		`opt(`hstretch), "/&opt" ),
					  `PushButton(`id(`sel_usr),		`opt(`hstretch), "/&usr" ),
					  `PushButton(`id(`sel_usr_local),	`opt(`hstretch), "/usr/&local" )
					  ),
				    `HBox(
					  `HWeight( 2, `Label("Current Item:") ),
					  `HWeight( 5, `Label(`id(`echoItem), `opt(`outputField, `hstretch), "") )
					  ),
				    `HBox(
					  `HWeight( 2, `Label("Current Branch:") ),
					  `HWeight( 5, `Label(`id(`echoBranch), `opt(`outputField, `hstretch), "") )
					  ),
				    `HBox(
					  `HWeight( 2, `Label("Current Path:") ),
					  `HWeight( 5, `Label(`id(`echoPath), `opt(`outputField, `hstretch), "") )
					  ),
				    `PushButton(`id(`ok), `opt(`default), "&OK")
				    )
			      )
		   );

    any id = nil;

    repeat
	{
	    id = UI::UserInput();

	    if      ( id == `sel_usr)		UI::ChangeWidget( `id( `dest_dir ), `CurrentItem, "usr"  );
	    else if ( id == `sel_usr_local)	UI::ChangeWidget( `id( `dest_dir ), `CurrentItem, `usr_local );
	    else if ( id == `sel_opt) 		UI::ChangeWidget( `id( `dest_dir ), `CurrentItem, `opt );
	    else if ( id == `dest_dir)
	    {
		any current_dir = UI::QueryWidget(`dest_dir, `CurrentItem );

		if ( current_dir != nil )
		    UI::ChangeWidget( `id( `echoItem ), `Value, sformat( "%1", current_dir ) );

		list<string> current_branch = (list<string>) UI::QueryWidget(`dest_dir, `CurrentBranch );

		if ( current_branch != nil )
		{
		    UI::ChangeWidget(`echoBranch, `Value, sformat( "%1", current_branch) );

		    string current_path = mergestring( current_branch, "/" );
		    if ( size( current_path ) > 2 )
			// Remove duplicate "/" at start
			current_path = substring( current_path, 1, size( current_path )-1 );
		    
		    UI::ChangeWidget(`echoPath, `Value, sformat( "%1", current_path ) );
		}
	    }
	    
	} until ( id == `ok );


    // Close the dialog.
    // Remember to read values from the dialog's widgets BEFORE closing it!
    UI::CloseDialog();

}

        
          // Tree with icons
{
    UI::OpenDialog(
               `VBox(
                     `Heading( "YaST2 Mini Control Center" ),
                     `Tree(`id(`mod),
			   "Modules",
			   [
			    `item(`id( "country" ), `icon( "yast-yast-language.png" ), "Localization", true,
				  [
				   `item(`id( "keyboard"   ), `icon( "yast-keyboard.png"   ), "Keyboard"   ),
				   `item(`id( "timezone"   ), `icon( "yast-timezone.png"   ), "Time zone"  )
				   ]
				  ),
			    `item(`id( "mouse"      ), `icon( "yast-mouse.png"      ), "Mouse"      ),
			    `item(`id( "lan"        ), `icon( "yast-lan.png"        ), "Network"    ),
			    `item(`id( "sw_single"  ), `icon( "yast-software.png"   ), "Software"   )
			    ] ),
                     `PushButton(`id(`ok), `opt(`default), "&OK")
                     )
               );

    UI::UserInput();
    UI::CloseDialog();
}

        
          // Example showing how to replace Tree items
{

    list pizza_list =
	[
	 "Pizza Napoli",
	 "Pizza Funghi",
	 "Pizza Salami",
	 "Pizza Hawaii"
	 ];

    list pasta_list =
	[
	 "Spaghetti",
	 "Rigatoni",
	 "Tortellini"
	 ];

    list veggie_toppings =
	[
	 "Cheese",
	 "Mushrooms",
	 "Pepperoni",
	 "Rucola",
	 "Tomatoes"
	 ];

    list meat_toppings =
	[
	 "Ham",
	 "Salami",
	 "Tuna"
	 ];

    list menu =	[
	`item( `id ( `pizza_branch ), "Pizza", true,
	       pizza_list ),
	`item( `id ( `pasta_branch ), "Pasta", true,
	       pasta_list)
	];

    list toppings = [
	`item( `id ( `meat_branch ), "Meat", true,
	       meat_toppings ),
	`item( `id ( `veggie_branch ), "Veggie", true,
	       veggie_toppings)
	];
		

    UI::OpenDialog( `VBox(
			  `Tree(`id(`listing), "Daily &Specials:", menu
			      ),
			  `HBox(
				`PushButton(`id(`menu), "&Menu" ),
				`PushButton(`id(`toppings), "&Toppings" )
				),
			  `PushButton(`id(`ok), "&OK" )
			  )
		    );

    symbol button = nil;

    do
    {
	button = (symbol) UI::UserInput();

	if ( button == `menu )      UI::ChangeWidget(`listing, `Items, menu);
	if ( button == `toppings )  UI::ChangeWidget(`listing, `Items, toppings );
	
	
    } while ( button != `ok );

    string order = (string) UI::QueryWidget(`listing, `CurrentItem );
    UI::CloseDialog();


    //
    // Show the result
    //
    
    UI::OpenDialog(`VBox(
			 `Label( sformat( "Your order: %1", order ) ),
			 `PushButton(`opt(`default), "&OK" )
			 )
		   );
    UI::UserInput();
    UI::CloseDialog();
}

        
          // Advanced example of using the Wizard widget.
//
// Note: YCP applications are discouraged from using the Wizard widget directly.
// Use the Wizard module instead.

{
    if ( ! UI::HasSpecialWidget(`Wizard) )
    {
	y2error( "This works only with UIs that provide the wizard widget!" );
	return;
    }

    string help_text =
	"<p>This is a help text.</p>"
	+ "<p>It should be helpful.</p>"
	+ "<p>If it isn't helpful, it should rather not be called a <i>help text</i>.</p>";

    UI::OpenDialog(`opt(`defaultsize ),
		   `Wizard(`opt(`treeEnabled),
			   `back,  "&Back",
			   `abort, "Ab&ort",
			   `next,  "&Next" ) );
    
    // UI::DumpWidgetTree();

    // UI::WizardCommand(`SetVerboseCommands( true ) );
    
    UI::WizardCommand(`SetDialogIcon( "/usr/share/YaST2/theme/SuSELinux/icons/22x22/apps/user_add.png" ) );
    UI::WizardCommand(`SetDialogHeading( "Welcome to the YaST2 installation" ) );
    UI::WizardCommand(`SetHelpText( help_text ) );


    UI::WizardCommand(`AddTreeItem( "", "First Toplevel Item" , "tl1" ) );
    UI::WizardCommand(`AddTreeItem( "", "Second Toplevel Item", "tl2" ) );
    UI::WizardCommand(`AddTreeItem( "", "Third Toplevel Item" , "tl3" ) );
		      
    UI::WizardCommand(`AddTreeItem( "tl1", "First Sublevel"  , "1-1" ) );
    UI::WizardCommand(`AddTreeItem( "tl1", "Second Sublevel" , "1-2" ) );
    UI::WizardCommand(`AddTreeItem( "tl1", "Third Sublevel"  , "1-3" ) );

    UI::WizardCommand(`AddTreeItem( "tl2", "First Sublevel"  , "2-1" ) );
    UI::WizardCommand(`AddTreeItem( "tl2", "Second Sublevel" , "2-2" ) );
    UI::WizardCommand(`AddTreeItem( "tl2", "Third Sublevel"  , "2-3" ) );
    
    UI::WizardCommand(`AddTreeItem( "1-2", "First 3rd level "  , "3rd 1" ) );
    UI::WizardCommand(`AddTreeItem( "1-2", "Second 3rd level " , "3rd 2" ) );
    UI::WizardCommand(`AddTreeItem( "1-2", "Item without ID"   , "" ) );

    UI::WizardCommand(`SelectTreeItem( "3rd 1" ) );


    
    UI::WizardCommand(`AddMenu( "&File",	"file-menu" ) );
    UI::WizardCommand(`AddMenu( "&Edit",	"edit-menu" ) );
    UI::WizardCommand(`AddMenu( "&Options",	"opt-menu"  ) );
    
    UI::WizardCommand(`AddMenuEntry	( "file-menu", "&New", 		"file-new" 	) );
    UI::WizardCommand(`AddMenuEntry	( "file-menu", "&Open", 	"file-open" 	) );
    UI::WizardCommand(`AddSubMenu	( "file-menu", "Open &Recent", 	"file-recent"  	) );
    UI::WizardCommand(`AddMenuEntry	( "file-menu", "&Save", 	"file-save" 	) );
    UI::WizardCommand(`AddMenuEntry	( "file-menu", "Save &As", 	"file-save-as" 	) );
		      
    UI::WizardCommand(`AddMenuEntry	( "file-recent", "/tmp/test1", 	"recent-test1" 	) );
    UI::WizardCommand(`AddMenuEntry	( "file-recent", "/tmp/test2", 	"recent-test2" 	) );
		      
    UI::WizardCommand(`AddMenuEntry	( "edit-menu", "C&ut",		"edit-cut"	) );
    UI::WizardCommand(`AddMenuEntry	( "edit-menu", "C&opy",		"edit-copy"	) );
    UI::WizardCommand(`AddMenuEntry	( "edit-menu", "&Paste",	"edit-paste"	) );
		      
    UI::WizardCommand(`AddMenuEntry	( "opt-menu", 	"&Settings",	"opt-settings"	) );
    UI::WizardCommand(`AddMenuSeparator	( "opt-menu" ) );
    UI::WizardCommand(`AddMenuEntry	( "opt-menu", 	"Activate &Hypersonic Transducer", "frank-n-furter" ) );

    

    while ( true )
    {
	map event = UI::WaitForEvent();

	y2milestone( "Got event: %1", event );

	if ( event[ "ID" ]:nil == `abort )
	    break;

	y2milestone( "Tree selection: %1", UI::QueryWidget(`id(`wizard), `CurrentItem ) );
    }

    UI::CloseDialog();

}