Name

Tree — Scrollable tree selection

Synopsis

Tree ( string label ,
  itemList items );
 

Parameters

string label

Options

immediate

make `notify trigger immediately when the selected item changes

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.

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

          // Simple tree example
{
    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.
    any dest_dir = UI::QueryWidget(`dest_dir, `CurrentItem);
    y2debug( "Selected: %1", dest_dir );


    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( sformat( "Selected destination directory: %1", 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(
		     `MinHeight( 14,
				 `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" ),
			   `PushButton(`id(`none ),		`opt(`hstretch), "none"  )
			   ),
		     `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 == `none 	) 	UI::ChangeWidget(`dest_dir, `CurrentItem, nil );

	    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: %1", UI::QueryWidget(`dest_dir, `OpenItems ) );
	    y2milestone( "Current Branch: %1", UI::QueryWidget(`dest_dir, `CurrentBranch ) );
	} 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, "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(`empty), "&None" )
				),
			  `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 );
	if ( button == `empty )	    UI::ChangeWidget(`listing, `Items, [] );
	
	
    } 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(`SetDialogIcon( "/usr/share/YaST2/theme/current/icons/22x22/apps/YaST.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();

}