2017-09-13 14:56:20 +00:00
|
|
|
|
--[[--
|
|
|
|
|
Widget that displays a shortcut icon for menu item.
|
|
|
|
|
--]]
|
|
|
|
|
|
2017-04-29 08:38:09 +00:00
|
|
|
|
local Blitbuffer = require("ffi/blitbuffer")
|
2013-10-18 20:38:07 +00:00
|
|
|
|
local BottomContainer = require("ui/widget/container/bottomcontainer")
|
|
|
|
|
local Button = require("ui/widget/button")
|
2017-04-29 08:38:09 +00:00
|
|
|
|
local CenterContainer = require("ui/widget/container/centercontainer")
|
|
|
|
|
local Device = require("device")
|
|
|
|
|
local FocusManager = require("ui/widget/focusmanager")
|
2013-10-18 20:38:07 +00:00
|
|
|
|
local Font = require("ui/font")
|
2017-04-29 08:38:09 +00:00
|
|
|
|
local FrameContainer = require("ui/widget/container/framecontainer")
|
2013-10-18 20:38:07 +00:00
|
|
|
|
local Geom = require("ui/geometry")
|
2017-04-29 08:38:09 +00:00
|
|
|
|
local GestureRange = require("ui/gesturerange")
|
|
|
|
|
local HorizontalGroup = require("ui/widget/horizontalgroup")
|
|
|
|
|
local HorizontalSpan = require("ui/widget/horizontalspan")
|
2013-10-22 15:11:31 +00:00
|
|
|
|
local InfoMessage = require("ui/widget/infomessage")
|
2017-04-29 08:38:09 +00:00
|
|
|
|
local InputContainer = require("ui/widget/container/inputcontainer")
|
|
|
|
|
local LeftContainer = require("ui/widget/container/leftcontainer")
|
|
|
|
|
local OverlapGroup = require("ui/widget/overlapgroup")
|
|
|
|
|
local RenderText = require("ui/rendertext")
|
|
|
|
|
local RightContainer = require("ui/widget/container/rightcontainer")
|
2017-09-13 14:56:20 +00:00
|
|
|
|
local Size = require("ui/size")
|
2018-01-13 22:38:53 +00:00
|
|
|
|
local TextBoxWidget = require("ui/widget/textboxwidget")
|
2017-04-29 08:38:09 +00:00
|
|
|
|
local TextWidget = require("ui/widget/textwidget")
|
|
|
|
|
local UIManager = require("ui/uimanager")
|
|
|
|
|
local UnderlineContainer = require("ui/widget/container/underlinecontainer")
|
|
|
|
|
local VerticalGroup = require("ui/widget/verticalgroup")
|
|
|
|
|
local VerticalSpan = require("ui/widget/verticalspan")
|
|
|
|
|
local WidgetContainer = require("ui/widget/container/widgetcontainer")
|
2016-12-29 08:10:38 +00:00
|
|
|
|
local logger = require("logger")
|
2017-04-29 08:38:09 +00:00
|
|
|
|
local util = require("ffi/util")
|
2013-10-18 20:38:07 +00:00
|
|
|
|
local _ = require("gettext")
|
2017-04-29 08:38:09 +00:00
|
|
|
|
local Input = Device.input
|
|
|
|
|
local Screen = Device.screen
|
2017-02-04 04:26:47 +00:00
|
|
|
|
local getMenuText = require("util").getMenuText
|
2012-04-15 00:28:48 +00:00
|
|
|
|
|
2013-10-18 20:38:07 +00:00
|
|
|
|
local ItemShortCutIcon = WidgetContainer:new{
|
2017-09-11 08:32:39 +00:00
|
|
|
|
dimen = Geom:new{ w = Screen:scaleBySize(22), h = Screen:scaleBySize(22) },
|
2014-03-13 13:52:43 +00:00
|
|
|
|
key = nil,
|
2017-09-13 14:56:20 +00:00
|
|
|
|
bordersize = Size.border.default,
|
2014-03-13 13:52:43 +00:00
|
|
|
|
radius = 0,
|
2014-07-30 07:00:02 +00:00
|
|
|
|
style = "square",
|
2012-04-29 15:53:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function ItemShortCutIcon:init()
|
2014-03-13 13:52:43 +00:00
|
|
|
|
if not self.key then
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local radius = 0
|
2014-10-22 13:34:11 +00:00
|
|
|
|
local background = Blitbuffer.COLOR_WHITE
|
2014-03-13 13:52:43 +00:00
|
|
|
|
if self.style == "rounded_corner" then
|
|
|
|
|
radius = math.floor(self.width/2)
|
|
|
|
|
elseif self.style == "grey_square" then
|
2014-10-22 13:34:11 +00:00
|
|
|
|
background = Blitbuffer.gray(0.2)
|
2014-03-13 13:52:43 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--@TODO calculate font size by icon size 01.05 2012 (houqp)
|
2016-01-03 07:44:23 +00:00
|
|
|
|
local sc_face
|
2014-03-13 13:52:43 +00:00
|
|
|
|
if self.key:len() > 1 then
|
|
|
|
|
sc_face = Font:getFace("ffont", 14)
|
|
|
|
|
else
|
|
|
|
|
sc_face = Font:getFace("scfont", 22)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
self[1] = FrameContainer:new{
|
|
|
|
|
padding = 0,
|
|
|
|
|
bordersize = self.bordersize,
|
|
|
|
|
radius = radius,
|
|
|
|
|
background = background,
|
|
|
|
|
dimen = self.dimen,
|
|
|
|
|
CenterContainer:new{
|
|
|
|
|
dimen = self.dimen,
|
|
|
|
|
TextWidget:new{
|
|
|
|
|
text = self.key,
|
|
|
|
|
face = sc_face,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
2012-04-29 15:53:48 +00:00
|
|
|
|
end
|
|
|
|
|
|
2012-12-07 15:30:15 +00:00
|
|
|
|
--[[
|
|
|
|
|
NOTICE:
|
|
|
|
|
@menu entry must be provided in order to close the menu
|
|
|
|
|
--]]
|
2013-10-18 20:38:07 +00:00
|
|
|
|
local MenuCloseButton = InputContainer:new{
|
2016-02-14 21:47:36 +00:00
|
|
|
|
overlap_align = "right",
|
2017-10-10 20:23:25 +00:00
|
|
|
|
padding_right = 0,
|
2014-03-13 13:52:43 +00:00
|
|
|
|
menu = nil,
|
|
|
|
|
dimen = Geom:new{},
|
2012-12-07 15:30:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function MenuCloseButton:init()
|
2014-03-13 13:52:43 +00:00
|
|
|
|
self[1] = TextWidget:new{
|
|
|
|
|
text = "×",
|
2017-10-10 20:23:25 +00:00
|
|
|
|
face = Font:getFace("cfont", 30), -- this font size align nicely with title
|
2014-03-13 13:52:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
local text_size = self[1]:getSize()
|
2017-10-10 20:23:25 +00:00
|
|
|
|
-- The text box height is greater than its width, and we want this × to
|
|
|
|
|
-- be diagonally aligned with our top right border
|
|
|
|
|
local text_width_pad = (text_size.h - text_size.w) / 2
|
|
|
|
|
-- We also add the provided padding_right
|
2016-02-14 21:47:36 +00:00
|
|
|
|
self.dimen = Geom:new{
|
2017-10-10 20:23:25 +00:00
|
|
|
|
w = text_size.w + text_width_pad + self.padding_right,
|
|
|
|
|
h = text_size.h,
|
2016-02-14 21:47:36 +00:00
|
|
|
|
}
|
2014-03-13 13:52:43 +00:00
|
|
|
|
|
|
|
|
|
self.ges_events.Close = {
|
|
|
|
|
GestureRange:new{
|
|
|
|
|
ges = "tap",
|
|
|
|
|
range = self.dimen,
|
|
|
|
|
},
|
|
|
|
|
doc = "Close menu",
|
|
|
|
|
}
|
2012-12-07 15:30:15 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function MenuCloseButton:onClose()
|
2014-03-13 13:52:43 +00:00
|
|
|
|
self.menu:onClose()
|
|
|
|
|
return true
|
2012-12-07 15:30:15 +00:00
|
|
|
|
end
|
2012-04-29 15:53:48 +00:00
|
|
|
|
|
|
|
|
|
--[[
|
|
|
|
|
Widget that displays an item for menu
|
2012-12-07 15:30:15 +00:00
|
|
|
|
--]]
|
2013-10-18 20:38:07 +00:00
|
|
|
|
local MenuItem = InputContainer:new{
|
2014-03-13 13:52:43 +00:00
|
|
|
|
text = nil,
|
|
|
|
|
show_parent = nil,
|
|
|
|
|
detail = nil,
|
|
|
|
|
face = Font:getFace("cfont", 30),
|
|
|
|
|
info_face = Font:getFace("infont", 15),
|
2018-01-13 22:38:53 +00:00
|
|
|
|
font = "cfont",
|
|
|
|
|
font_size = 24,
|
|
|
|
|
infont = "infont",
|
|
|
|
|
infont_size = 18,
|
2014-03-13 13:52:43 +00:00
|
|
|
|
dimen = nil,
|
|
|
|
|
shortcut = nil,
|
|
|
|
|
shortcut_style = "square",
|
|
|
|
|
_underline_container = nil,
|
2018-01-13 22:38:53 +00:00
|
|
|
|
linesize = Size.line.medium,
|
2012-04-29 15:53:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function MenuItem:init()
|
2017-10-10 20:23:25 +00:00
|
|
|
|
self.content_width = self.dimen.w - 2 * Size.padding.fullscreen
|
2014-03-13 13:52:43 +00:00
|
|
|
|
local shortcut_icon_dimen = Geom:new()
|
|
|
|
|
if self.shortcut then
|
|
|
|
|
shortcut_icon_dimen.w = math.floor(self.dimen.h*4/5)
|
|
|
|
|
shortcut_icon_dimen.h = shortcut_icon_dimen.w
|
2017-10-10 20:23:25 +00:00
|
|
|
|
self.content_width = self.content_width - shortcut_icon_dimen.w - Size.span.horizontal_default
|
2014-03-13 13:52:43 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
self.detail = self.text
|
|
|
|
|
|
|
|
|
|
-- we need this table per-instance, so we declare it here
|
|
|
|
|
if Device:isTouchDevice() then
|
|
|
|
|
self.ges_events = {
|
|
|
|
|
TapSelect = {
|
|
|
|
|
GestureRange:new{
|
|
|
|
|
ges = "tap",
|
|
|
|
|
range = self.dimen,
|
|
|
|
|
},
|
|
|
|
|
doc = "Select Menu Item",
|
|
|
|
|
},
|
|
|
|
|
HoldSelect = {
|
|
|
|
|
GestureRange:new{
|
|
|
|
|
ges = "hold",
|
|
|
|
|
range = self.dimen,
|
|
|
|
|
},
|
|
|
|
|
doc = "Hold Menu Item",
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
2017-10-13 12:42:08 +00:00
|
|
|
|
local text_mandatory_padding = 0
|
|
|
|
|
local text_ellipsis_mandatory_padding = 0
|
|
|
|
|
if self.mandatory then
|
|
|
|
|
text_mandatory_padding = Size.span.horizontal_default
|
|
|
|
|
-- Smaller padding when ellipsis for better visual feeling
|
|
|
|
|
text_ellipsis_mandatory_padding = Size.span.horizontal_small
|
|
|
|
|
end
|
2017-10-10 20:23:25 +00:00
|
|
|
|
local mandatory = self.mandatory and ""..self.mandatory or ""
|
2014-10-14 13:33:13 +00:00
|
|
|
|
|
|
|
|
|
local state_button_width = self.state_size.w or 0
|
|
|
|
|
local state_button = self.state or HorizontalSpan:new{
|
|
|
|
|
width = state_button_width,
|
|
|
|
|
}
|
|
|
|
|
local state_indent = self.state and self.state.indent or ""
|
2018-01-13 22:38:53 +00:00
|
|
|
|
local item_name
|
|
|
|
|
local mandatory_widget
|
|
|
|
|
|
|
|
|
|
if self.single_line then -- items only in single line
|
|
|
|
|
self.info_face = Font:getFace(self.infont, self.infont_size)
|
|
|
|
|
self.face = Font:getFace(self.font, self.font_size)
|
|
|
|
|
|
|
|
|
|
local mandatory_w = RenderText:sizeUtf8Text(0, self.dimen.w, self.info_face, ""..mandatory, true, self.bold).x
|
|
|
|
|
|
|
|
|
|
local my_text = self.text and ""..self.text or ""
|
|
|
|
|
local w = RenderText:sizeUtf8Text(0, self.dimen.w, self.face, my_text, true, self.bold).x
|
|
|
|
|
if w + mandatory_w + state_button_width + text_mandatory_padding >= self.content_width then
|
|
|
|
|
local indicator = "\226\128\166 " -- an ellipsis
|
|
|
|
|
local indicator_w = RenderText:sizeUtf8Text(0, self.dimen.w, self.face,
|
|
|
|
|
indicator, true, self.bold).x
|
|
|
|
|
self.text = RenderText:getSubTextByWidth(my_text, self.face,
|
|
|
|
|
self.content_width - indicator_w - mandatory_w - state_button_width - text_ellipsis_mandatory_padding,
|
|
|
|
|
true, self.bold) .. indicator
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
item_name = TextWidget:new{
|
|
|
|
|
text = self.text,
|
|
|
|
|
face = self.face,
|
|
|
|
|
bold = self.bold,
|
|
|
|
|
fgcolor = self.dim and Blitbuffer.COLOR_GREY or nil,
|
|
|
|
|
}
|
|
|
|
|
mandatory_widget = TextWidget:new{
|
|
|
|
|
text = mandatory,
|
|
|
|
|
face = self.info_face,
|
|
|
|
|
bold = self.bold,
|
|
|
|
|
fgcolor = self.dim and Blitbuffer.COLOR_GREY or nil,
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
while true do
|
|
|
|
|
-- Free previously made widgets to avoid memory leaks
|
|
|
|
|
if mandatory_widget then
|
|
|
|
|
mandatory_widget:free()
|
|
|
|
|
end
|
|
|
|
|
mandatory_widget = TextWidget:new {
|
|
|
|
|
text = mandatory,
|
|
|
|
|
face = Font:getFace(self.infont, self.infont_size),
|
|
|
|
|
bold = self.bold,
|
|
|
|
|
fgcolor = self.dim and Blitbuffer.COLOR_GREY or nil,
|
|
|
|
|
}
|
|
|
|
|
local height = mandatory_widget:getSize().h
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if height < self.dimen.h - 2 * self.linesize then -- we fit !
|
|
|
|
|
break
|
|
|
|
|
end
|
|
|
|
|
-- Don't go too low
|
|
|
|
|
if self.infont_size < 12 then
|
|
|
|
|
break;
|
|
|
|
|
else
|
|
|
|
|
-- If we don't fit, decrease font size
|
|
|
|
|
self.infont_size = self.infont_size - 1
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
self.info_face = Font:getFace(self.infont, self.infont_size)
|
|
|
|
|
local mandatory_w = RenderText:sizeUtf8Text(0, self.dimen.w, self.info_face, "" .. mandatory, true, self.bold).x
|
2018-02-04 18:48:39 +00:00
|
|
|
|
local max_item_height = self.dimen.h - 2 * self.linesize
|
|
|
|
|
local flag_fit = false
|
2018-01-13 22:38:53 +00:00
|
|
|
|
while true do
|
|
|
|
|
-- Free previously made widgets to avoid memory leaks
|
|
|
|
|
if item_name then
|
|
|
|
|
item_name:free()
|
|
|
|
|
end
|
|
|
|
|
item_name = TextBoxWidget:new {
|
|
|
|
|
text = self.text,
|
|
|
|
|
face = Font:getFace(self.font, self.font_size),
|
|
|
|
|
width = self.content_width - mandatory_w - state_button_width - text_mandatory_padding,
|
|
|
|
|
alignment = "left",
|
|
|
|
|
bold = self.bold,
|
|
|
|
|
fgcolor = self.dim and Blitbuffer.COLOR_GREY or nil,
|
|
|
|
|
}
|
|
|
|
|
local height = item_name:getSize().h
|
2018-02-04 18:48:39 +00:00
|
|
|
|
if height < max_item_height or flag_fit then -- we fit !
|
2018-01-13 22:38:53 +00:00
|
|
|
|
break
|
|
|
|
|
end
|
2018-02-04 18:48:39 +00:00
|
|
|
|
-- Don't go too low, and then decrease lines
|
|
|
|
|
if self.font_size <= 12 then
|
|
|
|
|
local line_height = height / #item_name.vertical_string_list -- should be an integer
|
|
|
|
|
local lines = math.floor(max_item_height / line_height)
|
|
|
|
|
local offset
|
|
|
|
|
if item_name.vertical_string_list[lines + 1] then
|
|
|
|
|
offset = item_name.vertical_string_list[lines + 1].offset - 2
|
|
|
|
|
else -- shouldn't happen, but just in case
|
2018-04-21 20:16:14 +00:00
|
|
|
|
offset = #item_name.charlist
|
2018-02-04 18:48:39 +00:00
|
|
|
|
end
|
|
|
|
|
local ellipsis_size = RenderText:sizeUtf8Text(0, self.content_width,
|
|
|
|
|
Font:getFace(self.font, self.font_size), "…", true, self.bold).x
|
|
|
|
|
local removed_char_width= 0
|
|
|
|
|
while removed_char_width < ellipsis_size do
|
|
|
|
|
-- the width of each char has already been calculated by TextBoxWidget
|
2018-04-21 20:16:14 +00:00
|
|
|
|
removed_char_width = removed_char_width + item_name:geCharWidth(offset)
|
2018-02-04 18:48:39 +00:00
|
|
|
|
offset = offset - 1
|
|
|
|
|
end
|
|
|
|
|
self.text = table.concat(item_name.charlist, '', 1, offset) .. "…"
|
|
|
|
|
flag_fit = true
|
2018-01-13 22:38:53 +00:00
|
|
|
|
else
|
|
|
|
|
-- If we don't fit, decrease font size
|
|
|
|
|
self.font_size = self.font_size - 2
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
self.face = Font:getFace(self.font, self.font_size)
|
|
|
|
|
end
|
|
|
|
|
|
2014-10-14 13:33:13 +00:00
|
|
|
|
local state_container = LeftContainer:new{
|
|
|
|
|
dimen = Geom:new{w = self.content_width/2, h = self.dimen.h},
|
|
|
|
|
HorizontalGroup:new{
|
|
|
|
|
HorizontalSpan:new{
|
|
|
|
|
width = RenderText:sizeUtf8Text(0, self.dimen.w, self.face,
|
2018-01-13 22:38:53 +00:00
|
|
|
|
state_indent, true, self.bold).x,
|
2014-10-14 13:33:13 +00:00
|
|
|
|
},
|
2018-01-13 22:38:53 +00:00
|
|
|
|
state_button,
|
2014-10-14 13:33:13 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2014-03-13 13:52:43 +00:00
|
|
|
|
local text_container = LeftContainer:new{
|
|
|
|
|
dimen = Geom:new{w = self.content_width, h = self.dimen.h},
|
2014-10-14 13:33:13 +00:00
|
|
|
|
HorizontalGroup:new{
|
|
|
|
|
HorizontalSpan:new{
|
|
|
|
|
width = self.state_size.w,
|
|
|
|
|
},
|
2018-01-13 22:38:53 +00:00
|
|
|
|
item_name,
|
2014-03-13 13:52:43 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2014-04-04 15:02:29 +00:00
|
|
|
|
|
2014-03-13 13:52:43 +00:00
|
|
|
|
local mandatory_container = RightContainer:new{
|
|
|
|
|
dimen = Geom:new{w = self.content_width, h = self.dimen.h},
|
2018-01-13 22:38:53 +00:00
|
|
|
|
mandatory_widget,
|
2014-03-13 13:52:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self._underline_container = UnderlineContainer:new{
|
2018-01-13 22:38:53 +00:00
|
|
|
|
color = self.line_color,
|
|
|
|
|
linesize = self.linesize,
|
2014-10-14 13:33:13 +00:00
|
|
|
|
vertical_align = "center",
|
2018-01-13 22:38:53 +00:00
|
|
|
|
padding = 0,
|
2014-03-13 13:52:43 +00:00
|
|
|
|
dimen = Geom:new{
|
|
|
|
|
w = self.content_width,
|
|
|
|
|
h = self.dimen.h
|
|
|
|
|
},
|
|
|
|
|
HorizontalGroup:new{
|
|
|
|
|
align = "center",
|
|
|
|
|
OverlapGroup:new{
|
|
|
|
|
dimen = Geom:new{w = self.content_width, h = self.dimen.h},
|
2014-10-14 13:33:13 +00:00
|
|
|
|
state_container,
|
2014-03-13 13:52:43 +00:00
|
|
|
|
text_container,
|
|
|
|
|
mandatory_container,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-10-10 20:23:25 +00:00
|
|
|
|
local hgroup = HorizontalGroup:new{
|
|
|
|
|
align = "center",
|
|
|
|
|
HorizontalSpan:new{ width = Size.padding.fullscreen },
|
|
|
|
|
}
|
|
|
|
|
if self.shortcut then
|
|
|
|
|
table.insert(hgroup, ItemShortCutIcon:new{
|
|
|
|
|
dimen = shortcut_icon_dimen,
|
|
|
|
|
key = self.shortcut,
|
|
|
|
|
style = self.shortcut_style,
|
|
|
|
|
})
|
|
|
|
|
table.insert(hgroup, HorizontalSpan:new{ width = Size.span.horizontal_default })
|
|
|
|
|
end
|
|
|
|
|
table.insert(hgroup, self._underline_container)
|
|
|
|
|
|
2014-03-13 13:52:43 +00:00
|
|
|
|
self[1] = FrameContainer:new{
|
|
|
|
|
bordersize = 0,
|
|
|
|
|
padding = 0,
|
2017-10-10 20:23:25 +00:00
|
|
|
|
hgroup,
|
2014-03-13 13:52:43 +00:00
|
|
|
|
}
|
2012-04-29 15:53:48 +00:00
|
|
|
|
end
|
|
|
|
|
|
2018-02-05 14:42:26 +00:00
|
|
|
|
function MenuItem:onFocus(initial_focus)
|
|
|
|
|
if Device:isTouchDevice() then
|
|
|
|
|
-- Devices which are Keys capable will get this onFocus called by
|
|
|
|
|
-- updateItems(), which will toggle the underline color of first item.
|
|
|
|
|
-- If the device is also Touch capable, let's not show the initial
|
|
|
|
|
-- underline for a prettier display (it will be shown only when keys
|
|
|
|
|
-- are used).
|
|
|
|
|
if not initial_focus or self.menu.did_focus_with_keys then
|
|
|
|
|
self._underline_container.color = Blitbuffer.COLOR_BLACK
|
|
|
|
|
self.menu.did_focus_with_keys = true
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
self._underline_container.color = Blitbuffer.COLOR_BLACK
|
|
|
|
|
end
|
2014-03-13 13:52:43 +00:00
|
|
|
|
return true
|
2012-04-29 15:53:48 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function MenuItem:onUnfocus()
|
2018-01-13 22:38:53 +00:00
|
|
|
|
self._underline_container.color = self.line_color
|
2014-03-13 13:52:43 +00:00
|
|
|
|
return true
|
2012-04-29 15:53:48 +00:00
|
|
|
|
end
|
|
|
|
|
|
2012-05-28 16:59:16 +00:00
|
|
|
|
function MenuItem:onShowItemDetail()
|
2017-05-08 07:26:01 +00:00
|
|
|
|
UIManager:show(InfoMessage:new{ text = self.detail, })
|
2014-03-13 13:52:43 +00:00
|
|
|
|
return true
|
2012-04-29 15:53:48 +00:00
|
|
|
|
end
|
|
|
|
|
|
2014-10-21 14:18:07 +00:00
|
|
|
|
function MenuItem:getGesPosition(ges)
|
|
|
|
|
local dimen = self[1].dimen
|
|
|
|
|
return {
|
|
|
|
|
x = (ges.pos.x - dimen.x)/dimen.w,
|
|
|
|
|
y = (ges.pos.y - dimen.y)/dimen.h,
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function MenuItem:onTapSelect(arg, ges)
|
|
|
|
|
local pos = self:getGesPosition(ges)
|
2017-10-10 21:50:45 +00:00
|
|
|
|
if G_reader_settings:isFalse("flash_ui") then
|
2016-12-29 08:10:38 +00:00
|
|
|
|
logger.dbg("creating coroutine for menu select")
|
2015-09-14 16:59:00 +00:00
|
|
|
|
local co = coroutine.create(function()
|
|
|
|
|
self.menu:onMenuSelect(self.table, pos)
|
|
|
|
|
end)
|
|
|
|
|
coroutine.resume(co)
|
2017-10-10 21:50:45 +00:00
|
|
|
|
else
|
|
|
|
|
self[1].invert = true
|
2018-06-02 16:10:55 +00:00
|
|
|
|
UIManager:setDirty(self.show_parent, function()
|
|
|
|
|
return "fast", self[1].dimen
|
|
|
|
|
end)
|
|
|
|
|
UIManager:tickAfterNext(function()
|
2017-10-10 21:50:45 +00:00
|
|
|
|
self[1].invert = false
|
2018-06-02 16:10:55 +00:00
|
|
|
|
UIManager:setDirty(self.show_parent, function()
|
|
|
|
|
return "ui", self[1].dimen
|
|
|
|
|
end)
|
2017-10-10 21:50:45 +00:00
|
|
|
|
logger.dbg("creating coroutine for menu select")
|
|
|
|
|
local co = coroutine.create(function()
|
|
|
|
|
self.menu:onMenuSelect(self.table, pos)
|
|
|
|
|
end)
|
|
|
|
|
coroutine.resume(co)
|
|
|
|
|
end)
|
|
|
|
|
end
|
2014-03-13 13:52:43 +00:00
|
|
|
|
return true
|
2012-11-11 06:00:52 +00:00
|
|
|
|
end
|
|
|
|
|
|
2014-10-21 14:18:07 +00:00
|
|
|
|
function MenuItem:onHoldSelect(arg, ges)
|
|
|
|
|
local pos = self:getGesPosition(ges)
|
2017-10-10 21:50:45 +00:00
|
|
|
|
if G_reader_settings:isFalse("flash_ui") then
|
2014-10-21 14:18:07 +00:00
|
|
|
|
self.menu:onMenuHold(self.table, pos)
|
2017-10-10 21:50:45 +00:00
|
|
|
|
else
|
|
|
|
|
self[1].invert = true
|
2018-06-02 16:10:55 +00:00
|
|
|
|
UIManager:setDirty(self.show_parent, function()
|
|
|
|
|
return "fast", self[1].dimen
|
|
|
|
|
end)
|
|
|
|
|
UIManager:tickAfterNext(function()
|
2017-10-10 21:50:45 +00:00
|
|
|
|
self[1].invert = false
|
2018-06-02 16:10:55 +00:00
|
|
|
|
UIManager:setDirty(self.show_parent, function()
|
|
|
|
|
return "ui", self[1].dimen
|
|
|
|
|
end)
|
2017-10-10 21:50:45 +00:00
|
|
|
|
self.menu:onMenuHold(self.table, pos)
|
|
|
|
|
end)
|
|
|
|
|
end
|
2014-03-13 13:52:43 +00:00
|
|
|
|
return true
|
2014-01-18 15:15:44 +00:00
|
|
|
|
end
|
|
|
|
|
|
2012-04-29 15:53:48 +00:00
|
|
|
|
--[[
|
|
|
|
|
Widget that displays menu
|
2012-12-07 15:30:15 +00:00
|
|
|
|
--]]
|
2013-10-18 20:38:07 +00:00
|
|
|
|
local Menu = FocusManager:new{
|
2014-03-13 13:52:43 +00:00
|
|
|
|
show_parent = nil,
|
|
|
|
|
-- face for displaying item contents
|
2017-04-29 08:38:09 +00:00
|
|
|
|
cface = Font:getFace("cfont"),
|
2014-03-13 13:52:43 +00:00
|
|
|
|
-- face for menu title
|
2017-04-29 08:38:09 +00:00
|
|
|
|
tface = Font:getFace("tfont"),
|
2014-03-13 13:52:43 +00:00
|
|
|
|
-- face for paging info display
|
2017-04-29 08:38:09 +00:00
|
|
|
|
fface = Font:getFace("ffont"),
|
2014-03-13 13:52:43 +00:00
|
|
|
|
-- font for item shortcut
|
2017-04-29 08:38:09 +00:00
|
|
|
|
sface = Font:getFace("scfont"),
|
2014-03-13 13:52:43 +00:00
|
|
|
|
|
|
|
|
|
title = "No Title",
|
|
|
|
|
-- default width and height
|
2017-09-11 08:32:39 +00:00
|
|
|
|
width = nil,
|
2014-03-13 13:52:43 +00:00
|
|
|
|
-- height will be calculated according to item number if not given
|
|
|
|
|
height = nil,
|
2017-09-13 14:56:20 +00:00
|
|
|
|
header_padding = Size.padding.large,
|
2014-03-13 13:52:43 +00:00
|
|
|
|
dimen = Geom:new{},
|
|
|
|
|
item_table = {},
|
|
|
|
|
item_shortcuts = {
|
|
|
|
|
"Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P",
|
|
|
|
|
"A", "S", "D", "F", "G", "H", "J", "K", "L", "Del",
|
2018-03-30 21:22:18 +00:00
|
|
|
|
"Z", "X", "C", "V", "B", "N", "M", ".", "Sym",
|
2014-03-13 13:52:43 +00:00
|
|
|
|
},
|
|
|
|
|
item_table_stack = nil,
|
|
|
|
|
is_enable_shortcut = true,
|
|
|
|
|
|
|
|
|
|
item_dimen = nil,
|
|
|
|
|
page = 1,
|
|
|
|
|
|
|
|
|
|
item_group = nil,
|
|
|
|
|
page_info = nil,
|
2014-09-05 13:07:21 +00:00
|
|
|
|
page_return = nil,
|
|
|
|
|
|
|
|
|
|
paths = {}, -- table to trace navigation path
|
2014-03-13 13:52:43 +00:00
|
|
|
|
|
|
|
|
|
-- set this to true to not paint as popup menu
|
|
|
|
|
is_borderless = false,
|
|
|
|
|
-- if you want to embed the menu widget into another widget, set
|
|
|
|
|
-- this to false
|
|
|
|
|
is_popout = true,
|
|
|
|
|
-- set this to true to add close button
|
|
|
|
|
has_close_button = true,
|
|
|
|
|
-- close_callback is a function, which is executed when menu is closed
|
|
|
|
|
-- it is usually set by the widget which creates the menu
|
2018-01-13 22:38:53 +00:00
|
|
|
|
close_callback = nil,
|
|
|
|
|
linesize = Size.line.medium,
|
|
|
|
|
perpage = G_reader_settings:readSetting("items_per_page") or 14,
|
|
|
|
|
line_color = Blitbuffer.COLOR_GREY,
|
2012-04-29 15:53:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-12-08 17:18:40 +00:00
|
|
|
|
function Menu:_recalculateDimen()
|
2018-02-04 18:48:39 +00:00
|
|
|
|
self.perpage = self.perpage_custom or G_reader_settings:readSetting("items_per_page") or 14
|
2018-01-13 22:38:53 +00:00
|
|
|
|
self.span_width = 0
|
2014-03-13 13:52:43 +00:00
|
|
|
|
self.dimen.w = self.width
|
2018-02-04 18:48:39 +00:00
|
|
|
|
self.dimen.h = self.height or Screen:getHeight()
|
2018-01-13 22:38:53 +00:00
|
|
|
|
if self.dimen.h > Screen:getHeight() or self.dimen.h == nil then
|
|
|
|
|
self.dimen.h = Screen:getHeight()
|
|
|
|
|
end
|
2014-03-13 13:52:43 +00:00
|
|
|
|
self.item_dimen = Geom:new{
|
|
|
|
|
w = self.dimen.w,
|
2018-01-13 22:38:53 +00:00
|
|
|
|
h = Screen:scaleBySize(46),
|
2014-03-13 13:52:43 +00:00
|
|
|
|
}
|
2018-01-13 22:38:53 +00:00
|
|
|
|
local height_dim
|
|
|
|
|
local bottom_height = 0
|
|
|
|
|
local top_height = 0
|
|
|
|
|
if self.page_return_arrow and self.page_info_text then
|
|
|
|
|
bottom_height = math.max(self.page_return_arrow:getSize().h, self.page_info_text:getSize().h)
|
|
|
|
|
+ 2 * Size.padding.button
|
2014-10-25 08:01:23 +00:00
|
|
|
|
end
|
2018-01-13 22:38:53 +00:00
|
|
|
|
if self.menu_title and not self.no_title then
|
|
|
|
|
top_height = self.menu_title:getSize().h + 2 * Size.padding.small
|
|
|
|
|
end
|
|
|
|
|
height_dim = self.dimen.h - bottom_height - top_height
|
|
|
|
|
self.item_dimen.h = math.floor(height_dim / self.perpage)
|
|
|
|
|
self.span_width = math.floor((height_dim - (self.perpage * (self.item_dimen.h ))) / 2 -1 )
|
2014-03-13 13:52:43 +00:00
|
|
|
|
self.page_num = math.ceil(#self.item_table / self.perpage)
|
2017-10-21 17:53:56 +00:00
|
|
|
|
-- fix current page if out of range
|
|
|
|
|
if self.page_num > 0 and self.page > self.page_num then self.page = self.page_num end
|
2012-12-08 17:18:40 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function Menu:init()
|
2014-03-13 13:52:43 +00:00
|
|
|
|
self.show_parent = self.show_parent or self
|
|
|
|
|
self.item_table_stack = {}
|
2018-01-13 22:38:53 +00:00
|
|
|
|
self.dimen.w = self.width
|
2018-02-04 18:48:39 +00:00
|
|
|
|
self.dimen.h = self.height or Screen:getHeight()
|
2018-01-13 22:38:53 +00:00
|
|
|
|
if self.dimen.h > Screen:getHeight() or self.dimen.h == nil then
|
|
|
|
|
self.dimen.h = Screen:getHeight()
|
|
|
|
|
end
|
2014-03-13 13:52:43 +00:00
|
|
|
|
self.page = 1
|
|
|
|
|
|
|
|
|
|
-----------------------------------
|
|
|
|
|
-- start to set up widget layout --
|
|
|
|
|
-----------------------------------
|
|
|
|
|
self.menu_title = TextWidget:new{
|
2016-02-14 21:47:36 +00:00
|
|
|
|
overlap_align = "center",
|
2014-03-13 13:52:43 +00:00
|
|
|
|
text = self.title,
|
|
|
|
|
face = self.tface,
|
|
|
|
|
}
|
|
|
|
|
-- group for title bar
|
|
|
|
|
self.title_bar = OverlapGroup:new{
|
|
|
|
|
dimen = {w = self.dimen.w, h = self.menu_title:getSize().h},
|
|
|
|
|
self.menu_title,
|
|
|
|
|
}
|
|
|
|
|
-- group for items
|
|
|
|
|
self.item_group = VerticalGroup:new{}
|
|
|
|
|
-- group for page info
|
|
|
|
|
self.page_info_left_chev = Button:new{
|
|
|
|
|
icon = "resources/icons/appbar.chevron.left.png",
|
|
|
|
|
callback = function() self:onPrevPage() end,
|
|
|
|
|
bordersize = 0,
|
2014-05-01 10:37:12 +00:00
|
|
|
|
show_parent = self,
|
2014-03-13 13:52:43 +00:00
|
|
|
|
}
|
|
|
|
|
self.page_info_right_chev = Button:new{
|
|
|
|
|
icon = "resources/icons/appbar.chevron.right.png",
|
|
|
|
|
callback = function() self:onNextPage() end,
|
|
|
|
|
bordersize = 0,
|
2014-05-01 10:37:12 +00:00
|
|
|
|
show_parent = self,
|
2014-03-13 13:52:43 +00:00
|
|
|
|
}
|
2014-07-30 07:00:02 +00:00
|
|
|
|
self.page_info_first_chev = Button:new{
|
|
|
|
|
icon = "resources/icons/appbar.chevron.first.png",
|
|
|
|
|
callback = function() self:onFirstPage() end,
|
|
|
|
|
bordersize = 0,
|
|
|
|
|
show_parent = self,
|
|
|
|
|
}
|
|
|
|
|
self.page_info_last_chev = Button:new{
|
|
|
|
|
icon = "resources/icons/appbar.chevron.last.png",
|
|
|
|
|
callback = function() self:onLastPage() end,
|
|
|
|
|
bordersize = 0,
|
|
|
|
|
show_parent = self,
|
2014-08-05 15:41:36 +00:00
|
|
|
|
}
|
2014-09-05 13:07:21 +00:00
|
|
|
|
self.page_info_spacer = HorizontalSpan:new{
|
2014-11-20 22:07:39 +00:00
|
|
|
|
width = Screen:scaleBySize(32),
|
2014-07-30 07:00:02 +00:00
|
|
|
|
}
|
2014-03-13 13:52:43 +00:00
|
|
|
|
self.page_info_left_chev:hide()
|
|
|
|
|
self.page_info_right_chev:hide()
|
2014-07-30 07:00:02 +00:00
|
|
|
|
self.page_info_first_chev:hide()
|
|
|
|
|
self.page_info_last_chev:hide()
|
|
|
|
|
|
2018-02-09 21:33:15 +00:00
|
|
|
|
local title_goto, type_goto, hint_func
|
|
|
|
|
local buttons = {
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
text = _("Cancel"),
|
|
|
|
|
callback = function()
|
|
|
|
|
self.page_info_text:closeInputDialog()
|
|
|
|
|
end,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
text = _("Go to page"),
|
|
|
|
|
is_enter_default = true,
|
|
|
|
|
callback = function()
|
|
|
|
|
local page = tonumber(self.page_info_text.input_dialog:getInputText())
|
|
|
|
|
if page and page >= 1 and page <= self.page_num then
|
|
|
|
|
self:onGotoPage(page)
|
|
|
|
|
end
|
|
|
|
|
self.page_info_text:closeInputDialog()
|
|
|
|
|
end,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if self.goto_letter then
|
|
|
|
|
title_goto = _("Input page number or letter")
|
|
|
|
|
type_goto = "string"
|
|
|
|
|
hint_func = function()
|
|
|
|
|
return string.format("(1 - %s) or (a - z)", self.page_num)
|
|
|
|
|
end
|
|
|
|
|
table.insert(buttons[1], {
|
|
|
|
|
text = _("Go to letter"),
|
|
|
|
|
is_enter_default = true,
|
|
|
|
|
callback = function()
|
|
|
|
|
for k, v in ipairs(self.item_table) do
|
|
|
|
|
--TODO support utf8 lowercase
|
|
|
|
|
local filename = util.basename(v.path):lower()
|
|
|
|
|
local search_string = self.page_info_text.input_dialog:getInputText():lower()
|
|
|
|
|
local i, _ = filename:find(search_string)
|
|
|
|
|
if i == 1 and not v.is_go_up then
|
|
|
|
|
self:onGotoPage(math.ceil(k / self.perpage))
|
|
|
|
|
break
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
self.page_info_text:closeInputDialog()
|
|
|
|
|
end,
|
|
|
|
|
})
|
|
|
|
|
else
|
|
|
|
|
title_goto = _("Input page number")
|
|
|
|
|
type_goto = "number"
|
|
|
|
|
hint_func = function()
|
|
|
|
|
return string.format("(1 - %s)", self.page_num)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2015-04-13 06:45:02 +00:00
|
|
|
|
self.page_info_text = Button:new{
|
2014-03-13 13:52:43 +00:00
|
|
|
|
text = "",
|
2015-04-13 06:45:02 +00:00
|
|
|
|
hold_input = {
|
2018-02-09 21:33:15 +00:00
|
|
|
|
title = title_goto ,
|
|
|
|
|
type = type_goto,
|
|
|
|
|
hint_func = hint_func,
|
|
|
|
|
buttons = buttons,
|
2015-04-13 06:45:02 +00:00
|
|
|
|
},
|
|
|
|
|
bordersize = 0,
|
|
|
|
|
text_font_face = "cfont",
|
|
|
|
|
text_font_size = 20,
|
|
|
|
|
text_font_bold = false,
|
2014-03-13 13:52:43 +00:00
|
|
|
|
}
|
|
|
|
|
self.page_info = HorizontalGroup:new{
|
2014-07-30 07:00:02 +00:00
|
|
|
|
self.page_info_first_chev,
|
2014-08-05 15:41:36 +00:00
|
|
|
|
self.page_info_spacer,
|
2014-03-13 13:52:43 +00:00
|
|
|
|
self.page_info_left_chev,
|
|
|
|
|
self.page_info_text,
|
2014-07-30 07:00:02 +00:00
|
|
|
|
self.page_info_right_chev,
|
2014-08-05 15:41:36 +00:00
|
|
|
|
self.page_info_spacer,
|
2014-07-30 07:00:02 +00:00
|
|
|
|
self.page_info_last_chev,
|
2014-03-13 13:52:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-09-05 13:07:21 +00:00
|
|
|
|
-- return button
|
|
|
|
|
self.page_return_arrow = Button:new{
|
|
|
|
|
icon = "resources/icons/appbar.arrow.left.up.png",
|
|
|
|
|
callback = function() self:onReturn() end,
|
|
|
|
|
bordersize = 0,
|
|
|
|
|
show_parent = self,
|
|
|
|
|
}
|
|
|
|
|
self.page_return_arrow:hide()
|
|
|
|
|
self.return_button = HorizontalGroup:new{
|
|
|
|
|
HorizontalSpan:new{
|
2017-09-13 14:56:20 +00:00
|
|
|
|
width = Size.span.horizontal_small,
|
2014-09-05 13:07:21 +00:00
|
|
|
|
},
|
|
|
|
|
self.page_return_arrow,
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-04 15:37:50 +00:00
|
|
|
|
local header = VerticalGroup:new{
|
|
|
|
|
VerticalSpan:new{width = self.header_padding},
|
|
|
|
|
self.title_bar,
|
|
|
|
|
}
|
2014-03-13 13:52:43 +00:00
|
|
|
|
local body = self.item_group
|
|
|
|
|
local footer = BottomContainer:new{
|
|
|
|
|
dimen = self.dimen:copy(),
|
|
|
|
|
self.page_info,
|
|
|
|
|
}
|
2014-09-05 13:07:21 +00:00
|
|
|
|
local page_return = BottomContainer:new{
|
|
|
|
|
dimen = self.dimen:copy(),
|
|
|
|
|
WidgetContainer:new{
|
|
|
|
|
dimen = Geom:new{
|
|
|
|
|
w = Screen:getWidth(),
|
|
|
|
|
h = self.page_return_arrow:getSize().h,
|
|
|
|
|
},
|
|
|
|
|
self.return_button,
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-03-13 13:52:43 +00:00
|
|
|
|
|
2018-01-13 22:38:53 +00:00
|
|
|
|
self:_recalculateDimen()
|
|
|
|
|
self.vertical_span = HorizontalGroup:new{
|
|
|
|
|
VerticalSpan:new{ width = self.span_width }
|
|
|
|
|
}
|
2014-03-13 13:52:43 +00:00
|
|
|
|
if self.no_title then
|
2018-01-13 22:38:53 +00:00
|
|
|
|
self.content_group = VerticalGroup:new{
|
|
|
|
|
align = "left",
|
|
|
|
|
self.vertical_span,
|
|
|
|
|
body,
|
2014-03-13 13:52:43 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
2018-01-13 22:38:53 +00:00
|
|
|
|
self.content_group = VerticalGroup:new{
|
|
|
|
|
align = "left",
|
|
|
|
|
header,
|
|
|
|
|
self.vertical_span,
|
|
|
|
|
body,
|
2014-03-13 13:52:43 +00:00
|
|
|
|
}
|
|
|
|
|
end
|
2018-01-13 22:38:53 +00:00
|
|
|
|
local content = OverlapGroup:new{
|
|
|
|
|
dimen = self.dimen:copy(),
|
|
|
|
|
self.content_group,
|
|
|
|
|
page_return,
|
|
|
|
|
footer,
|
|
|
|
|
}
|
2014-03-13 13:52:43 +00:00
|
|
|
|
|
|
|
|
|
self[1] = FrameContainer:new{
|
2014-10-22 13:34:11 +00:00
|
|
|
|
background = Blitbuffer.COLOR_WHITE,
|
2014-03-13 13:52:43 +00:00
|
|
|
|
bordersize = self.is_borderless and 0 or 2,
|
|
|
|
|
padding = 0,
|
|
|
|
|
margin = 0,
|
2014-11-27 14:01:34 +00:00
|
|
|
|
radius = self.is_popout and math.floor(self.dimen.w/20) or 0,
|
2014-03-13 13:52:43 +00:00
|
|
|
|
content
|
|
|
|
|
}
|
|
|
|
|
------------------------------------------
|
|
|
|
|
-- start to set up input event callback --
|
|
|
|
|
------------------------------------------
|
|
|
|
|
if Device:isTouchDevice() then
|
|
|
|
|
if self.has_close_button then
|
2017-10-10 20:23:25 +00:00
|
|
|
|
table.insert(self.title_bar, MenuCloseButton:new{
|
|
|
|
|
menu = self,
|
|
|
|
|
padding_right = self.header_padding,
|
|
|
|
|
})
|
2014-03-13 13:52:43 +00:00
|
|
|
|
end
|
|
|
|
|
-- watch for outer region if it's a self contained widget
|
|
|
|
|
if self.is_popout then
|
|
|
|
|
self.ges_events.TapCloseAllMenus = {
|
|
|
|
|
GestureRange:new{
|
|
|
|
|
ges = "tap",
|
|
|
|
|
range = Geom:new{
|
|
|
|
|
x = 0, y = 0,
|
|
|
|
|
w = Screen:getWidth(),
|
|
|
|
|
h = Screen:getHeight(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
self.ges_events.Swipe = {
|
|
|
|
|
GestureRange:new{
|
|
|
|
|
ges = "swipe",
|
|
|
|
|
range = self.dimen,
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-06-10 13:12:11 +00:00
|
|
|
|
self.ges_events.Close = self.on_close_ges
|
2014-03-13 13:52:43 +00:00
|
|
|
|
end
|
2014-09-03 04:09:25 +00:00
|
|
|
|
|
2014-10-30 18:42:18 +00:00
|
|
|
|
if not Device:hasKeyboard() then
|
2014-09-03 04:09:25 +00:00
|
|
|
|
-- remove menu item shortcut for K4
|
|
|
|
|
self.is_enable_shortcut = false
|
|
|
|
|
end
|
|
|
|
|
|
2014-06-10 07:57:10 +00:00
|
|
|
|
if Device:hasKeys() then
|
2014-03-13 13:52:43 +00:00
|
|
|
|
-- set up keyboard events
|
2014-06-05 06:58:53 +00:00
|
|
|
|
self.key_events.Close = { {"Back"}, doc = "close menu" }
|
2014-03-13 13:52:43 +00:00
|
|
|
|
self.key_events.NextPage = {
|
2014-06-05 06:58:53 +00:00
|
|
|
|
{Input.group.PgFwd}, doc = "goto next page of the menu"
|
2014-03-13 13:52:43 +00:00
|
|
|
|
}
|
|
|
|
|
self.key_events.PrevPage = {
|
2014-06-05 06:58:53 +00:00
|
|
|
|
{Input.group.PgBack}, doc = "goto previous page of the menu"
|
2014-03-13 13:52:43 +00:00
|
|
|
|
}
|
|
|
|
|
-- we won't catch presses to "Right", leave that to MenuItem.
|
|
|
|
|
self.key_events.FocusRight = nil
|
|
|
|
|
-- shortcut icon is not needed for touch device
|
|
|
|
|
if self.is_enable_shortcut then
|
|
|
|
|
self.key_events.SelectByShortCut = { {self.item_shortcuts} }
|
|
|
|
|
end
|
|
|
|
|
self.key_events.Select = {
|
2014-06-05 06:58:53 +00:00
|
|
|
|
{"Press"}, doc = "select current menu item"
|
2014-03-13 13:52:43 +00:00
|
|
|
|
}
|
2018-03-18 10:42:35 +00:00
|
|
|
|
self.key_events.Right = {
|
|
|
|
|
{"Right"}, doc = "hold menu item"
|
|
|
|
|
}
|
2014-03-13 13:52:43 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if #self.item_table > 0 then
|
|
|
|
|
-- if the table is not yet initialized, this call
|
|
|
|
|
-- must be done manually:
|
2014-04-04 15:02:29 +00:00
|
|
|
|
self.page = math.ceil((self.item_table.current or 1) / self.perpage)
|
2014-03-13 13:52:43 +00:00
|
|
|
|
end
|
2017-01-10 18:02:46 +00:00
|
|
|
|
if self.path_items then
|
|
|
|
|
self:refreshPath()
|
|
|
|
|
else
|
2017-02-01 15:34:12 +00:00
|
|
|
|
self:updateItems()
|
2017-01-10 18:02:46 +00:00
|
|
|
|
end
|
2012-04-29 15:53:48 +00:00
|
|
|
|
end
|
|
|
|
|
|
2014-12-01 16:21:42 +00:00
|
|
|
|
function Menu:onCloseWidget()
|
2014-12-04 02:09:09 +00:00
|
|
|
|
-- FIXME:
|
|
|
|
|
-- we cannot refresh regionally using the dimen field
|
|
|
|
|
-- because some menus without menu title use VerticalGroup to include
|
|
|
|
|
-- a text widget which is not calculated into the dimen.
|
|
|
|
|
-- For example, it's a dirty hack to use two menus(one this menu and one
|
|
|
|
|
-- touch menu) in the filemanager in order to capture tap gesture to popup
|
|
|
|
|
-- the filemanager menu.
|
2018-06-02 16:10:55 +00:00
|
|
|
|
-- NOTE: For the same reason, don't make it flash,
|
|
|
|
|
-- because that'll trigger when we close the FM and open a book...
|
2014-12-04 02:09:09 +00:00
|
|
|
|
UIManager:setDirty(nil, "partial")
|
2014-12-01 16:21:42 +00:00
|
|
|
|
end
|
|
|
|
|
|
2018-04-06 09:53:11 +00:00
|
|
|
|
function Menu:updatePageInfo(select_number)
|
|
|
|
|
if self.item_group[1] then
|
|
|
|
|
if Device:hasKeys() then
|
|
|
|
|
-- reset focus manager accordingly
|
|
|
|
|
self.selected = { x = 1, y = select_number }
|
|
|
|
|
end
|
|
|
|
|
-- update page information
|
|
|
|
|
self.page_info_text:setText(util.template(_("page %1 of %2"), self.page, self.page_num))
|
|
|
|
|
self.page_info_left_chev:showHide(self.page_num > 1)
|
|
|
|
|
self.page_info_right_chev:showHide(self.page_num > 1)
|
|
|
|
|
self.page_info_first_chev:showHide(self.page_num > 2)
|
|
|
|
|
self.page_info_last_chev:showHide(self.page_num > 2)
|
|
|
|
|
self.page_return_arrow:showHide(self.onReturn ~= nil)
|
|
|
|
|
|
|
|
|
|
self.page_info_left_chev:enableDisable(self.page > 1)
|
|
|
|
|
self.page_info_right_chev:enableDisable(self.page < self.page_num)
|
|
|
|
|
self.page_info_first_chev:enableDisable(self.page > 1)
|
|
|
|
|
self.page_info_last_chev:enableDisable(self.page < self.page_num)
|
|
|
|
|
self.page_return_arrow:enableDisable(#self.paths > 0)
|
|
|
|
|
else
|
|
|
|
|
self.page_info_text:setText(_("No choices available"))
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2012-06-10 18:14:29 +00:00
|
|
|
|
function Menu:updateItems(select_number)
|
2014-11-30 12:04:33 +00:00
|
|
|
|
local old_dimen = self.dimen and self.dimen:copy()
|
2014-03-13 13:52:43 +00:00
|
|
|
|
-- self.layout must be updated for focusmanager
|
|
|
|
|
self.layout = {}
|
|
|
|
|
self.item_group:clear()
|
|
|
|
|
self.page_info:resetLayout()
|
2014-09-05 13:07:21 +00:00
|
|
|
|
self.return_button:resetLayout()
|
2018-01-13 22:38:53 +00:00
|
|
|
|
self.vertical_span:clear()
|
|
|
|
|
self.content_group:resetLayout()
|
2014-03-13 13:52:43 +00:00
|
|
|
|
self:_recalculateDimen()
|
|
|
|
|
|
|
|
|
|
-- default to select the first item
|
|
|
|
|
if not select_number then
|
|
|
|
|
select_number = 1
|
|
|
|
|
end
|
2018-01-13 22:38:53 +00:00
|
|
|
|
--font size between 12 and 18 for better matching
|
|
|
|
|
local infont_size = math.floor(18 - (self.perpage - 6) / 3)
|
|
|
|
|
--font size between 14 and 24 for better matching
|
|
|
|
|
local font_size = math.floor(24 - ((self.perpage - 6)/ 18) * 10 )
|
2014-03-13 13:52:43 +00:00
|
|
|
|
|
2014-12-18 08:16:11 +00:00
|
|
|
|
for c = 1, math.min(self.perpage, #self.item_table) do
|
2014-03-13 13:52:43 +00:00
|
|
|
|
-- calculate index in item_table
|
|
|
|
|
local i = (self.page - 1) * self.perpage + c
|
|
|
|
|
if i <= #self.item_table then
|
|
|
|
|
local item_shortcut = nil
|
|
|
|
|
local shortcut_style = "square"
|
|
|
|
|
if self.is_enable_shortcut then
|
|
|
|
|
-- give different shortcut_style to keys in different
|
|
|
|
|
-- lines of keyboard
|
|
|
|
|
if c >= 11 and c <= 20 then
|
|
|
|
|
--shortcut_style = "rounded_corner"
|
|
|
|
|
shortcut_style = "grey_square"
|
|
|
|
|
end
|
|
|
|
|
item_shortcut = self.item_shortcuts[c]
|
|
|
|
|
end
|
2016-02-14 21:47:36 +00:00
|
|
|
|
local item_tmp = MenuItem:new{
|
|
|
|
|
show_parent = self.show_parent,
|
|
|
|
|
state = self.item_table[i].state,
|
|
|
|
|
state_size = self.state_size or {},
|
2017-02-04 04:26:47 +00:00
|
|
|
|
text = getMenuText(self.item_table[i]),
|
2016-02-14 21:47:36 +00:00
|
|
|
|
mandatory = self.item_table[i].mandatory,
|
2016-12-03 13:19:35 +00:00
|
|
|
|
bold = self.item_table.current == i or self.item_table[i].bold == true,
|
2017-09-22 16:24:38 +00:00
|
|
|
|
dim = self.item_table[i].dim,
|
2018-01-13 22:38:53 +00:00
|
|
|
|
font = "smallinfofont",
|
|
|
|
|
font_size = font_size,
|
|
|
|
|
infont = "infont",
|
|
|
|
|
infont_size = infont_size,
|
2016-02-14 21:47:36 +00:00
|
|
|
|
dimen = self.item_dimen:new(),
|
|
|
|
|
shortcut = item_shortcut,
|
|
|
|
|
shortcut_style = shortcut_style,
|
|
|
|
|
table = self.item_table[i],
|
|
|
|
|
menu = self,
|
2018-01-13 22:38:53 +00:00
|
|
|
|
linesize = self.linesize,
|
|
|
|
|
single_line = self.single_line,
|
|
|
|
|
line_color = self.line_color,
|
2016-02-14 21:47:36 +00:00
|
|
|
|
}
|
2014-03-13 13:52:43 +00:00
|
|
|
|
table.insert(self.item_group, item_tmp)
|
|
|
|
|
-- this is for focus manager
|
|
|
|
|
table.insert(self.layout, {item_tmp})
|
|
|
|
|
end -- if i <= self.items
|
|
|
|
|
end -- for c=1, self.perpage
|
2014-07-30 07:00:02 +00:00
|
|
|
|
|
2018-04-06 09:53:11 +00:00
|
|
|
|
self:updatePageInfo(select_number)
|
2014-03-13 13:52:43 +00:00
|
|
|
|
|
2014-11-30 12:04:33 +00:00
|
|
|
|
UIManager:setDirty("all", function()
|
|
|
|
|
local refresh_dimen =
|
|
|
|
|
old_dimen and old_dimen:combine(self.dimen)
|
|
|
|
|
or self.dimen
|
2015-04-26 18:10:18 +00:00
|
|
|
|
return "ui", refresh_dimen
|
2014-11-30 12:04:33 +00:00
|
|
|
|
end)
|
2012-04-29 15:53:48 +00:00
|
|
|
|
end
|
|
|
|
|
|
2014-09-13 06:00:57 +00:00
|
|
|
|
--[[
|
|
|
|
|
the itemnumber paramter determines menu page number after switching item table
|
|
|
|
|
1. itemnumber >= 0
|
|
|
|
|
the page number is calculated with items per page
|
|
|
|
|
2. itemnumber == nil
|
|
|
|
|
the page number is 1
|
|
|
|
|
3. itemnumber is negative number
|
|
|
|
|
the page number is not changed, used when item_table is appended with
|
|
|
|
|
new entries
|
2017-10-21 17:53:56 +00:00
|
|
|
|
|
|
|
|
|
alternatively, itemmatch may be provided as a {key = value} table,
|
|
|
|
|
and the page number will be the page containing the first item for
|
|
|
|
|
which item.key = value
|
2014-09-13 06:00:57 +00:00
|
|
|
|
--]]
|
2017-10-21 17:53:56 +00:00
|
|
|
|
function Menu:switchItemTable(new_title, new_item_table, itemnumber, itemmatch)
|
2014-09-05 13:07:21 +00:00
|
|
|
|
if self.menu_title and new_title then
|
2014-03-13 13:52:43 +00:00
|
|
|
|
self.menu_title.text = new_title
|
|
|
|
|
end
|
2014-07-30 07:00:02 +00:00
|
|
|
|
|
2014-09-13 06:00:57 +00:00
|
|
|
|
if itemnumber == nil then
|
|
|
|
|
self.page = 1
|
|
|
|
|
elseif itemnumber >= 0 then
|
2014-08-12 07:36:23 +00:00
|
|
|
|
self.page = math.ceil(itemnumber / self.perpage)
|
|
|
|
|
end
|
|
|
|
|
|
2017-10-21 17:53:56 +00:00
|
|
|
|
if type(itemmatch) == "table" then
|
|
|
|
|
local key, value = next(itemmatch)
|
|
|
|
|
for num, item in ipairs(new_item_table) do
|
|
|
|
|
if item[key] == value then
|
|
|
|
|
self.page = math.floor((num-1) / self.perpage) + 1
|
|
|
|
|
break
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2014-11-23 10:06:20 +00:00
|
|
|
|
-- make sure current page is in right page range
|
|
|
|
|
local max_pages = math.ceil(#new_item_table / self.perpage)
|
|
|
|
|
if self.page > max_pages then
|
|
|
|
|
self.page = max_pages
|
|
|
|
|
end
|
|
|
|
|
|
2014-03-13 13:52:43 +00:00
|
|
|
|
self.item_table = new_item_table
|
2017-02-01 15:34:12 +00:00
|
|
|
|
self:updateItems()
|
2012-06-11 05:46:19 +00:00
|
|
|
|
end
|
|
|
|
|
|
2018-03-19 15:18:20 +00:00
|
|
|
|
function Menu:onScreenResize(dimen)
|
|
|
|
|
-- @TODO investigate: could this cause minor memory leaks?
|
|
|
|
|
self:init()
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
|
2012-04-29 15:53:48 +00:00
|
|
|
|
function Menu:onSelectByShortCut(_, keyevent)
|
2014-03-13 13:52:43 +00:00
|
|
|
|
for k,v in ipairs(self.item_shortcuts) do
|
|
|
|
|
if k > self.perpage then
|
|
|
|
|
break
|
|
|
|
|
elseif v == keyevent.key then
|
|
|
|
|
if self.item_table[(self.page-1)*self.perpage + k] then
|
|
|
|
|
self:onMenuSelect(self.item_table[(self.page-1)*self.perpage + k])
|
|
|
|
|
end
|
|
|
|
|
break
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return true
|
2012-04-29 15:53:48 +00:00
|
|
|
|
end
|
|
|
|
|
|
2012-06-10 18:14:29 +00:00
|
|
|
|
function Menu:onWrapFirst()
|
2014-03-13 13:52:43 +00:00
|
|
|
|
if self.page > 1 then
|
|
|
|
|
self.page = self.page - 1
|
|
|
|
|
local end_position = self.perpage
|
|
|
|
|
if self.page == self.page_num then
|
|
|
|
|
end_position = #self.item_table % self.perpage
|
|
|
|
|
end
|
|
|
|
|
self:updateItems(end_position)
|
|
|
|
|
end
|
|
|
|
|
return false
|
2012-06-10 18:14:29 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function Menu:onWrapLast()
|
2014-03-13 13:52:43 +00:00
|
|
|
|
if self.page < self.page_num then
|
|
|
|
|
self:onNextPage()
|
|
|
|
|
end
|
|
|
|
|
return false
|
2012-06-10 18:14:29 +00:00
|
|
|
|
end
|
|
|
|
|
|
2012-06-10 15:36:19 +00:00
|
|
|
|
--[[
|
|
|
|
|
override this function to process the item selected in a different manner
|
|
|
|
|
]]--
|
|
|
|
|
function Menu:onMenuSelect(item)
|
2014-03-13 13:52:43 +00:00
|
|
|
|
if item.sub_item_table == nil then
|
2017-08-18 08:02:11 +00:00
|
|
|
|
if self.close_callback then
|
|
|
|
|
self.close_callback()
|
|
|
|
|
end
|
2014-03-13 13:52:43 +00:00
|
|
|
|
self:onMenuChoice(item)
|
|
|
|
|
else
|
|
|
|
|
-- save menu title for later resume
|
|
|
|
|
self.item_table.title = self.title
|
|
|
|
|
table.insert(self.item_table_stack, self.item_table)
|
2017-02-01 14:24:21 +00:00
|
|
|
|
self:switchItemTable(item.text, item.sub_item_table)
|
2014-03-13 13:52:43 +00:00
|
|
|
|
end
|
|
|
|
|
return true
|
2012-06-10 15:36:19 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--[[
|
2014-03-13 13:52:43 +00:00
|
|
|
|
default to call item callback
|
|
|
|
|
override this function to handle the choice
|
2013-02-02 19:35:25 +00:00
|
|
|
|
--]]
|
2012-06-10 15:36:19 +00:00
|
|
|
|
function Menu:onMenuChoice(item)
|
2014-03-13 13:52:43 +00:00
|
|
|
|
if item.callback then
|
|
|
|
|
item.callback()
|
|
|
|
|
end
|
|
|
|
|
return true
|
2012-06-10 15:36:19 +00:00
|
|
|
|
end
|
|
|
|
|
|
2014-01-18 15:15:44 +00:00
|
|
|
|
--[[
|
|
|
|
|
override this function to process the item hold in a different manner
|
|
|
|
|
]]--
|
|
|
|
|
function Menu:onMenuHold(item)
|
2014-03-13 13:52:43 +00:00
|
|
|
|
return true
|
2014-01-18 15:15:44 +00:00
|
|
|
|
end
|
|
|
|
|
|
2012-04-29 15:53:48 +00:00
|
|
|
|
function Menu:onNextPage()
|
2014-09-05 13:07:21 +00:00
|
|
|
|
if self.onNext and self.page == self.page_num - 1 then
|
|
|
|
|
self:onNext()
|
|
|
|
|
end
|
2014-03-13 13:52:43 +00:00
|
|
|
|
if self.page < self.page_num then
|
|
|
|
|
self.page = self.page + 1
|
2017-02-01 15:34:12 +00:00
|
|
|
|
self:updateItems()
|
2014-03-13 13:52:43 +00:00
|
|
|
|
elseif self.page == self.page_num then
|
|
|
|
|
-- on the last page, we check if we're on the last item
|
|
|
|
|
local end_position = #self.item_table % self.perpage
|
|
|
|
|
if end_position == 0 then
|
|
|
|
|
end_position = self.perpage
|
|
|
|
|
end
|
|
|
|
|
if end_position ~= self.selected.y then
|
|
|
|
|
self:updateItems(end_position)
|
|
|
|
|
end
|
2015-08-01 09:44:40 +00:00
|
|
|
|
self.page = 1
|
2017-02-01 15:34:12 +00:00
|
|
|
|
self:updateItems()
|
2014-03-13 13:52:43 +00:00
|
|
|
|
end
|
|
|
|
|
return true
|
2012-04-29 15:53:48 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function Menu:onPrevPage()
|
2014-03-13 13:52:43 +00:00
|
|
|
|
if self.page > 1 then
|
|
|
|
|
self.page = self.page - 1
|
2015-08-01 09:44:40 +00:00
|
|
|
|
elseif self.page == 1 then
|
2015-08-01 09:50:10 +00:00
|
|
|
|
self.page = self.page_num
|
2014-03-13 13:52:43 +00:00
|
|
|
|
end
|
2017-02-01 15:34:12 +00:00
|
|
|
|
self:updateItems()
|
2014-03-13 13:52:43 +00:00
|
|
|
|
return true
|
2012-04-29 15:53:48 +00:00
|
|
|
|
end
|
|
|
|
|
|
2014-07-30 07:00:02 +00:00
|
|
|
|
function Menu:onFirstPage()
|
|
|
|
|
self.page = 1
|
2017-02-01 15:34:12 +00:00
|
|
|
|
self:updateItems()
|
2014-07-30 07:00:02 +00:00
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function Menu:onLastPage()
|
|
|
|
|
self.page = self.page_num
|
2017-02-01 15:34:12 +00:00
|
|
|
|
self:updateItems()
|
2014-07-30 07:00:02 +00:00
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
|
2015-04-13 06:45:02 +00:00
|
|
|
|
function Menu:onGotoPage(page)
|
|
|
|
|
self.page = page
|
2017-02-01 15:34:12 +00:00
|
|
|
|
self:updateItems()
|
2015-04-13 06:45:02 +00:00
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
|
2012-05-27 06:03:13 +00:00
|
|
|
|
function Menu:onSelect()
|
2016-06-27 16:45:01 +00:00
|
|
|
|
local item = self.item_table[(self.page-1)*self.perpage+self.selected.y]
|
|
|
|
|
if item then
|
|
|
|
|
self:onMenuSelect(item)
|
|
|
|
|
end
|
2014-03-13 13:52:43 +00:00
|
|
|
|
return true
|
2012-05-27 06:03:13 +00:00
|
|
|
|
end
|
|
|
|
|
|
2018-03-18 10:42:35 +00:00
|
|
|
|
function Menu:onRight()
|
|
|
|
|
local item = self.item_table[(self.page-1)*self.perpage+self.selected.y]
|
|
|
|
|
if item then
|
|
|
|
|
self:onMenuHold(item)
|
|
|
|
|
end
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
|
2012-04-29 15:53:48 +00:00
|
|
|
|
function Menu:onClose()
|
2014-03-13 13:52:43 +00:00
|
|
|
|
local table_length = #self.item_table_stack
|
|
|
|
|
if table_length == 0 then
|
|
|
|
|
self:onCloseAllMenus()
|
|
|
|
|
else
|
|
|
|
|
-- back to parent menu
|
2016-01-03 07:44:23 +00:00
|
|
|
|
local parent_item_table = table.remove(self.item_table_stack, table_length)
|
2017-02-01 14:24:21 +00:00
|
|
|
|
self:switchItemTable(parent_item_table.title, parent_item_table)
|
2014-03-13 13:52:43 +00:00
|
|
|
|
end
|
|
|
|
|
return true
|
2012-04-29 15:53:48 +00:00
|
|
|
|
end
|
2012-06-10 15:36:19 +00:00
|
|
|
|
|
2012-12-08 17:18:40 +00:00
|
|
|
|
function Menu:onCloseAllMenus()
|
2014-03-13 13:52:43 +00:00
|
|
|
|
UIManager:close(self)
|
|
|
|
|
if self.close_callback then
|
|
|
|
|
self.close_callback()
|
|
|
|
|
end
|
|
|
|
|
return true
|
2012-12-08 17:18:40 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function Menu:onTapCloseAllMenus(arg, ges_ev)
|
2014-03-13 13:52:43 +00:00
|
|
|
|
if ges_ev.pos:notIntersectWith(self.dimen) then
|
|
|
|
|
self:onCloseAllMenus()
|
|
|
|
|
return true
|
|
|
|
|
end
|
2012-12-08 17:18:40 +00:00
|
|
|
|
end
|
|
|
|
|
|
2012-12-30 05:56:21 +00:00
|
|
|
|
function Menu:onSwipe(arg, ges_ev)
|
2014-03-13 13:52:43 +00:00
|
|
|
|
if ges_ev.direction == "west" then
|
2016-02-14 21:47:36 +00:00
|
|
|
|
self:onNextPage()
|
2014-03-13 13:52:43 +00:00
|
|
|
|
elseif ges_ev.direction == "east" then
|
2016-02-14 21:47:36 +00:00
|
|
|
|
self:onPrevPage()
|
2018-04-06 09:53:11 +00:00
|
|
|
|
else
|
|
|
|
|
-- trigger full refresh
|
|
|
|
|
UIManager:setDirty(nil, "full")
|
2014-03-13 13:52:43 +00:00
|
|
|
|
end
|
2012-12-30 05:56:21 +00:00
|
|
|
|
end
|
2013-10-18 20:38:07 +00:00
|
|
|
|
|
2014-10-25 08:02:42 +00:00
|
|
|
|
function Menu.itemTableFromTouchMenu(t)
|
|
|
|
|
local item_t = {}
|
|
|
|
|
for k,v in pairs(t) do
|
|
|
|
|
local item = { text = k }
|
|
|
|
|
if v.callback then
|
|
|
|
|
item.callback = v.callback
|
|
|
|
|
else
|
|
|
|
|
item.sub_item_table = v
|
|
|
|
|
end
|
|
|
|
|
table.insert(item_t, item)
|
|
|
|
|
end
|
|
|
|
|
return item_t
|
|
|
|
|
end
|
|
|
|
|
|
2013-10-18 20:38:07 +00:00
|
|
|
|
return Menu
|