From c6aa92d0283c32fe8f2fc78c8104fc2e8af4460c Mon Sep 17 00:00:00 2001 From: Charles Dang Date: Fri, 11 Mar 2016 01:54:42 +1100 Subject: [PATCH] taddon_list: initial prototype for new full-window GUI2 addons manager This is still behind the --new_widgets guard and is not ready for deployment yet --- data/gui/default/window/addon_list.cfg | 1373 +++++++++++++++++------- src/gui/dialogs/addon_list.cpp | 355 +++++- src/gui/dialogs/addon_list.hpp | 18 +- 3 files changed, 1318 insertions(+), 428 deletions(-) diff --git a/data/gui/default/window/addon_list.cfg b/data/gui/default/window/addon_list.cfg index 38b0532becc2..84cb683d646c 100644 --- a/data/gui/default/window/addon_list.cfg +++ b/data/gui/default/window/addon_list.cfg @@ -3,523 +3,1150 @@ ### Definition of the window to select an addon for installation. ### -[window] - id = "addon_list" - description = "Addon selection dialog." +#define _GUI_ADDON_LIST_SECTION + [grid] - [resolution] - definition = "default" + [row] + grow_factor = 1 - automatic_placement = "true" - vertical_placement = "center" - horizontal_placement = "center" + [column] + grow_factor = 1 - maximum_height = 650 + border = "all" + border_size = 5 - [linked_group] - id = "icon" - fixed_width = "true" - [/linked_group] + horizontal_grow = "true" + vertical_grow = "true" - [linked_group] - id = "name" - fixed_width = "true" - [/linked_group] + [listbox] + id = "addons" + definition = "default" - [linked_group] - id = "version" - fixed_width = "true" - [/linked_group] + horizontal_scrollbar_mode = "never" - [linked_group] - id = "author" - fixed_width = "true" - [/linked_group] + [header] + + [row] + grow_factor = 1 - [linked_group] - id = "downloads" - fixed_width = "true" - [/linked_group] + [column] + grow_factor = 1 + horizontal_grow = "true" - [linked_group] - id = "size" - fixed_width = "true" - [/linked_group] + [spacer] + linked_group = "icon" + [/spacer] + [/column] - [linked_group] - id = "toggle" - fixed_width = "true" - [/linked_group] + [column] + grow_factor = 1 + horizontal_grow = "true" - [linked_group] - id = "list_item" - fixed_width = "true" - [/linked_group] + [toggle_button] + id = "sort_name" + definition = "listbox_header" + linked_group = "name" - [tooltip] - id = "tooltip_large" - [/tooltip] + label = _ "Name" + [/toggle_button] - [helptip] - id = "tooltip_large" - [/helptip] + [/column] - [grid] + [column] + grow_factor = 1 + horizontal_grow = "true" - [row] - grow_factor = 0 + border = "left" + border_size = 5 - [column] - grow_factor = 1 + [label] + definition = "default" + linked_group = "version" + label = _ "Version" + [/label] - border = "all" - border_size = 5 - horizontal_alignment = "left" - [label] - definition = "title" + [/column] - label = "Get Add-ons" - [/label] + [column] + grow_factor = 1 + horizontal_grow = "true" - [/column] + [toggle_button] + id = "sort_author" + definition = "listbox_header" + linked_group = "author" - [/row] + label = _ "Author" + [/toggle_button] - [row] - grow_factor = 0 + [/column] - [column] - grow_factor = 1 + [column] + grow_factor = 1 + horizontal_grow = "true" - border = "all" - border_size = 5 - horizontal_alignment = "left" + [toggle_button] + id = "sort_size" + definition = "listbox_header" + linked_group = "size" - [label] - definition = "default" + label = _ "Size" + [/toggle_button] - label = "Choose the add-on to download." - [/label] + [/column] + + [column] + grow_factor = 1 + horizontal_grow = "true" - [/column] + [toggle_button] + id = "sort_downloads" + definition = "listbox_header" + linked_group = "downloads" - [/row] + label = _ "Downloads" + [/toggle_button] - [row] - grow_factor = 0 + [/column] + + [column] + grow_factor = 1 + horizontal_grow = "true" + + [toggle_button] + id = "sort_size" + definition = "listbox_header" + linked_group = "type" + + label = _ "Type" + [/toggle_button] + [/column] - [column] - horizontal_alignment = "left" + [/row] - [grid] + [/header] + + [list_definition] [row] - grow_factor = 1 [column] - border = "all" - border_size = 5 - horizontal_alignment = "left" + vertical_grow = "true" + horizontal_grow = "true" - [label] + [toggle_panel] + ## linked_group = "list_item" definition = "default" - label = "Filter:" - [/label] + return_value_id = "ok" - [/column] + [grid] - [column] - border = "all" - border_size = 5 - horizontal_alignment = "left" + [row] - [text_box] - id = "filter" - definition = "default" + [column] + horizontal_grow = "false" + vertical_alignment = "center" + + border = "all" + border_size = 5 + + [drawing] + id = "icon" + definition = "default" + linked_group = "icon" + + width = 72 + height = 72 + + [draw] + + [image] + name = "(text)" + w = "(min(image_original_width, 72))" + h = "(min(image_original_height, 72))" + [/image] + + [/draw] + + [/drawing] + + [/column] + + [column] + grow_factor = 1 + horizontal_grow = "true" + vertical_alignment = "center" + + [grid] + + [row] + grow_factor = 1 + + [column] + grow_factor = 1 + horizontal_grow = "true" + + border = "all" + border_size = 5 + + [label] + id = "name" + definition = "default" + linked_group = "name" + wrap = "true" + characters_per_line = 20 + [/label] + [/column] + + [/row] + + [row] + + [column] + grow_factor = 1 + horizontal_grow = "true" + + border = "all" + border_size = 5 + + [label] + id = "installation_status" + definition = "default_small" + linked_group = "name" + [/label] + [/column] + + [/row] + + [/grid] + + [/column] + + [column] + grow_factor = 0 + horizontal_grow = "true" + + border = "all" + border_size = 5 - tooltip = "Filters on addon descripton, version, type or author." - [/text_box] + [label] + id = "version" + definition = "default" + linked_group = "version" + wrap = "true" + [/label] + + [/column] + + [column] + grow_factor = 1 + horizontal_grow = "true" + + border = "all" + border_size = 5 + + [label] + id = "author" + definition = "default" + linked_group = "author" + wrap = "true" + characters_per_line = 20 + [/label] + + [/column] + + [column] + grow_factor = 1 + horizontal_grow = "true" + + border = "all" + border_size = 5 + + [label] + id = "size" + definition = "default" + linked_group = "size" + wrap = "true" + [/label] + + [/column] + + [column] + grow_factor = 1 + horizontal_grow = "true" + + border = "all" + border_size = 5 + + [label] + id = "downloads" + definition = "default" + linked_group = "downloads" + wrap = "true" + [/label] + + [/column] + + [column] + grow_factor = 1 + horizontal_grow = "true" + + border = "all" + border_size = 5 + + [label] + id = "type" + definition = "default" + linked_group = "type" + wrap = "true" + [/label] + + [/column] + + [/row] + + [/grid] + + [/toggle_panel] [/column] [/row] - [/grid] + [/list_definition] - [/column] + [/listbox] - [/row] -### -### The header using listbox own [header] doesn't work becsue of http://gna.org/bugs/?23752 -### - [row] + [/column] + + [/row] + + [/grid] +#enddef + +#define _GUI_ADDON_DETAILS_SECTION + [grid] + + [row] + grow_factor = 1 + + [column] + grow_factor = 0 + border = "all" + border_size = 5 + vertical_grow = "true" + horizontal_alignment = "left" + + [drawing] + id = "image" + definition = "default" + + width = 72 + height = 72 + + [draw] + [image] + name = "(text)" + w = "(min(image_original_width, 72))" + h = "(min(image_original_height, 72))" + [/image] + [/draw] + + [/drawing] + + [/column] + + [column] grow_factor = 1 + horizontal_grow = "true" + vertical_grow = "true" - [column] + [grid] - grow_factor = 1 - horizontal_grow = "true" - [grid] + [row] - [row] - [column] - - grow_factor = 1 - horizontal_grow = "true" - [grid] - ## linked_group = "list_item" - [row] - [column] + [column] + border = "all" + border_size = 5 + horizontal_grow = "true" + vertical_alignment = "top" + + [label] + id = "title" + definition = "title" + label = "Epic Addon Of Awesomeness" + [/label] + + [/column] + + [/row] + + [row] + + [column] + horizontal_alignment = "left" + + [grid] - grow_factor = 0 - border = "all" - border_size = 5 - [spacer] - linked_group = "icon" - [/spacer] - [/column] - [column] - grow_factor = 1 - horizontal_grow = "true" + [row] + + [column] + border = "left,top,bottom" + border_size = 5 + horizontal_alignment = "left" + vertical_alignment = "top" + + [label] + id = "byline" + definition = "default_small" + label = "By:" + [/label] + + [/column] + + [column] + border = "all" + border_size = 5 + horizontal_alignment = "left" + vertical_alignment = "top" - border = "all" - border_size = 5 + [scroll_label] + id = "author" + definition = "default_small" - [toggle_button] - id = "sort_name" - definition = "listbox_header" - linked_group = "name" + horizontal_scrollbar_mode = "never" + [/scroll_label] - label = _ "Name" - [/toggle_button] + [/column] + + [column] - [/column] + [spacer] + width = 10 + [/spacer] - [column] - grow_factor = 1 - horizontal_grow = "true" + [/column] - border = "all" - border_size = 5 + [column] + border = "left,top,bottom" + border_size = 5 + horizontal_alignment = "left" + vertical_alignment = "top" - [label] - definition = "default" - linked_group = "version" - label = _ "Version" - [/label] + [label] + id = "version_string" + definition = "default_small" + label = "Version:" + [/label] - [/column] + [/column] + + [column] + border = "left,top,bottom" + border_size = 5 + horizontal_alignment = "left" + vertical_alignment = "top" - [column] - grow_factor = 1 - horizontal_grow = "true" + [scroll_label] + id = "version" + definition = "default_small" - border = "all" - border_size = 5 + horizontal_scrollbar_mode = "never" + [/scroll_label] - [toggle_button] - id = "sort_author" - definition = "listbox_header" - linked_group = "author" + [/column] + + [/row] + + [/grid] + + [/column] + + [/row] + + [row] - label = _ "Author" - [/toggle_button] + [column] + border = "all" + border_size = 5 + vertical_alignment = "top" + horizontal_alignment = "left" - [/column] + [scroll_label] + id = "description" + definition = "description" + label = _ "No description available." - [column] - grow_factor = 1 - horizontal_grow = "true" + horizontal_scrollbar_mode = "never" + [/scroll_label] - border = "all" - border_size = 5 + [/column] - [toggle_button] - id = "sort_downloads" - definition = "listbox_header" - linked_group = "downloads" + [/row] - label = _ "Downloads" - [/toggle_button] + [row] + grow_factor = 1 - [/column] + [column] + horizontal_grow = "true" + vertical_alignment = "bottom" - [column] - grow_factor = 1 - horizontal_grow = "true" + [grid] - border = "all" - border_size = 5 + [row] + grow_factor = 1 - [toggle_button] - id = "sort_size" - definition = "listbox_header" - linked_group = "size" + [column] + grow_factor = 0 + border = "all" + border_size = 5 + vertical_alignment = "top" + horizontal_alignment = "left" - label = _ "Size" - [/toggle_button] + [label] + definition = "default" + label = _ "Type:" + [/label] - [/column] - - - [column] - grow_factor = 0 - [spacer] - linked_group = "toggle" - [/spacer] - [/column] - [/row] - [/grid] - [/column] - [column] - grow_factor = 0 - ## For the scrollbar - [spacer] - width=25 - [/spacer] - [/column] + [/column] + [column] + grow_factor = 1 + border = "all" + border_size = 5 + horizontal_grow = true + vertical_alignment = "top" - [/row] + [label] + id = "type" + definition = "default" + [/label] - [/grid] + [/column] + + [column] + grow_factor = 0 + border = "all" + border_size = 5 + vertical_alignment = "top" + horizontal_alignment = "left" + + [label] + definition = "default" + label = _ "date^First uploaded:" + [/label] + + [/column] + + [column] + grow_factor = 1 + border = "all" + border_size = 5 + vertical_alignment = "top" + horizontal_grow = true + + [label] + id = "created" + definition = "default" + [/label] + + [/column] + + [/row] + + [row] + + [column] + grow_factor = 0 + border = "all" + border_size = 5 + vertical_alignment = "top" + horizontal_alignment = "left" + + [label] + definition = "default" + label = _ "Status:" + [/label] + + [/column] + + [column] + grow_factor = 1 + border = "all" + border_size = 5 + vertical_alignment = "top" + horizontal_grow = true + + [label] + id = "status" + definition = "default" + wrap = "true" + [/label] + + [/column] + + [column] + grow_factor = 0 + border = "all" + border_size = 5 + vertical_alignment = "top" + horizontal_alignment = "left" + + [label] + definition = "default" + label = _ "date^Last updated:" + [/label] + + [/column] + + [column] + grow_factor = 1 + border = "all" + border_size = 5 + horizontal_grow = true + vertical_alignment = "top" + + [label] + id = "updated" + definition = "default" + [/label] + + [/column] + + [/row] + + [row] + + [column] + grow_factor = 0 + border = "all" + border_size = 5 + vertical_alignment = "top" + horizontal_alignment = "left" + + [label] + definition = "default" + label = _ "Size:" + [/label] + + [/column] - [/column] + [column] + grow_factor = 1 + border = "all" + border_size = 5 + vertical_alignment = "top" + horizontal_grow = true + + [label] + id = "size" + definition = "default" + [/label] - [/row] + [/column] - [row] - grow_factor = 1 + [column] + grow_factor = 0 + border = "all" + border_size = 5 + vertical_alignment = "top" + horizontal_alignment = "left" - [column] - grow_factor = 1 + [label] + definition = "default" + label = _ "Downloads:" + [/label] + + [/column] - border = "all" - border_size = 5 + [column] + grow_factor = 1 + border = "all" + border_size = 5 + vertical_alignment = "top" + horizontal_grow = true - horizontal_grow = "true" - vertical_grow = "true" + [label] + id = "downloads" + definition = "default" + [/label] - [listbox] - id = "addons" - definition = "default" - ### - ### We need the scrollbar becase otherwise the size of the header wouldnt match the size of the content. - ### - vertical_scrollbar_mode = "always" + [/column] - [list_definition] + [/row] - [row] + [/grid] - [column] - vertical_grow = "true" - horizontal_grow = "true" + [/column] - [toggle_panel] - ## linked_group = "list_item" - definition = "default" + [/row] - return_value_id = "ok" - [grid] + [row] + grow_factor = 0 - [row] + [column] + grow_factor = 1 + border = "all" + border_size = 10 + horizontal_grow = true + vertical_alignment = "bottom" - [column] - vertical_grow = "true" - horizontal_grow = "true" + [drawing] + definition = "default" - [grid] + width = (width) + height = 1 - [row] + [draw] - [column] - ## grow_factor = 1 - horizontal_grow = "false" + [line] + x1 = 0 + y1 = 0 + x2 = (width - 1) + y2 = 0 - border = "all" - border_size = 5 + color = {GUI__FONT_COLOR_DISABLED__DEFAULT} + thickness = 1 + [/line] - [drawing] - id = "icon" - definition = "default" - linked_group = "icon" + [/draw] - width = 72 - height = 72 - - [draw] + [/drawing] + + [/column] + + [/row] + + [row] + + [column] + horizontal_grow = "true" + vertical_alignment = "bottom" + + [grid] + + [row] + + [column] + grow_factor = 0 + border = "all" + border_size = 5 + vertical_alignment = "top" + horizontal_alignment = "left" + + [label] + definition = "default" + label = _ "Dependencies:" + [/label] + + [/column] + + [column] + grow_factor = 1 + border = "all" + border_size = 5 + vertical_alignment = "top" + horizontal_alignment = "left" + + [scroll_label] + id = "dependencies" + definition = "default" + label = _ "addon_dependencies^None" + + horizontal_scrollbar_mode = "never" + [/scroll_label] + + [/column] + + [/row] + + [row] + + [column] + grow_factor = 0 + border = "all" + border_size = 5 + vertical_alignment = "top" + horizontal_alignment = "left" + + [label] + definition = "default" + label = _ "Translations:" + [/label] + + [/column] + + [column] + grow_factor = 1 + border = "all" + border_size = 5 + vertical_alignment = "top" + horizontal_alignment = "left" - [image] - name = "(text)" - w = "(min(image_original_width, 72))" - h = "(min(image_original_height, 72))" - [/image] + [scroll_label] + id = "translations" + definition = "default" + label = _ "translations^None" - [/draw] + horizontal_scrollbar_mode = "never" + [/scroll_label] - [/drawing] + [/column] - [/column] + [/row] - [column] - grow_factor = 1 - horizontal_grow = "true" + [row] - border = "all" - border_size = 5 + [column] + grow_factor = 0 + border = "all" + border_size = 5 + horizontal_alignment = "left" - [label] - id = "name" - definition = "default" - linked_group = "name" - [/label] + [label] + definition = "default" + label = _ "Website:" + [/label] - [/column] + [/column] - [column] - grow_factor = 1 - horizontal_grow = "true" + [column] + grow_factor = 1 + horizontal_grow = "true" - border = "all" - border_size = 5 + [stacked_widget] + id = "feedback_stack" - [label] - id = "version" - definition = "default" - linked_group = "version" - [/label] + [stack] - [/column] + [layer] - [column] - grow_factor = 1 - horizontal_grow = "true" + [row] - border = "all" - border_size = 5 + [column] + border = "all" + border_size = 5 + horizontal_alignment = "left" - [label] - id = "author" - definition = "default" - linked_group = "author" - [/label] + [label] + id = "url_none" + label = _ "url^None" + [/label] - [/column] + [/column] - [column] - grow_factor = 1 - horizontal_grow = "true" + [/row] - border = "all" - border_size = 5 + [/layer] - [label] - id = "downloads" - definition = "default" - linked_group = "downloads" - [/label] + [layer] - [/column] + [row] - [column] - grow_factor = 1 - horizontal_grow = "true" + [column] + grow_factor = 1 + border = "all" + border_size = 5 + horizontal_grow = "true" - border = "all" - border_size = 5 + [text_box] + id = "url" + definition = "default" + [/text_box] - [label] - id = "size" - definition = "default" - linked_group = "size" - [/label] + [/column] - [/column] - [column] - grow_factor = 0 + [column] + border = "all" + border_size = 5 + horizontal_alignment = "right" - border = "all" - border_size = 5 - horizontal_alignment = "right" - vertical_alignment = "bottom" - - [toggle_button] - linked_group = "toggle" - id = "expand" - definition = "tree_view_node" - [/toggle_button] + [button] + id = "url_copy" + definition = "action_copy" + label = _ "url^Copy" + tooltip = _ "Copy this URL to clipboard" + [/button] - [/column] - [/row] + [/column] - [/grid] + [column] + border = "all" + border_size = 5 + horizontal_alignment = "right" - [/column] + [button] + id = "url_go" + definition = "action_go" + label = _ "url^Go" + tooltip = _ "Visit this URL with a web browser" + [/button] - [/row] + [/column] - [row] + [/row] - [column] - vertical_grow = "true" - horizontal_grow = "true" + [/layer] - [grid] - id = "description_grid" - [row] + [/stack] - [column] - grow_factor = 1 - vertical_grow = "true" - horizontal_grow = "true" + [/stacked_widget] - border = "all" - border_size = 5 + [/column] - [label] - id = "description" - definition = "default" + [/row] - characters_per_line = 66 - [/label] + [/grid] - [/column] + [/column] -#ifdef __unused__ - [column] - grow_factor = 0 + [/row] - horizontal_alignment = "right" - vertical_alignment = "bottom" - [toggle_button] - id = "collapse" - definition = "default" - [/toggle_button] + [/grid] - [/column] -#endif + [/column] - [/row] + [/row] - [/grid] + [/grid] +#enddef - [/column] +#define _GUI_ADDON_FILTER_OPTIONS + [grid] - [/row] + [row] + grow_factor = 1 - [/grid] + [column] + grow_factor = 0 + border = "all" + border_size = 5 + horizontal_alignment = "left" + + [label] + definition = "default" + + label = "Filter:" + [/label] + [/column] + + [column] + grow_factor = 0 + border = "all" + border_size = 5 + horizontal_alignment = "left" + + [text_box] + id = "filter" + definition = "default" + + tooltip = "Filters on addon descripton, version, type or author." + [/text_box] + [/column] + + [column] + grow_factor = 0 + border = "all" + border_size = 5 + horizontal_alignment = "left" + + [label] + definition = "default" + label = _ "Installation status:" + [/label] + + [/column] + + [column] + grow_factor = 0 + border = "all" + border_size = 5 - [/toggle_panel] + horizontal_alignment = "left" - [/column] + # TODO: maybe do this in the source code? + [combobox] + definition = "default" + [option] + label = "All Add-ons" + [/option] - [/row] + [option] + label = "Installed" + [/option] - [/list_definition] + [option] + label = "Upgradable" + [/option] - [/listbox] + [option] + label = "Not Installed" + [/option] + + [option] + label = "Not Uploaded" + [/option] + [/combobox] + [/column] + + [column] + grow_factor = 1 + border = "all" + border_size = 5 + horizontal_alignment = "right" + + [button] + id = "options" + definition = "default" + + label = "Filter Options" + [/button] + + [/column] + + [/row] + + [/grid] +#enddef + +[window] + id = "addon_list" + description = "Addon selection dialog." + + [resolution] + definition = "borderless" + + automatic_placement = "false" + + x = 0 + y = 0 + + width = "(screen_width)" + height = "(screen_height)" + + [linked_group] + id = "icon" + fixed_width = "true" + [/linked_group] + + [linked_group] + id = "name" + fixed_width = "true" + [/linked_group] + + [linked_group] + id = "version" + fixed_width = "true" + [/linked_group] + + [linked_group] + id = "author" + fixed_width = "true" + [/linked_group] + + [linked_group] + id = "downloads" + fixed_width = "true" + [/linked_group] + + [linked_group] + id = "size" + fixed_width = "true" + [/linked_group] + + [linked_group] + id = "type" + fixed_width = "true" + [/linked_group] + + [linked_group] + id = "toggle" + fixed_width = "true" + [/linked_group] + + [linked_group] + id = "list_item" + fixed_width = "true" + [/linked_group] + + [tooltip] + id = "tooltip_large" + [/tooltip] + + [helptip] + id = "tooltip_large" + [/helptip] + + [grid] + + [row] + grow_factor = 0 + + [column] + grow_factor = 1 + + border = "all" + border_size = 5 + horizontal_alignment = "left" + [label] + definition = "title" + + label = "Get Add-ons" + [/label] + + [/column] + + [/row] + + [row] + + [column] + grow_factor = 1 + horizontal_grow = "true" + vertical_grow = "true" + + border = "top" + border_size = 5 + + {_GUI_ADDON_FILTER_OPTIONS} + + [/column] + + [/row] + + [row] + grow_factor = 1 + + [column] + horizontal_grow = "true" + + [grid] + + [row] + grow_factor = 1 + + [column] + grow_factor = 0 + horizontal_grow = "true" + vertical_alignment = "top" + + {_GUI_ADDON_LIST_SECTION} + + [/column] + + [column] + grow_factor = 1 + horizontal_grow = "true" + vertical_grow = "true" + + {_GUI_ADDON_DETAILS_SECTION} + + [/column] + + [/row] + + [/grid] [/column] @@ -560,7 +1187,9 @@ horizontal_alignment = "left" [button] - id = "help" + # NOTE: this isn't named "help" because it conflicts with + # a titlescreen hack in gui2::twindow::get_retval_by_id(). + id = "show_help" definition = "default" label = "Help" @@ -593,7 +1222,7 @@ id = "cancel" definition = "default" - label = "Cancel" + label = "Exit" [/button] [/column] @@ -611,3 +1240,7 @@ [/resolution] [/window] + +#undef _GUI_ADDON_FILTER_OPTIONS +#undef _GUI_ADDON_LIST_SECTION +#undef _GUI_ADDON_DETAILS_SECTION diff --git a/src/gui/dialogs/addon_list.cpp b/src/gui/dialogs/addon_list.cpp index 01872014d06f..5dc46c4a69e9 100644 --- a/src/gui/dialogs/addon_list.cpp +++ b/src/gui/dialogs/addon_list.cpp @@ -16,11 +16,21 @@ #include "gui/dialogs/addon_list.hpp" +#include "addon/info.hpp" +#include "addon/state.hpp" + +#include "desktop/clipboard.hpp" +#include "desktop/open.hpp" + #include "gettext.hpp" #include "gui/auxiliary/filter.hpp" #include "gui/auxiliary/find_widget.tpp" +#include "gui/dialogs/helper.hpp" #include "gui/widgets/button.hpp" #include "gui/widgets/label.hpp" +#include "gui/widgets/stacked_widget.hpp" +#include "gui/widgets/drawing.hpp" +#include "gui/widgets/image.hpp" #ifdef GUI2_EXPERIMENTAL_LISTBOX #include "gui/widgets/list.hpp" #else @@ -33,8 +43,16 @@ #include "gui/widgets/window.hpp" #include "utils/foreach.tpp" #include "serialization/string_utils.hpp" +#include "formula_string_utils.hpp" +#include "marked-up_text.hpp" +#include "font.hpp" +#include "preferences.hpp" +#include "strftime.hpp" + +#include "config.hpp" #include +#include namespace gui2 { @@ -105,6 +123,17 @@ struct filter_transform const std::vector filtertext_; }; +taddon_list::taddon_list(const config& cfg) + : orders_() + , cfg_(cfg) + , cfg_iterators_(cfg_.child_range("campaign")) + , addons_() + , tracking_info_() + , ids_() +{ + read_addons_list(cfg, addons_); +} + void taddon_list::on_filtertext_changed(ttext_* textbox, const std::string& text) { tlistbox& listbox = find_widget(textbox->get_window(), "addons", true); @@ -187,22 +216,6 @@ void taddon_list::register_sort_button_numeric(twindow& window, const std::strin register_sort_button(window, id, boost::bind(&num_up, &cfg_, prop_id, _1, _2), boost::bind(&num_down, &cfg_, prop_id, _1, _2)); } - -void taddon_list::expand(tgrid& grid, twidget& w) -{ - tselectable_& selectable = dynamic_cast(w); - if(selectable.get_value_bool()) - { - find_widget(&grid, "description", false) - .set_visible(twidget::tvisible::visible); - } - else - { - find_widget(&grid, "description", false) - .set_visible(twidget::tvisible::invisible); - } -} - namespace { bool default_sort(const tpane::titem& i1, const tpane::titem& i2) { @@ -222,6 +235,147 @@ namespace { } } +static inline const addon_info& addon_at(const std::string& id, const addons_list& addons) +{ + addons_list::const_iterator it = addons.find(id); + assert(it != addons.end()); + return it->second; +} + +static std::string describe_addon_status(const addon_tracking_info& info) +{ + std::string tc, tx; + + switch(info.state) { + case ADDON_NONE: + tc = "#a69275"; + tx = info.can_publish ? _("addon_state^Published, not installed") : _("addon_state^Not installed"); + break; + + case ADDON_INSTALLED: + case ADDON_NOT_TRACKED: + // Consider add-ons without version information as installed + // for the main display. Their Description info should elaborate + // on their status. + tc = "#00ff00"; + tx = info.can_publish ? _("addon_state^Published") : _("addon_state^Installed"); + break; + + case ADDON_INSTALLED_UPGRADABLE: + tc = "#ffff00"; + tx = info.can_publish ? _("addon_state^Published, upgradable") : _("addon_state^Installed, upgradable"); + break; + + case ADDON_INSTALLED_OUTDATED: + tc = "#ff7f00"; + tx = info.can_publish ? _("addon_state^Published, outdated on server") : _("addon_state^Installed, outdated on server"); + break; + + case ADDON_INSTALLED_BROKEN: + tc = "#ff0000"; + tx = info.can_publish ? _("addon_state^Published, broken") : _("addon_state^Installed, broken"); + break; + + default: + tc = "#777777"; + tx = _("addon_state^Unknown"); + } + + return "" + tx + ""; +} + +static std::string colorify_addon_state_string(const std::string& str, + const addon_tracking_info& state) +{ + std::string colorname = ""; + + switch(state.state) { + case ADDON_NONE: + return str; + case ADDON_INSTALLED: + case ADDON_NOT_TRACKED: + colorname = "#00ff00"; // GOOD_COLOR + break; + case ADDON_INSTALLED_UPGRADABLE: + colorname = "#ffff00"; // YELLOW_COLOR/color_upgradable + break; + case ADDON_INSTALLED_OUTDATED: + colorname = "#ff7f00"; // <255,127,0>/color_outdated + break; + case ADDON_INSTALLED_BROKEN: + colorname = "#ff0000"; // BAD_COLOR + break; + default: + colorname = "#777777"; // GRAY_COLOR + break; + } + + return "" + str + ""; +} + +static std::string describe_addon_state_info(const addon_tracking_info& state) +{ + std::string s; + + utils::string_map i18n_symbols; + i18n_symbols["local_version"] = state.installed_version.str(); + + switch(state.state) { + case ADDON_NONE: + if(!state.can_publish) { + s = _("addon_state^Not installed"); + } else { + s = _("addon_state^Published, not installed"); + } + break; + case ADDON_INSTALLED: + if(!state.can_publish) { + s = _("addon_state^Installed"); + } else { + s = _("addon_state^Published"); + } + break; + case ADDON_NOT_TRACKED: + if(!state.can_publish) { + s = _("addon_state^Installed, not tracking local version"); + } else { + // Published add-ons often don't have local status information, + // hence untracked. This should be considered normal. + s = _("addon_state^Published, not tracking local version"); + } + break; + case ADDON_INSTALLED_UPGRADABLE: { + const std::string vstr + = !state.can_publish + ? _("addon_state^Installed ($local_version|), " + "upgradable") + : _("addon_state^Published ($local_version| " + "installed), upgradable"); + s = utils::interpolate_variables_into_string(vstr, &i18n_symbols); + } break; + case ADDON_INSTALLED_OUTDATED: { + const std::string vstr + = !state.can_publish + ? _("addon_state^Installed ($local_version|), " + "outdated on server") + : _("addon_state^Published ($local_version| " + "installed), outdated on server"); + s = utils::interpolate_variables_into_string(vstr, &i18n_symbols); + } break; + case ADDON_INSTALLED_BROKEN: + if(!state.can_publish) { + s = _("addon_state^Installed, broken"); + } else { + s = _("addon_state^Published, broken"); + } + break; + default: + s = _("addon_state^Unknown"); + } + + return colorify_addon_state_string(s, state); +} + void taddon_list::pre_show(CVideo& /*video*/, twindow& window) { if(new_widgets) { @@ -264,45 +418,42 @@ void taddon_list::pre_show(CVideo& /*video*/, twindow& window) } else { tlistbox& list = find_widget(&window, "addons", false); - /** - * @todo do we really want to keep the length limit for the various - * items? - */ FOREACH(const AUTO & c, cfg_.child_range("campaign")) { + ids_.push_back(c["name"]); + const addon_info& info = addon_at(ids_.back(), addons_); + tracking_info_[info.id] = get_addon_tracking_info(info); + std::map data; string_map item; - item["label"] = c["icon"]; + item["label"] = info.display_icon(); data.insert(std::make_pair("icon", item)); - utf8::string tmp = c["name"]; - item["label"] = utf8::truncate(tmp, 20); + item["label"] = info.display_title(); data.insert(std::make_pair("name", item)); - tmp = c["version"].str(); - item["label"] = utf8::truncate(tmp, 12); + item["label"] = describe_addon_status(tracking_info_[info.id]); + item["use_markup"] = "true"; + data.insert(std::make_pair("installation_status", item)); + + item["label"] = info.version.str(); data.insert(std::make_pair("version", item)); - tmp = c["author"].str(); - item["label"] = utf8::truncate(tmp, 16); + // utf8::truncate(tmp, 16) + item["label"] = info.author; data.insert(std::make_pair("author", item)); - item["label"] = c["downloads"]; - data.insert(std::make_pair("downloads", item)); - - item["label"] = c["size"]; + item["label"] = size_display_string(info.size); data.insert(std::make_pair("size", item)); - item["label"] = c["description"]; - data.insert(std::make_pair("description", item)); + item["label"] = lexical_cast(info.downloads); + data.insert(std::make_pair("downloads", item)); + + item["label"] = info.display_type(); + data.insert(std::make_pair("type", item)); list.add_row(data); - unsigned index = list.get_item_count() - 1; - ttoggle_button& button = find_widget(list.get_row_grid(list.get_item_count() - 1), "expand", true); - tgrid& lower_grid = find_widget(list.get_row_grid(list.get_item_count() - 1), "description_grid", false); - lower_grid.set_visible(twidget::tvisible::invisible); - button.set_callback_state_change(boost::bind(show_desc_impl, index, boost::ref(list), boost::ref(lower_grid), _1)); } register_sort_button_alphabetical(window, "sort_name", "name"); register_sort_button_alphabetical(window, "sort_author", "author"); @@ -314,6 +465,117 @@ void taddon_list::pre_show(CVideo& /*video*/, twindow& window) filter_box.set_text_changed_callback( boost::bind(&taddon_list::on_filtertext_changed, this, _1, _2)); + +#ifdef GUI2_EXPERIMENTAL_LISTBOX + connect_signal_notify_modified(list, + boost::bind(&taddon_list::on_addon_select, + *this, + boost::ref(window))); +#else + list.set_callback_value_change( + dialog_callback); +#endif + + tbutton& url_go_button = find_widget(&window, "url_go", false); + tbutton& url_copy_button = find_widget(&window, "url_copy", false); + ttext_box& url_textbox = find_widget(&window, "url", false); + + url_textbox.set_active(false); + + if (!desktop::clipboard::available()) { + url_copy_button.set_active(false); + url_copy_button.set_tooltip(_("Clipboard support not found, contact your packager")); + } + + if(!desktop::open_object_is_supported()) { + // No point in displaying the button on platforms that can't do + // open_object(). + url_go_button.set_visible(tcontrol::tvisible::invisible); + } + + //connect_signal_mouse_left_click( + // url_go_button, + // boost::bind(&taddon_description::browse_url_callback, this)); + + //connect_signal_mouse_left_click( + // url_copy_button, + // boost::bind(&taddon_description::copy_url_callback, this)); + + on_addon_select(window); + } +} + +static std::string format_addon_time(time_t time) +{ + if(time) { + char buf[1024] = { 0 }; + struct std::tm* const t = std::localtime(&time); + + const char* format = preferences::use_twelve_hour_clock_format() + ? "%Y-%m-%d %I:%M %p" + : "%Y-%m-%d %H:%M"; + + if(util::strftime(buf, sizeof(buf), format, t)) { + return buf; + } + } + + return utils::unicode_em_dash; +} + +/** + * Retrieves an element from the given associative container or dies in some + * way. + * + * It fails an @a assert() check or throws an exception if the requested element + * does not exist. + * + * @return An element from the container that is guranteed to have existed + * before running this function. + */ +template +static typename MapT::mapped_type const& const_at(typename MapT::key_type const& key, + MapT const& map) +{ + typename MapT::const_iterator it = map.find(key); + if(it == map.end()) { + assert(it != map.end()); + throw std::out_of_range( + "const_at()"); // Shouldn't get here without disabling assert() + } + return it->second; +} + +void taddon_list::on_addon_select(twindow& window) +{ + const int index = find_widget(&window, "addons", false).get_selected_row(); + + const addon_info& info = addon_at(ids_[index], addons_); + + find_widget(&window, "image", false).set_label(info.display_icon()); + + find_widget(&window, "title", false).set_label(info.display_title()); + find_widget(&window, "description", false).set_label(info.description); + find_widget(&window, "version", false).set_label(info.version.str()); + find_widget(&window, "author", false).set_label(info.author); + find_widget(&window, "type", false).set_label(info.display_type()); + + tcontrol& status = find_widget(&window, "status", false); + status.set_label(describe_addon_state_info(tracking_info_[info.id])); + status.set_use_markup(true); + + find_widget(&window, "size", false).set_label(size_display_string(info.size)); + find_widget(&window, "downloads", false).set_label(lexical_cast(info.downloads)); + find_widget(&window, "created", false).set_label(format_addon_time(info.created)); + find_widget(&window, "updated", false).set_label(format_addon_time(info.updated)); + + const std::string& feedback_url = info.feedback_url; + + if(!feedback_url.empty()) { + find_widget(&window, "feedback_stack", false).select_layer(1); + find_widget(&window, "url", false).set_value(feedback_url); + } else { + find_widget(&window, "feedback_stack", false).select_layer(0); } } @@ -339,13 +601,12 @@ void taddon_list::create_campaign(tpane& pane, const config& campaign) item["label"] = utf8::truncate(tmp, 16); data.insert(std::make_pair("author", item)); - item["label"] = campaign["downloads"]; - data.insert(std::make_pair("downloads", item)); - item["label"] = utils::si_string(campaign["size"], true, _("unit_byte^B")); - data.insert(std::make_pair("size", item)); + item["label"] = campaign["downloads"]; + data.insert(std::make_pair("downloads", item)); + item["label"] = campaign["description"]; data.insert(std::make_pair("description", item)); @@ -367,16 +628,6 @@ void taddon_list::create_campaign(tpane& pane, const config& campaign) tgrid* grid = pane.grid(id); assert(grid); - - ttoggle_button* expand - = find_widget(grid, "expand", false, false); - - if(expand) { - expand->set_callback_state_change( - boost::bind(&taddon_list::expand, this, boost::ref(*grid), _1)); - find_widget(grid, "description", false) - .set_visible(twidget::tvisible::invisible); - } } void taddon_list::load(tpane& pane) diff --git a/src/gui/dialogs/addon_list.hpp b/src/gui/dialogs/addon_list.hpp index 0ec4d6432e69..9e49f86d5d74 100644 --- a/src/gui/dialogs/addon_list.hpp +++ b/src/gui/dialogs/addon_list.hpp @@ -15,8 +15,11 @@ #ifndef GUI_DIALOGS_ADDON_LIST_HPP_INCLUDED #define GUI_DIALOGS_ADDON_LIST_HPP_INCLUDED +#include "addon/info.hpp" +#include "addon/state.hpp" + #include "gui/dialogs/dialog.hpp" -#include "../widgets/generator.hpp" +#include "gui/widgets/generator.hpp" #include "gui/widgets/pane.hpp" #include "config.hpp" // needed for config::const_child_itors @@ -31,10 +34,7 @@ class tselectable_; class taddon_list : public tdialog { public: - explicit taddon_list(const config& cfg) - : orders_(), cfg_(cfg), cfg_iterators_(cfg_.child_range("campaign")) - { - } + explicit taddon_list(const config& cfg); private: void register_sort_button(twindow& window, const std::string& id, const tgenerator_::torder_func& up, const tgenerator_::torder_func& down); @@ -52,7 +52,7 @@ class taddon_list : public tdialog * expand. */ void expand(tgrid& grid, twidget& w); - + void on_addon_select(twindow& window); /** Inherited from tdialog, implemented by REGISTER_DIALOG. */ virtual const std::string& window_id() const; @@ -76,6 +76,12 @@ class taddon_list : public tdialog */ config::const_child_itors cfg_iterators_; + addons_list addons_; + + addons_tracking_list tracking_info_; + + std::vector ids_; + /** * Debug function to load a single campaign. *