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