diff --git a/src/SheetMe.Designer/ViewModels/Controls/ControlViewModel.cs b/src/SheetMe.Designer/ViewModels/Controls/ControlViewModel.cs
index e79d033..4e80057 100644
--- a/src/SheetMe.Designer/ViewModels/Controls/ControlViewModel.cs
+++ b/src/SheetMe.Designer/ViewModels/Controls/ControlViewModel.cs
@@ -113,6 +113,7 @@ public abstract class ControlViewModel : ViewModelBase
{
Model.Locked = value;
OnPropertyChanged(nameof(IsLockedFlag));
+ OnPropertyChanged(nameof(LockIcon));
}
}
@@ -124,12 +125,39 @@ public abstract class ControlViewModel : ViewModelBase
{
Model.Hidden = value;
OnPropertyChanged(nameof(IsHiddenFlag));
+ OnPropertyChanged(nameof(HiddenIcon));
OnPropertyChanged(nameof(DesignOpacity));
}
}
/// 디자인 표시 불투명도 — 숨김이면 0.25
public double DesignOpacity => Model.Hidden ? 0.25 : 1.0;
+
+ ///
+ /// 레이어 목록 표시명 — Text 가 있으면 그것, 없으면 Id.
+ ///
+ /// Id 는 대부분 Label12 처럼 자동 생성된 이름이라 목록에서 아무것도 알려주지 않는다.
+ /// 다만 Id 는 외부 참조 키이기도 하므로(DataTableField 대상명, EventHandlerMappingTag 의
+ /// ShtCod_컨트롤명 규약) 버리지 않고 툴팁·검색 대상으로 함께 유지한다.
+ ///
+ public string LayerName
+ {
+ get
+ {
+ var text = Text.Replace('\r', ' ').Replace('\n', ' ').Trim();
+ if (text.Length == 0)
+ {
+ return Id;
+ }
+ return text.Length <= 40 ? text : string.Concat(text.AsSpan(0, 39), "…");
+ }
+ }
+
+ /// 레이어 잠금 토글 아이콘명
+ public string LockIcon => Model.Locked ? "lock" : "unlock";
+
+ /// 레이어 숨김 토글 아이콘명
+ public string HiddenIcon => Model.Hidden ? "eye-off" : "eye";
#endregion
#region Constructors
diff --git a/src/SheetMe.Designer/ViewModels/DesignerViewModel.cs b/src/SheetMe.Designer/ViewModels/DesignerViewModel.cs
index 8da199c..ce9f0de 100644
--- a/src/SheetMe.Designer/ViewModels/DesignerViewModel.cs
+++ b/src/SheetMe.Designer/ViewModels/DesignerViewModel.cs
@@ -68,6 +68,9 @@ public sealed class DesignerViewModel : ViewModelBase
private double zoom = 1.0;
private FormDocument document;
private PageViewModel? selectedPage;
+ private string layerFilter = string.Empty;
+ private double viewportWidth;
+ private double viewportHeight;
private bool isTabOrderMode;
private readonly List tabOrderSequence = new();
private readonly Dictionary? spreadDesigns;
@@ -138,13 +141,53 @@ public sealed class DesignerViewModel : ViewModelBase
get => selectedPage;
set
{
- if (SetProperty(ref selectedPage, value) && value is not null)
+ if (SetProperty(ref selectedPage, value))
{
- Selection.ActivePage = value;
+ if (value is not null)
+ {
+ Selection.ActivePage = value;
+ }
+ OnPropertyChanged(nameof(VisibleLayers));
}
}
}
+ ///
+ /// 레이어 검색어 — 이름(LayerName)·Id 부분일치. 서식당 컨트롤이 수백 개인 경우가 흔해
+ /// (운영 최대 1,067개) 스크롤만으로는 대상을 찾을 수 없다.
+ ///
+ public string LayerFilter
+ {
+ get => layerFilter;
+ set
+ {
+ if (SetProperty(ref layerFilter, value ?? string.Empty))
+ {
+ OnPropertyChanged(nameof(VisibleLayers));
+ }
+ }
+ }
+
+ ///
+ /// 레이어 목록에 실제로 표시할 항목 — 활성 페이지의 컨트롤에 검색어를 적용한 결과.
+ /// 검색어가 비면 원본 컬렉션을 그대로 돌려준다(항목 추가·삭제가 즉시 반영되도록).
+ ///
+ public System.Collections.IEnumerable? VisibleLayers
+ {
+ get
+ {
+ var source = SelectedPage?.Controls;
+ if (source is null || layerFilter.Length == 0)
+ {
+ return source;
+ }
+ return source
+ .Where(c => c.LayerName.Contains(layerFilter, StringComparison.OrdinalIgnoreCase)
+ || c.Id.Contains(layerFilter, StringComparison.OrdinalIgnoreCase))
+ .ToList();
+ }
+ }
+
/// 레이어 목록 선택 항목 — 캔버스 선택과 동기
public ControlViewModel? SelectedLayerItem
{
@@ -401,6 +444,38 @@ public sealed class DesignerViewModel : ViewModelBase
/// 줌 100%
public void ZoomReset() => Zoom = 1.0;
+
+ ///
+ /// 캔버스 뷰포트 크기 통지 — DesignerCanvasView 가 SizeChanged 에서 호출한다.
+ /// ZoomFit 은 뷰포트를 알아야 하는데 VM 은 시각 트리를 모르므로 값만 받아 둔다.
+ ///
+ public void ReportViewport(double width, double height)
+ {
+ viewportWidth = width;
+ viewportHeight = height;
+ }
+
+ ///
+ /// 화면 맞춤 — 활성 페이지 한 장이 뷰포트에 들어오는 배율.
+ /// 문서 전체(WorldHeight)가 아니라 페이지 한 장 기준이다. 7페이지 서식을 통째로 맞추면
+ /// 14% 가 되어 아무것도 안 보인다(운영 최대 7페이지).
+ ///
+ public void ZoomFit()
+ {
+ var page = SelectedPage ?? (Pages.Count > 0 ? Pages[0] : null);
+ if (page is null || viewportWidth <= 0 || viewportHeight <= 0)
+ {
+ return;
+ }
+ // Border Padding 24 양쪽 + 스크롤바 여유
+ var availableWidth = viewportWidth - 64;
+ var availableHeight = viewportHeight - 64;
+ if (availableWidth <= 0 || availableHeight <= 0)
+ {
+ return;
+ }
+ Zoom = Math.Round(Math.Min(availableWidth / page.WidthDip, availableHeight / page.HeightDip), 2);
+ }
#endregion
#region Methods - 좌표/히트테스트
diff --git a/src/SheetMe.Designer/ViewModels/Inspector/InspectorViewModel.cs b/src/SheetMe.Designer/ViewModels/Inspector/InspectorViewModel.cs
index fb5aabb..1b16e73 100644
--- a/src/SheetMe.Designer/ViewModels/Inspector/InspectorViewModel.cs
+++ b/src/SheetMe.Designer/ViewModels/Inspector/InspectorViewModel.cs
@@ -20,6 +20,7 @@ public sealed class InspectorViewModel : ViewModelBase
"System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";
private readonly DesignerViewModel designer;
private readonly List<(PropertyRowViewModel Row, RowBinding Binding)> boundsRows = new();
+ private SectionRowViewModel? currentSection;
private bool showAdvanced;
/// 공통/글꼴 섹션이 이미 다루는 키 — 고급 목록에서 제외
@@ -33,6 +34,9 @@ public sealed class InspectorViewModel : ViewModelBase
/// 행 목록(섹션 헤더 포함)
public ObservableCollection Rows { get; } = new();
+ /// 선택된 컨트롤이 있는지 — 인스펙터 빈 상태 판정
+ public bool HasSelection => designer.Selection.Items.Count > 0;
+
/// 선택 요약 텍스트
public string Summary
=> designer.Selection.Items.Count switch
@@ -73,7 +77,9 @@ public sealed class InspectorViewModel : ViewModelBase
{
Rows.Clear();
boundsRows.Clear();
+ currentSection = null;
OnPropertyChanged(nameof(Summary));
+ OnPropertyChanged(nameof(HasSelection));
var items = designer.Selection.Items;
if (items.Count == 0)
@@ -82,7 +88,7 @@ public sealed class InspectorViewModel : ViewModelBase
}
// 공통: 이름/위치/크기
- Rows.Add(new SectionRowViewModel("공통"));
+ AddSection("공통");
if (items.Count == 1)
{
AddRow(new TextRowViewModel("이름"), new RowBinding
@@ -98,7 +104,7 @@ public sealed class InspectorViewModel : ViewModelBase
AddBoundsRow("높이", vm => vm.Height, (vm, v) => vm.Height = Math.Max(1, v));
// 폰트(공통) — Font Property(상속 시 빈 값)
- Rows.Add(new SectionRowViewModel("글꼴"));
+ AddSection("글꼴");
AddFontRow("글꼴", f => f.Family, (f, v) => f.Family = v.Length == 0 ? f.Family : v);
AddFontRow("크기(pt)", f => f.SizePt.ToString("0.##", CultureInfo.InvariantCulture),
(f, v) => f.SizePt = double.TryParse(v, NumberStyles.Number, CultureInfo.InvariantCulture, out var size) && size > 0 ? size : f.SizePt);
@@ -110,7 +116,7 @@ public sealed class InspectorViewModel : ViewModelBase
var curatedKeys = new HashSet(HandledKeys, StringComparer.Ordinal);
if (items.All(i => i.Type == type) && ControlRegistry.Find(type) is { } descriptor && descriptor.Properties.Count > 0)
{
- Rows.Add(new SectionRowViewModel(descriptor.DisplayName));
+ AddSection(descriptor.DisplayName);
foreach (var def in descriptor.Properties)
{
AddDefRow(def);
@@ -134,7 +140,8 @@ public sealed class InspectorViewModel : ViewModelBase
if (!showAdvanced)
{
- Rows.Add(new ToggleAdvancedRowViewModel($"전체 속성 표시 ▾ ({advancedKeys.Count}개)", () =>
+ currentSection = null;
+ AddPlain(new ToggleAdvancedRowViewModel($"전체 속성 표시 ▾ ({advancedKeys.Count}개)", () =>
{
showAdvanced = true;
Rebuild();
@@ -142,8 +149,8 @@ public sealed class InspectorViewModel : ViewModelBase
return;
}
- Rows.Add(new SectionRowViewModel("전체 속성 (고급 — 레거시 원문)"));
- Rows.Add(new ToggleAdvancedRowViewModel("전체 속성 숨기기 ▴", () =>
+ AddSection("전체 속성 (고급 — 레거시 원문)");
+ AddPlain(new ToggleAdvancedRowViewModel("전체 속성 숨기기 ▴", () =>
{
showAdvanced = false;
Rebuild();
@@ -182,21 +189,21 @@ public sealed class InspectorViewModel : ViewModelBase
break;
case LegacyPropValue.NestedValue nested:
- Rows.Add(new ReadOnlyRowViewModel(key, $"(중첩 속성 {nested.Children.Count}개 — 보존됨)"));
+ AddPlain(new ReadOnlyRowViewModel(key, $"(중첩 속성 {nested.Children.Count}개 — 보존됨)"));
break;
case LegacyPropValue.BinaryValue:
- Rows.Add(new ReadOnlyRowViewModel(key, "(바이너리 — 원문 보존됨)"));
+ AddPlain(new ReadOnlyRowViewModel(key, "(바이너리 — 원문 보존됨)"));
break;
case LegacyPropValue.ReferenceValue reference:
- Rows.Add(new ReadOnlyRowViewModel(key, $"(참조: {reference.Name})"));
+ AddPlain(new ReadOnlyRowViewModel(key, $"(참조: {reference.Name})"));
break;
}
}
// 속성 추가 — 레거시 컨트롤의 임의 속성 지정(오타 주의: 로더가 모르는 키는 경고 처리)
- Rows.Add(new AddPropertyRowViewModel(key =>
+ AddPlain(new AddPropertyRowViewModel(key =>
{
if (target.Model.Props.Contains(key))
{
@@ -330,6 +337,20 @@ public sealed class InspectorViewModel : ViewModelBase
};
}
+ /// 섹션 헤더 시작 — 이후 추가되는 행은 이 섹션에 편입된다
+ private void AddSection(string label)
+ {
+ currentSection = new SectionRowViewModel(label);
+ Rows.Add(currentSection);
+ }
+
+ /// 바인딩 없는 행 추가(읽기 전용·버튼 행) — 섹션 편입만 처리
+ private void AddPlain(PropertyRowViewModel row)
+ {
+ currentSection?.Adopt(row);
+ Rows.Add(row);
+ }
+
private void AddRow(PropertyRowViewModel row, RowBinding binding, Action? afterCommit = null)
{
var items = designer.Selection.Items;
@@ -363,6 +384,7 @@ public sealed class InspectorViewModel : ViewModelBase
}
afterCommit?.Invoke();
};
+ currentSection?.Adopt(row);
Rows.Add(row);
}
diff --git a/src/SheetMe.Designer/ViewModels/Inspector/PropertyRows.cs b/src/SheetMe.Designer/ViewModels/Inspector/PropertyRows.cs
index 6bb6ddc..e0a5d42 100644
--- a/src/SheetMe.Designer/ViewModels/Inspector/PropertyRows.cs
+++ b/src/SheetMe.Designer/ViewModels/Inspector/PropertyRows.cs
@@ -21,6 +21,15 @@ public abstract class PropertyRowViewModel : ViewModelBase
/// 선택 대상들의 값이 서로 다른지 — "여러 값" 표시
public bool IsMixed { get; private set; }
+ /// 소속 섹션 — null 이면 섹션 밖(항상 표시)
+ public SectionRowViewModel? Section { get; set; }
+
+ /// 접힌 섹션에 속하면 숨긴다 — ItemContainerStyle 이 바인딩
+ public bool IsRowVisible => Section is null || Section.IsExpanded;
+
+ /// 표시 여부 통지(섹션 접기/펴기)
+ public void NotifyVisibilityChanged() => OnPropertyChanged(nameof(IsRowVisible));
+
/// 정규화 문자열 값 — 파생 편집기가 형 변환
public string ValueText
{
@@ -59,6 +68,9 @@ public abstract class PropertyRowViewModel : ViewModelBase
OnPropertyChanged(nameof(ValueText));
OnPropertyChanged(nameof(IsMixed));
building = false;
+ // 파생 표시(토글 3상태·미리보기·요약)도 초기값 기준으로 맞춘다.
+ // 드래그 중 RefreshBoundsRows 가 Initialize 만 호출하는 경로가 있어 여기서 하지 않으면 파생이 뒤처진다.
+ OnValueApplied();
}
/// 값 적용 후 파생 갱신 지점
@@ -66,10 +78,59 @@ public abstract class PropertyRowViewModel : ViewModelBase
#endregion
}
-/// 구분 헤더 행
+///
+/// 구분 헤더 행 — 클릭으로 접기/펴기.
+///
+/// Rows 는 평면 컬렉션을 유지하고(다중선택 재구성이 잦아 트리 재구축 비용을 피한다) 섹션이 자기
+/// 소속 행을 들고 있다가 IsRowVisible 만 갱신한다. 컨테이너 Visibility 만 바뀌므로 편집 중이던
+/// 값·포커스 상태가 보존된다.
+///
public sealed class SectionRowViewModel : PropertyRowViewModel
{
- public SectionRowViewModel(string label) : base(label) { }
+ private bool isExpanded = true;
+
+ /// 이 섹션에 속한 행들
+ public List Children { get; } = new();
+
+ /// 펼침 상태
+ public bool IsExpanded
+ {
+ get => isExpanded;
+ set
+ {
+ if (!SetProperty(ref isExpanded, value))
+ {
+ return;
+ }
+ OnPropertyChanged(nameof(ChevronIcon));
+ foreach (var child in Children)
+ {
+ child.NotifyVisibilityChanged();
+ }
+ }
+ }
+
+ /// 펼침 표시 아이콘
+ public string ChevronIcon => isExpanded ? "chevron-down" : "chevron-right";
+
+ /// 접힌 상태에서 몇 개가 숨어 있는지
+ public string CountText => Children.Count == 0 ? string.Empty : Children.Count.ToString();
+
+ /// 헤더 클릭 — 접기/펴기
+ public M.Framework.WPF.ICustomCommand? ToggleCommand { get; }
+
+ public SectionRowViewModel(string label) : base(label)
+ {
+ ToggleCommand = new M.Framework.WPF.Command((sender, e) => IsExpanded = !IsExpanded);
+ }
+
+ /// 행 편입 — InspectorViewModel 이 Rows.Add 와 함께 호출
+ public void Adopt(PropertyRowViewModel row)
+ {
+ row.Section = this;
+ Children.Add(row);
+ OnPropertyChanged(nameof(CountText));
+ }
}
/// 한 줄 문자열 행
@@ -93,11 +154,21 @@ public sealed class NumberRowViewModel : PropertyRowViewModel
/// 토글(참/거짓) 행
public sealed class ToggleRowViewModel : PropertyRowViewModel
{
- /// 체크 상태 — "True"/"False" 문자열과 동기
- public bool IsOn
+ ///
+ /// 체크 상태 — "True"/"False" 문자열과 동기. 다중선택에서 값이 갈리면 null(불확정)이다.
+ /// false 로 내리면 "전부 꺼짐"으로 보이고, 사용자가 한 번 눌러 켰다 끄면 전부 False 가 되어
+ /// 조용히 값이 뭉개진다.
+ ///
+ public bool? IsOn
{
- get => ValueText == "True";
- set => ValueText = value ? "True" : "False";
+ get => IsMixed ? null : ValueText == "True";
+ set
+ {
+ if (value is bool on)
+ {
+ ValueText = on ? "True" : "False";
+ }
+ }
}
public ToggleRowViewModel(string label) : base(label) { }
diff --git a/src/SheetMe.Designer/ViewModels/MainViewModel.cs b/src/SheetMe.Designer/ViewModels/MainViewModel.cs
index 04a27a4..ff387be 100644
--- a/src/SheetMe.Designer/ViewModels/MainViewModel.cs
+++ b/src/SheetMe.Designer/ViewModels/MainViewModel.cs
@@ -78,9 +78,39 @@ internal sealed class MainViewModel : ViewModelBase
public bool IsSheetListLoading
{
get => isSheetListLoading;
- set => SetProperty(ref isSheetListLoading, value);
+ set
+ {
+ if (SetProperty(ref isSheetListLoading, value))
+ {
+ OnPropertyChanged(nameof(SheetListStatusText));
+ }
+ }
}
+ ///
+ /// 서식 목록 하단 상태 한 줄 — 로딩·건수·미접속을 목록 바로 아래에서 알린다.
+ /// 종전에는 목록 위에 "불러오는 중..." 오버레이를 덮어 목록이 가려졌고, 건수는 창 하단
+ /// 전역 상태줄에만 떠서 목록과 시선 거리가 멀었다.
+ ///
+ public string SheetListStatusText
+ {
+ get
+ {
+ if (!CanUseDb)
+ {
+ return "DB 미접속 — 파일 모드";
+ }
+ if (IsSheetListLoading)
+ {
+ return "불러오는 중…";
+ }
+ return SheetList.Count == 0 ? "결과 없음" : $"{SheetList.Count}건";
+ }
+ }
+
+ /// 열린 문서 존재 여부 — 빈 상태 오버레이 표시 판정
+ public bool HasOpenDocuments => OpenDesigners.Count > 0;
+
/// DB 사용 가능 여부(서식 목록 패널 안내용)
public bool CanUseDb => dataBusiness.CanUseDb;
@@ -119,6 +149,7 @@ internal sealed class MainViewModel : ViewModelBase
ZoomInCommand = new Command((sender, e) => CurrentDesigner?.ZoomIn());
ZoomOutCommand = new Command((sender, e) => CurrentDesigner?.ZoomOut());
ZoomResetCommand = new Command((sender, e) => CurrentDesigner?.ZoomReset());
+ ZoomFitCommand = new Command((sender, e) => CurrentDesigner?.ZoomFit());
// Application.Shutdown() 은 Window.Closing 의 e.Cancel 을 무시하므로 VM 에서 먼저 확인한다
ExitCommand = new Command((sender, e) =>
{
@@ -132,6 +163,9 @@ internal sealed class MainViewModel : ViewModelBase
SearchSheetsCommand = new Command(async (sender, e) => await LoadSheetListAsync());
OpenSheetCommand = new Command((object param) => OnOpenSheetFromList(param as SheetSummary));
CloseDocumentCommand = new Command((object param) => OnCloseDocument(param as DesignerViewModel));
+
+ SheetList.CollectionChanged += (s, e) => OnPropertyChanged(nameof(SheetListStatusText));
+ OpenDesigners.CollectionChanged += (s, e) => OnPropertyChanged(nameof(HasOpenDocuments));
}
#endregion
@@ -187,6 +221,9 @@ internal sealed class MainViewModel : ViewModelBase
/// 줌 100%
public ICustomCommand? ZoomResetCommand { get; set; }
+ /// 화면 맞춤 — 활성 페이지 한 장이 뷰포트에 들어오는 배율
+ public ICustomCommand? ZoomFitCommand { get; set; }
+
/// 종료
public ICustomCommand? ExitCommand { get; set; }
diff --git a/src/SheetMe.Designer/Views/DesignerCanvasView.xaml b/src/SheetMe.Designer/Views/DesignerCanvasView.xaml
index a5205ea..ab23ae2 100644
--- a/src/SheetMe.Designer/Views/DesignerCanvasView.xaml
+++ b/src/SheetMe.Designer/Views/DesignerCanvasView.xaml
@@ -23,6 +23,7 @@
VerticalScrollBarVisibility="Auto"
Background="{DynamicResource B.CanvasBg}"
Focusable="False"
+ SizeChanged="OnScrollSizeChanged"
PreviewMouseWheel="OnPreviewMouseWheel"
PreviewMouseLeftButtonDown="OnPanMouseDown"
PreviewMouseMove="OnPanMouseMove"
diff --git a/src/SheetMe.Designer/Views/DesignerCanvasView.xaml.cs b/src/SheetMe.Designer/Views/DesignerCanvasView.xaml.cs
index 52d3c4b..8969445 100644
--- a/src/SheetMe.Designer/Views/DesignerCanvasView.xaml.cs
+++ b/src/SheetMe.Designer/Views/DesignerCanvasView.xaml.cs
@@ -46,9 +46,14 @@ public partial class DesignerCanvasView : UserControl
if (subscribedDesigner is not null)
{
subscribedDesigner.InlineEditRequested += ShowInlineEditor;
+ subscribedDesigner.ReportViewport(Scroll.ViewportWidth, Scroll.ViewportHeight);
}
}
+ /// 뷰포트 크기를 VM 에 전달 — 화면 맞춤(ZoomFit)이 시각 트리를 몰라도 되게 한다
+ private void OnScrollSizeChanged(object sender, SizeChangedEventArgs e)
+ => subscribedDesigner?.ReportViewport(Scroll.ViewportWidth, Scroll.ViewportHeight);
+
/// 대상 컨트롤 위에 편집기 표시 — 폰트/정렬 일치, 줌은 World LayoutTransform 이 처리
private void ShowInlineEditor(ControlViewModel target)
{
diff --git a/src/SheetMe.Designer/Views/InspectorView.xaml b/src/SheetMe.Designer/Views/InspectorView.xaml
index 630cd47..f9c4c92 100644
--- a/src/SheetMe.Designer/Views/InspectorView.xaml
+++ b/src/SheetMe.Designer/Views/InspectorView.xaml
@@ -4,60 +4,141 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ins="clr-namespace:SheetMe.Designer.ViewModels.Inspector"
+ xmlns:ctl="clr-namespace:SheetMe.Designer.Controls"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance ins:InspectorViewModel}">
+
+
+
+
+
+
+
+
+
-
+
-
-
+
+
+
+
+
-
-
+
+
+
+
+
-
-
+
+
+
+
+
-
-
+
+
+
+
-
-
+
+
+
+
+
@@ -66,8 +147,11 @@
-
+
+
+
+
@@ -113,9 +197,12 @@
-
+
+
+
+
-
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SheetMe.Designer/Views/MainView.xaml b/src/SheetMe.Designer/Views/MainView.xaml
index 149c1ff..cada4b6 100644
--- a/src/SheetMe.Designer/Views/MainView.xaml
+++ b/src/SheetMe.Designer/Views/MainView.xaml
@@ -118,14 +118,23 @@
+
-
-
+
-
-
-
-
+
+
+
+
+
+
+
+
+
-
+
@@ -233,19 +279,21 @@
-
+
-
+
+
-
+
@@ -257,15 +305,37 @@
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
@@ -347,19 +414,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
@@ -419,11 +531,24 @@
-
+
+
+
+
+
+
-
-
+
-
@@ -552,9 +672,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+