Bivio::Delegate::SimpleWidgetFactory
# Copyright (c) 1999-2009 bivio Software, Inc. All rights reserved. # $Id$ package Bivio::Delegate::SimpleWidgetFactory; use strict; use Bivio::Base 'UI.Widget'; use Bivio::IO::Trace; b_use('IO.ClassLoaderAUTOLOAD'); our($_TRACE); my($_VS) = b_use('UIHTML.ViewShortcuts'); my($_QT) = b_use('Biz.QueryType'); my($_TI) = b_use('Agent.TaskId'); my($_FM) = b_use('Biz.FormModel'); my($_M) = b_use('Biz.Model'); my($_N) = b_use('Type.Name'); my($_TV) = b_use('Bivio.TypeValue'); my($_TA) = b_use('Type.TextArea'); sub create { my($proto, $field, $attrs) = @_; $attrs ||= {}; my($model, $field_name, $field_type) = _get_model_and_field_type($field, $attrs); my($widget) = $attrs->{wf_widget} || $attrs->{wf_class} && $_VS->vs_new($attrs->{wf_class}, { field => $field_name, %$attrs, }); return $widget || $proto->internal_create_edit( $model, $field_name, $field_type, $attrs ) if !$attrs->{wf_want_display} && $_FM->is_blesser_of($model); #TODO: This is broken in the case of $attrs->{value} existing. Hack for now $widget = $proto->internal_create_display( $model, $field_name, $field_type, $attrs, ) unless $widget; my(%attrs_copy) = %$attrs; delete($attrs_copy{value}); # Wrap the resultant widget in a link? my($wll) = $widget->unsafe_get('wf_list_link'); $wll = {task => $wll, query => 'THIS_DETAIL'} if defined($wll) && !ref($wll); $widget = $_VS->vs_new('Link', { href => ['->format_uri', $_QT->from_any($wll->{query} || 'NO_QUERY'), $wll->{task} ? ($_TI->from_any($wll->{task})) : $wll->{uri} ? $wll->{uri} : (), ], value => $widget, control_off_value => $widget, $wll->{task} ? (control => $wll->{task}) : (), %attrs_copy, %$wll, }) if $wll; return $widget; } sub internal_create_display { # (proto, Biz.Model, string, Bivio.Type, hash_ref) : UI.Widget # Create a display-only widget for the specified field. my($proto, $model, $field, $type, $attrs) = @_; my($value) = $attrs->{source_is_list_model} ? [['->get_list_model'], $field] : $field; $model = $model->get_list_model if $attrs->{source_is_list_model}; if ($field eq 'RealmOwner.name' && $model->can('format_name')) { return $_VS->vs_new('String', { field => $value, #TODO: This is broken in the case of $attrs->{value} existing value => $attrs->{source_is_list_model} ? [['->get_list_model'], '->format_name'] : ['->format_name'], %$attrs, }); } if (UNIVERSAL::isa($type, 'Bivio::Type::Percent')) { b_use('HTMLWidget.PercentCell'); return $_VS->vs_new('PercentCell', { field => $value, %$attrs, }); } if (UNIVERSAL::isa($type, 'Bivio::Type::Dollar')) { return $_VS->vs_new('DollarCell', { field => $value, string_font => 'table_cell', column_align => 'right', column_data_class => 'amount_cell', %$attrs, }); } if (UNIVERSAL::isa($type, 'Bivio::Type::Year')) { return $_VS->vs_new('String', { field => $field, value => [$value], string_font => 'table_cell', column_align => 'right', column_data_class => 'amount_cell', %$attrs, }), } if (UNIVERSAL::isa($type, 'Bivio::Type::Integer')) { return $_VS->vs_new('Integer', { field => $value, %$attrs, }); } if (UNIVERSAL::isa($type, 'Bivio::Type::Amount')) { return $_VS->vs_new('AmountCell', { field => $value, decimals => $proto->internal_default_decimals($field), want_parens => $proto->internal_default_want_parens($field), %$attrs, }); } if (UNIVERSAL::isa($type, 'Bivio::Type::DateTimeWithTimeZone')) { return $_VS->vs_new('String', { field => $field, value => [$value, '->as_literal'], %$attrs, }); } if (UNIVERSAL::isa($type, 'Bivio::Type::Date')) { return $_VS->vs_new('String', { field => $field, value => [[$value], 'HTMLFormat.DateTime', $attrs->{mode} || b_use('UI.DateTimeMode')->get_date_default, defined($attrs->{no_timezone}) ? $attrs->{no_timezone} : 1], column_align => 'E', %$attrs, }); } if (UNIVERSAL::isa($type, 'Bivio::Type::Time')) { return $_VS->vs_new('String', { field => $field, value => ['Bivio::Type::Time', '->to_string', [$value]], column_align => 'E', %$attrs, }); } if (UNIVERSAL::isa($type, 'Bivio::Type::DateTime')) { return $_VS->vs_new('DateTime', { field => $field, column_align => 'E', #TODO: This is broken in the case of $attrs->{value} existing value => [$value], %$attrs, }); } if (UNIVERSAL::isa($type, 'Bivio::Type::Enum')) { return $_VS->vs_new('Enum', { field => $value, %$attrs, }); } if (UNIVERSAL::isa($type, 'Bivio::Type::Email')) { return $_VS->vs_new('MailTo', { field => $field, email => [$value], %$attrs, }); } if (UNIVERSAL::isa($type, 'Bivio::Type::PrimaryId')) { return $_VS->vs_new('String', { field => $field, value => [$value], string_font => 'table_cell', column_align => 'right', %$attrs, }), } # Default number formatting if (UNIVERSAL::isa($type, 'Bivio::Type::Number')) { return $_VS->vs_new('AmountCell', { field => $field, decimals => $type->get_decimals, %$attrs, }), } if (UNIVERSAL::isa($type, 'Bivio::Type::HTTPURI')) { return $_VS->vs_new(Link => { href => [$value], control => [$value], value => $_VS->vs_new(String => {value => [$value]}), %$attrs, }); } # default type is string return $_VS->vs_new('String', { field => $field, #TODO: This is broken in the case of $attrs->{value} existing value => [$value], string_font => 'table_cell', %$attrs, }); } sub internal_create_edit { # (proto, Biz.Model, string, Bivio.Type, hash_ref) : UI.Widget # Create an editable widget for the specified field. my($proto, $model, $field, $type, $attrs) = @_; if (UNIVERSAL::isa($type, 'Bivio::Type::TimeZoneSelector')) { return $_VS->vs_new(ComboBox => { field => $field, list_class => 'TimeZoneList', list_display_field => ['display_name'], %$attrs, }); } if (UNIVERSAL::isa($type, 'Bivio::Type::Enum')) { # Don't have larger than a 2x3 Grid return $_VS->vs_new('Select', { field => $field, choices => $type, %$attrs, }) if $attrs->{wf_want_select} || !defined($attrs->{wf_want_select}) && $type->get_count() > 6; $attrs->{label_on_field} = 0; return $_VS->vs_new('RadioGrid', { field => $field, choices => $type, %$attrs, }); } if (UNIVERSAL::isa($type, 'Bivio::Type::EnumSet')) { return $_VS->vs_new('CheckboxGrid', { field => SQL_Support()->extract_column_name($field), choices => $_TV->new( $type, $type->from_array([ $type->get_enum_type->get_non_zero_list, ]), ), %$attrs, }); } if ($type->can('provide_select_choices')) { return $_VS->vs_new('Select', { field => $field, choices => $type, unknown_label => $_VS->vs_unknown_label($model, $field), %$attrs, }); } if (UNIVERSAL::isa($type, 'Bivio::Type::Time')) { return $_VS->vs_new('Text', { field => $field, size => 14, %$attrs, }); } if (UNIVERSAL::isa($type, 'Bivio::Type::DateTime')) { return $_VS->vs_new('DateField', { field => $field, event_handler => $_VS->vs_new('DateYearHandler'), %$attrs, }); } if (UNIVERSAL::isa($type, 'Bivio::Type::FileField')) { b_use('HTMLWidget.File'); return $_VS->vs_new('File', { field => $field, size => 45, %$attrs, }); } if (UNIVERSAL::isa($type, 'Bivio::Type::Amount')) { return $_VS->vs_new('Text', { field => $field, size => 10, %$attrs, }); } if (UNIVERSAL::isa($type, 'Bivio::Type::StringArray')) { return $_VS->vs_new('TextArea', { field => $field, rows => 2, cols => $_TA->LINE_WIDTH, %$attrs, }); } if (UNIVERSAL::isa($type, 'Bivio::Type::TupleSlot')) { return $_VS->vs_new('Text', { field => $field, size => 30, %$attrs, }); } if (UNIVERSAL::isa($type, 'Bivio::Type::Text') && !$model->get_field_info($field, 'in_list') ) { return $_VS->vs_new('TextArea', { field => $field, rows => 5, cols => $_TA->LINE_WIDTH, %$attrs, }); } if (UNIVERSAL::isa($type, 'Bivio::Type::PrimaryId')) { return $_VS->vs_new('Select', { field => $field, %$attrs, }); } # PUT SUPERCLASSES last since they may be overridden #TODO: need to be intelligent here, create widget based on the field type if (UNIVERSAL::isa($type, 'Bivio::Type::String') || UNIVERSAL::isa($type, 'Bivio::Type::RealmName')) { return $_VS->vs_new('Text', { field => $field, size => _default_size($type), %$attrs, }); } if (UNIVERSAL::isa($type, 'Bivio::Type::FormButton')) { $attrs->{label_on_field} = 0; return $_VS->vs_new('FormButton', { field => $field, %$attrs, }); } if (UNIVERSAL::isa($type, 'Bivio::Type::Boolean')) { $attrs->{label_on_field} = 0; return $_VS->vs_new('Checkbox', { field => $field, %$attrs, }); } if (UNIVERSAL::isa($type, 'Bivio::Type::Year')) { return $_VS->vs_new('Text', { field => $field, size => $type->get_width, %$attrs, }); } if (UNIVERSAL::isa($type, 'Bivio::Type::PageSize')) { # max is 2000 but we only want to get 500 on pages # like this. b_die($type, ': range changed') if $type->get_min != 5 || $type->get_max != 2000; return $_VS->vs_new('Select', { field => $field, choices => $_TV->new( $type, [qw(5 10 15 20 30 40 50 75 100 200 300 400 500)]), %$attrs, }); } if (UNIVERSAL::isa($type, 'Bivio::Type::Number')) { return $_VS->vs_new('Text', { field => $field, size => $type->get_width, %$attrs, }); } b_die($type, ': unsupported type'); # DOES NOT RETURN } sub internal_default_decimals { return 2; } sub internal_default_want_parens { return 0; } sub _default_size { my($type) = @_; my($w) = $type->get_width; return $w <= 15 ? $w : $w <= $_N->get_width ? 15 : 30; } sub _get_model_and_field_type { my($field, $attrs) = @_; my($model_name, $field_name) = $field =~ /^([^\.]+)\.(.+)$/; b_die($field, ": couldn't parse") unless defined($model_name) && defined($field_name); my($model) = $_M->get_instance($model_name); return ( $model, $field_name, $attrs->{wf_type} || $model->get_field_type($field_name), ); } 1;