Re: Generic nested menus implementation
- To: mathgroup at smc.vnet.net
- Subject: [mg100658] Re: Generic nested menus implementation
- From: Albert Retey <awnl at gmx-topmail.de>
- Date: Wed, 10 Jun 2009 17:10:15 -0400 (EDT)
- References: <h0nums$bfl$1@smc.vnet.net>
Hi, I have played around with your initial idea also, and appended is what I came up with... hth, albert (* START OF CODE *) (* just a little helper *) ClearAll@toLabel; toLabel[s_String] := s; toLabel[s_String -> l_List] := s; toLabel[s_String :> _] := s; (* the heart of the code: recursive definition of the action menues *) ClearAll@createNextLevel; SetAttributes[createNextLevel, HoldFirst]; Options[createNextLevel] = Flatten[{ Options[ActionMenu], "AppearanceWrapper" -> Row, "DefaultLabels" -> {} }]; createNextLevel[{levels_, stack_}, label_String, action_, opts : OptionsPattern[]] := ( Do[levels[i] =., {i, Length[stack] + 1, Max[DownValues[levels][[All, 1, 1, 1]]]}]; action[stack] ); createNextLevel[{levels_, stack_}, label_String :> myaction_, action_, opts : OptionsPattern[]] := ( Do[levels[i] =., {i, Length[stack] + 1, Max[DownValues[levels][[All, 1, 1, 1]]]}]; myaction ); createNextLevel[{levels_, stack_}, label_String -> items_List, action_, opts : OptionsPattern[]] := With[{ defaultlabel = Function[ Which[ Length[#] >= Length[stack] + 1, #[[Length[stack] + 1]], Length[#] > 0, Last[#], True, "Choose" ] ][OptionValue["DefaultLabels"]] }, Do[levels[i] =., {i, Length[stack] + 1, Max[DownValues[levels][[All, 1, 1, 1]]]}]; levels[Length[stack]] = With[{i = Length[stack] + 1}, ActionMenu[ Dynamic[ If[Length[stack] >= i, stack[[i]], defaultlabel] ], Map[ toLabel[#] :> ( stack = Append[Take[stack, i - 1], toLabel[#]]; createNextLevel[{levels, stack}, #, action, opts]; ) &, items ], FilterRules[Flatten@{opts}, Options[ActionMenu]] ] ] ]; (* main function to call *) ClearAll@nestedActionMenu Options[nestedActionMenu] = Flatten[{ "AppearanceWrapper" -> Row, "DefaultLabels" -> {}, Options[ActionMenu] }]; nestedActionMenu[items_List, action_: None, opts : OptionsPattern[]] := With[{ defaultlabel = Function[If[Length[#] > 0, #[[1]], "Choose"]][ OptionValue["DefaultLabels"]] }, DynamicModule[{ levels, stack = {} }, levels[0] = ActionMenu[ Dynamic[If[Length[stack] > 0, stack[[1]], defaultlabel]], Map[ toLabel[#] :> ( stack = {toLabel[#]}; createNextLevel[{levels, stack}, #, action, opts]) &, items ], FilterRules[Flatten@{opts}, Options[ActionMenu]] ]; Column[{ Dynamic[OptionValue["AppearanceWrapper"][ Table[ levels[i], {i, 0, Max[DownValues[levels][[All, 1, 1, 1]]]}] ]] }] ] ]; (* END OF CODE *) (* simple example usage, with the default there are no actions! *) nestedActionMenu[{ "Africa" -> { "Algeria" -> {"Algiers", "Oran"}, "Angola" -> {"Luanda", "Huambo"} }, "North America" -> { "United States" -> {"New York", "Washington"}, "Canada" -> {"Toronto", "Montreal"} } } ] (* example with actions, they can be given globaly or locally for \ each entry *) Deploy@nestedActionMenu[{ "Africa" -> { "Algeria" -> {"Algiers", "Oran"}, "Angola" -> {"Luanda", "Huambo"} }, "North America" -> { "United States" -> {"New York", "Washington"}, "Canada" -> {"Toronto", "Montreal" :> Print["Monteral is special!"]} } }, Print, "DefaultLabels" -> {"Continent", "Country", "City"} ] (* somewhat more involved example,menu definition is created \ programmatically from CountryData and CityData, watch out,this will \ not work well when ContryData and CityData are not yet initialized \ and will take a while to finish *) menuDefinition = Map[ Function[cont, cont -> Map[ # -> CountryData[#, "LargestCities"][[All, 1]] &, CountryData[cont] ] ], DeleteCases[CountryData["Continents"], "Antarctica"] ]; (* example for a more interesting global action *) cityinfo[{continent___, country_, city_}] := CreateDialog[{ Grid[{ {Style[city, "Section"], Show[CountryData[country, "Flag"], ImageSize -> 50]}, {continent, country}, {"Population", CityData[city, "Population"]}, {"Elevation", CityData[city, "Elevation"]} }, Alignment -> {{Left, Right}, Automatic}], DefaultButton[] }] (* check that the global action works: *) cityinfo[{"Europe", "Switzerland", "Zurich"}] (* now we are ready to install the nested action menu in a "menu bar", \ we use the option "AppearanceWrapper" for further formatting... *) SetOptions[EvaluationNotebook[], DockedCells -> { Cell[BoxData[ToBoxes[Button["City Information", CreateDialog[{ExpressionCell[Panel[ nestedActionMenu[ menuDefinition, (DialogReturn[]; cityinfo[#]) &, Appearance -> None, "AppearanceWrapper" -> Function[ Grid[{#}, Alignment -> Left, Dividers -> All, FrameStyle -> LightGray, ItemSize -> {10, 1}]], "DefaultLabels" -> {"Continent", "Country", "City"} ], ImageMargins -> 0, FrameMargins -> 0 ], CellMargins -> {{0, 0}, {0, 0}} ]}, WindowMargins -> { {MousePosition["ScreenAbsolute"][[1]], Automatic}, {Automatic, MousePosition["ScreenAbsolute"][[2]] - 40}}, WindowFrame -> "Frameless", CellMargins -> 0, CellFrameMargins -> 0, WindowSize -> FitAll ], Appearance -> None ] ]], "DockedCell" ] } ]