1/*
2 * Copyright 2013, Stephan A��mus <superstippi@gmx.de>.
3 * Copyright 2019-2020, Andrew Lindesay <apl@lindesay.co.nz>.
4 * All rights reserved. Distributed under the terms of the MIT License.
5 */
6
7#include "FilterView.h"
8
9#include <algorithm>
10#include <stdio.h>
11
12#include <AutoLocker.h>
13#include <Catalog.h>
14#include <CheckBox.h>
15#include <LayoutBuilder.h>
16#include <MenuField.h>
17#include <MenuItem.h>
18#include <Messenger.h>
19#include <PopUpMenu.h>
20#include <TextControl.h>
21
22#include "Model.h"
23#include "support.h"
24
25
26#undef B_TRANSLATION_CONTEXT
27#define B_TRANSLATION_CONTEXT "FilterView"
28
29
30FilterView::FilterView()
31	:
32	BGroupView("filter view", B_VERTICAL)
33{
34	// Construct category popup
35	BPopUpMenu* showMenu = new BPopUpMenu(B_TRANSLATE("Category"));
36	fShowField = new BMenuField("category", B_TRANSLATE("Category:"), showMenu);
37
38	// Construct search terms field
39	fSearchTermsText = new BTextControl("search terms",
40		B_TRANSLATE("Search terms:"), "", NULL);
41	fSearchTermsText->SetModificationMessage(
42		new BMessage(MSG_SEARCH_TERMS_MODIFIED));
43
44	BSize minSearchSize = fSearchTermsText->MinSize();
45	float minSearchWidth
46		= be_plain_font->StringWidth(fSearchTermsText->Label())
47			+ be_plain_font->StringWidth("XXX") * 6;
48	minSearchWidth = std::max(minSearchSize.width, minSearchWidth);
49	minSearchSize.width = minSearchWidth;
50	fSearchTermsText->SetExplicitMinSize(minSearchSize);
51	float maxSearchWidth = minSearchWidth * 2;
52	fSearchTermsText->SetExplicitMaxSize(BSize(maxSearchWidth, B_SIZE_UNSET));
53
54	// Build layout
55	BLayoutBuilder::Group<>(this)
56		.AddGroup(B_HORIZONTAL)
57			.AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING, 1.2f)
58				.Add(fShowField, 0.0f)
59				.SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET))
60			.End()
61			.AddGlue(0.5f)
62			.Add(fSearchTermsText, 1.0f)
63		.End()
64
65		.SetInsets(B_USE_DEFAULT_SPACING)
66	;
67}
68
69
70FilterView::~FilterView()
71{
72}
73
74
75void
76FilterView::AttachedToWindow()
77{
78	fShowField->Menu()->SetTargetForItems(Window());
79	fSearchTermsText->SetTarget(this);
80	fSearchTermsText->MakeFocus();
81}
82
83
84void
85FilterView::MessageReceived(BMessage* message)
86{
87	switch (message->what) {
88		case MSG_SEARCH_TERMS_MODIFIED:
89		{
90			BMessage searchTerms(MSG_SEARCH_TERMS_MODIFIED);
91			searchTerms.AddString("search terms", fSearchTermsText->Text());
92			Window()->PostMessage(&searchTerms);
93			break;
94		}
95
96		default:
97			BGroupView::MessageReceived(message);
98			break;
99	}
100}
101
102
103void
104FilterView::AdoptModel(Model& model)
105{
106	// Adopt categories
107	BMenu* showMenu = fShowField->Menu();
108	showMenu->RemoveItems(0, showMenu->CountItems(), true);
109
110	showMenu->AddItem(new BMenuItem(B_TRANSLATE("All categories"),
111		new BMessage(MSG_CATEGORY_SELECTED)));
112
113	AutoLocker<BLocker> locker(model.Lock());
114	int32 categoryCount = model.CountCategories();
115
116	if (categoryCount > 0) {
117		showMenu->AddItem(new BSeparatorItem());
118		_AddCategoriesToMenu(model, showMenu);
119	}
120
121	showMenu->SetEnabled(categoryCount > 0);
122
123	if (!_SelectCategoryCode(showMenu, model.Category()))
124		showMenu->ItemAt(0)->SetMarked(true);
125}
126
127
128/*! Tries to mark the menu item that corresponds to the supplied
129    category code.  If the supplied code was found and the item marked
130    then the method will return true.
131*/
132
133/*static*/ bool
134FilterView::_SelectCategoryCode(BMenu* menu, const BString& code)
135{
136	for (int32 i = 0; i < menu->CountItems(); i++) {
137		BMenuItem* item = menu->ItemAt(i);
138		if (_MatchesCategoryCode(item, code)) {
139			item->SetMarked(true);
140			return true;
141		}
142	}
143	return false;
144}
145
146
147/*static*/ bool
148FilterView::_MatchesCategoryCode(BMenuItem* item, const BString& code)
149{
150	BMessage* message = item->Message();
151	if (message == NULL)
152		return false;
153	BString itemCode;
154	message->FindString("code", &itemCode);
155	return itemCode == code;
156}
157
158
159/*static*/ void
160FilterView::_AddCategoriesToMenu(Model& model, BMenu* menu)
161{
162	int count = model.CountCategories();
163	for (int i = 0; i < count; i++) {
164		const CategoryRef& category = model.CategoryAtIndex(i);
165		BMessage* message = new BMessage(MSG_CATEGORY_SELECTED);
166		message->AddString("code", category->Code());
167		BMenuItem* item = new BMenuItem(category->Name(), message);
168		menu->AddItem(item);
169	}
170}
171