note
description: "[
When a SD_FLOATING_TOOL_BAR_ZONE is resizing by user.
SD_TOOL_BAR_GROUP_DIVIDER will calculate best grouping.
It will get total minmum size.
]"
legal: "See notice at end of class."
status: "See notice at end of class."
date: "$Date$"
revision: "$Revision$"
class
SD_TOOL_BAR_GROUP_DIVIDER
create
make_with_content
feature {NONE} -- Initlization
make_with_content (a_content: SD_TOOL_BAR_CONTENT)
-- Make with a_content
require
not_void: a_content /= Void
local
l_group_count: INTEGER
do
l_group_count := a_content.groups_count (False)
content := a_content
create algorithm.make (l_group_count, init_group_width (a_content.items_visible))
create internal_refined_grouping.make
init_grouping_infos
ensure
set: content = a_content
end
init_group_width (a_items: LIST [SD_TOOL_BAR_ITEM]): ARRAYED_LIST [INTEGER]
-- Initlizatie a list with each items width in it
local
l_temp_group: ARRAYED_LIST [SD_TOOL_BAR_ITEM]
do
create Result.make (1)
from
create l_temp_group.make (1)
a_items.start
until
a_items.after
loop
if attached {SD_TOOL_BAR_SEPARATOR} a_items.item then
if l_temp_group.count > 0 then
Result.extend (group_width (l_temp_group))
create l_temp_group.make (1)
end
else
l_temp_group.extend (a_items.item)
end
a_items.forth
end
if l_temp_group.count > 0 then
Result.extend (group_width (l_temp_group))
end
ensure
group_count_right: Result.count = content.groups_count (False)
end
init_grouping_infos
-- Initlialize grouping infos
local
l_count: INTEGER
l_max_group_count: INTEGER
l_refined_grouping: like internal_refined_grouping
do
create grouping_algorithm.make (10)
from
l_count := 1
l_max_group_count := content.item_count_except_sep (False)
create group_infos.make (1)
until
l_count > l_max_group_count
loop
group_items (l_count)
l_refined_grouping := internal_refined_grouping
check l_refined_grouping /= Void then end -- Implied by postcondition of `group_items'
check not_has: not group_infos.has_item (l_refined_grouping) end
group_infos.put (l_refined_grouping, l_count)
debug ("docking")
print ("%N SD_TOOL_BAR_GROUP_DIVIDER init_grouping_infos: when group count is: " + l_count.out)
print ("%N " + l_refined_grouping.out)
end
l_count := l_count + 1
end
end
feature -- Query
group_width (a_group: ARRAYED_LIST [SD_TOOL_BAR_ITEM]): INTEGER
-- Group total item width
require
not_void: a_group /= Void
do
from
a_group.start
until
a_group.after
loop
Result := Result + tool_bar_item_width (a_group.item)
a_group.forth
end
ensure
valid: Result >= 0
end
group_item_width (a_items: LIST [SD_TOOL_BAR_ITEM]): ARRAYED_LIST [INTEGER]
-- Initlizatie a list with each item width in it
do
create Result.make (1)
from
a_items.start
until
a_items.after
loop
if attached {SD_TOOL_BAR_SEPARATOR} a_items.item then
check must_not_contain_separator: False end
else
Result.extend (tool_bar_item_width (a_items.item))
end
a_items.forth
end
ensure
group_count_right: Result.count = a_items.count
end
tool_bar_item_width (a_tool_bar_item: SD_TOOL_BAR_ITEM): INTEGER
-- Tool bar item width of `a_tool_bar_item'
require
not_void: a_tool_bar_item /= Void
do
Result := a_tool_bar_item.width
ensure
valid: Result >= 0
end
group_count: INTEGER
-- Group count
do
check not_void: algorithm /= Void end
Result := algorithm.group_count
end
best_grouping_by_width_to_right (a_width: INTEGER): SD_TOOL_BAR_GROUP_INFO
-- Best groupinp, Result is calculated by `a_width'
require
valid: a_width > 0
local
l_list : ARRAYED_LIST [SD_TOOL_BAR_GROUP_INFO]
do
from
last_group_index := group_infos.count
l_list := group_infos.linear_representation
l_list.finish
until
l_list.before or attached Result
loop
if (not l_list.item.has_any_sub_info and a_width < l_list.item.maximum_width) or
(l_list.item.has_any_sub_info and a_width < l_list.item.maximum_width_sub) then
Result := l_list.item
else
last_group_index := last_group_index - 1
l_list.back
end
end
if not attached Result then
Result := l_list.first
last_group_index := 1
end
ensure
not_void: Result /= Void
valid: last_group_index >= 1 and last_group_index <= group_infos.count
end
best_grouping_by_width_to_left (a_width: INTEGER): SD_TOOL_BAR_GROUP_INFO
-- Best groupinp, Result is calculated by `a_width'
-- When pointer is moving to left, use this feature
require
valid: a_width > 0
local
l_list : ARRAYED_LIST [SD_TOOL_BAR_GROUP_INFO]
do
from
last_group_index := 1
l_list := group_infos.linear_representation
l_list.start
until
l_list.after or attached Result
loop
if (not l_list.item.has_any_sub_info and a_width > l_list.item.maximum_width) or
(l_list.item.has_any_sub_info and a_width > l_list.item.maximum_width_sub) then
Result := l_list.item
else
last_group_index := last_group_index + 1
l_list.forth
end
end
if not attached Result then
Result := l_list.last
last_group_index := l_list.count
end
ensure
not_void: Result /= Void
valid: last_group_index >= 1 and last_group_index <= group_infos.count
end
last_group_index: INTEGER
-- Last grouping info index
best_grouping (a_group_count: INTEGER): detachable SD_TOOL_BAR_GROUP_INFO
-- Best grouping.
require
valid: a_group_count >= 1 and a_group_count <= content.item_count_except_sep (False)
do
Result := group_infos.item (a_group_count)
end
max_row_count: INTEGER
-- Actual maximum row count
do
Result := content.item_count_except_sep (False)
end
content: SD_TOOL_BAR_CONTENT
-- Tool bar content managed by Current
algorithm: SD_HUFFMAN_ALGORITHM
-- Algorithm for grouping
feature {NONE} -- Implementation
insert_arrayed_list_to_group_info_sub_level (a_list: ARRAYED_LIST [ARRAYED_LIST [INTEGER]]; a_sub_group_index: INTEGER; a_widths: ARRAYED_LIST [INTEGER])
-- Insert `a_list' to `internal_refined_grouping'
require
internal_refined_grouping_attached: internal_refined_grouping /= Void
not_void: a_list /= Void
valid: a_sub_group_index > 0 and a_sub_group_index <= content.groups_count (False)
valid: list_and_width_equal (a_list, a_widths)
local
l_sub_group_info: SD_TOOL_BAR_GROUP_INFO
do
if attached internal_refined_grouping as l_refined_grouping then
l_refined_grouping.go_i_th (a_sub_group_index)
-- `position_groups_imp' from SD_FLOATING_TOOL_BAR_ZONE_ASSISTANT count base on total items in sub group.
l_sub_group_info := convert_arrayed_list_to_group_info (a_list, False, a_widths)
l_refined_grouping.set_sub_group_info (l_sub_group_info, a_sub_group_index)
else
check from_precondition_internal_refined_grouping_attached: False end
end
end
list_and_width_equal (a_list: ARRAYED_LIST [ARRAYED_LIST [INTEGER]]; a_items_width: ARRAYED_LIST [INTEGER]): BOOLEAN
-- Is items count in `a_list' equal to `a_items_width''s count?
require
not_void: a_list /= Void
not_void: a_items_width /= Void
local
l_count: INTEGER
do
from
a_list.start
until
a_list.after
loop
l_count := l_count + a_list.item.count
a_list.forth
end
Result := l_count = a_items_width.count
end
convert_arrayed_list_to_group_info (a_list: ARRAYED_LIST [ARRAYED_LIST [INTEGER]]; a_count_clear: BOOLEAN; a_items_width: ARRAYED_LIST [INTEGER]): SD_TOOL_BAR_GROUP_INFO
-- Only convert first level
-- Item is one group which has several items
require
not_void: a_list /= Void
not_void: a_items_width /= Void
valid: list_and_width_equal (a_list, a_items_width)
local
l_one_group: ARRAYED_LIST [INTEGER]
l_group_width: INTEGER
l_item: HASH_TABLE [INTEGER, INTEGER]
l_item_count: INTEGER
do
from
l_item_count := 1
create Result.make
a_list.start
until
a_list.after
loop
from
l_one_group := a_list.item
create l_item.make (10)
l_group_width := 0
if a_count_clear then
l_item_count := 1
end
l_one_group.start
until
l_one_group.after
loop
l_item.extend (a_items_width.i_th (l_one_group.item), l_item_count)
l_group_width := l_one_group.item + l_group_width
l_item_count := l_item_count + 1
l_one_group.forth
end
Result.extend (l_item, a_list.index /= 1)
a_list.forth
end
ensure
not_void: Result /= Void
end
group_items (a_group_count: INTEGER)
-- Group items
local
l_row_left: INTEGER
l_group: ARRAYED_LIST [ARRAYED_LIST [INTEGER]]
do
if a_group_count >= 1 then
if a_group_count > algorithm.max_group_count then
l_group := algorithm.best_grouping_when (algorithm.max_group_count)
internal_refined_grouping := convert_arrayed_list_to_group_info (l_group, False, algorithm.item_width)
else
l_group := algorithm.best_grouping_when (a_group_count)
internal_refined_grouping := convert_arrayed_list_to_group_info (l_group, False, algorithm.item_width)
end
if a_group_count > content.groups_count (False) and a_group_count <= content.item_count_except_sep (False) then
l_row_left := a_group_count - content.groups_count (False)
debug ("docking")
print ("%N SD_TOOL_BAR_GROUP_DIVIDER on_pointer_motion l_row_left is: " + l_row_left.out)
end
compute_extra_groups (l_row_left)
end
end
ensure
created: a_group_count > 1 implies internal_refined_grouping /= Void
end
grouping_algorithm_factory (a_max_width_group_index: INTEGER): SD_HUFFMAN_ALGORITHM
-- Factory method of algorithm
require
valid: a_max_width_group_index > 0 and a_max_width_group_index <= content.groups_count (False)
local
l_sub_group_item_count: INTEGER
do
Result := grouping_algorithm.item (a_max_width_group_index)
if not attached Result then
l_sub_group_item_count := content.group_items (a_max_width_group_index, False).count
create Result.make (l_sub_group_item_count,
group_item_width (content.group_items (a_max_width_group_index, False)))
grouping_algorithm.extend (Result, a_max_width_group_index)
end
ensure
not_void: Result /= Void
end
compute_extra_groups (a_extra_group_count: INTEGER)
-- Compute extra groups when all top groups are all in separated row
require
valid: a_extra_group_count > 0
set: internal_refined_grouping /= Void
local
l_max_group_info: SD_TOOL_BAR_GROUP_INFO
l_temp_algorithm: SD_HUFFMAN_ALGORITHM
l_reduced: INTEGER
l_stop: BOOLEAN
l_max_info: TUPLE [max_group_index: INTEGER; max_row_item_count: INTEGER]
l_refined_grouping: like internal_refined_grouping
do
from
l_refined_grouping := internal_refined_grouping
check l_refined_grouping /= Void end -- Implied by precondition `set'
until
l_reduced >= a_extra_group_count or l_stop
loop
l_max_group_info := l_refined_grouping.maximum_width_top_group
l_max_group_info.start
l_max_info := l_refined_grouping.maximum_width_group_index
-- Is there only one item in max width row?
if l_max_info.max_row_item_count <= 1 then
l_stop := True
end
if not l_stop then
l_temp_algorithm := grouping_algorithm_factory (l_max_info.max_group_index)
if is_initialized_sub_group (l_max_group_info) and then l_temp_algorithm.max_group_count < l_max_group_info.group_count + 1 then
-- The maximum width row is already one item per row
l_stop := True
else
-- Should not pass a_extra
l_reduced := l_reduced + 1
-- Because if `l_max_group_info' is not sub group info, then group count will be 1 larger than actual
if not is_initialized_sub_group (l_max_group_info) then
-- Is not from a sub group info
-- Or from sub group info which item count = 1
if l_temp_algorithm.max_group_count < 2 then
-- `l_temp_algorithm' is maximum width group which only have one item, so we stop here
l_stop := True
else
insert_arrayed_list_to_group_info_sub_level
(l_temp_algorithm.best_grouping_when (2), l_max_info.max_group_index, l_temp_algorithm.item_width)
end
else
-- Is from a sub group info
l_max_group_info.start
insert_arrayed_list_to_group_info_sub_level
(l_temp_algorithm.best_grouping_when (l_max_group_info.group_count + 1), l_max_info.max_group_index, l_temp_algorithm.item_width)
end
end
end
end
end
is_initialized_sub_group (a_group_info: SD_TOOL_BAR_GROUP_INFO): BOOLEAN
-- If `a_group_info' which is subgroup infornation initlialized?
require
not_void: a_group_info /= Void
local
l_snapshot: SD_TOOL_BAR_GROUP_INFO
do
l_snapshot := a_group_info.deep_twin
l_snapshot.start
Result := not (l_snapshot.count = 1 and l_snapshot.is_new_group)
end
group_infos: HASH_TABLE [SD_TOOL_BAR_GROUP_INFO, INTEGER]
-- Group informations
grouping_algorithm: HASH_TABLE [SD_HUFFMAN_ALGORITHM, INTEGER]
-- We store algorithm here, so it'll not need recompute
-- 2nd INTEGER is sub group index count
internal_refined_grouping: SD_TOOL_BAR_GROUP_INFO
-- Refined grouping info
invariant
not_void: algorithm /= Void
note
library: "SmartDocking: Library of reusable components for Eiffel."
copyright: "Copyright (c) 1984-2017, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end