1/*
2Open Tracker License
3
4Terms and Conditions
5
6Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
7
8Permission is hereby granted, free of charge, to any person obtaining a copy of
9this software and associated documentation files (the "Software"), to deal in
10the Software without restriction, including without limitation the rights to
11use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12of the Software, and to permit persons to whom the Software is furnished to do
13so, subject to the following conditions:
14
15The above copyright notice and this permission notice applies to all licensees
16and shall be included in all copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
25Except as contained in this notice, the name of Be Incorporated shall not be
26used in advertising or otherwise to promote the sale, use or other dealings in
27this Software without prior written authorization from Be Incorporated.
28
29Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
30of Be Incorporated in the United States and other countries. Other brand product
31names are registered trademarks or trademarks of their respective holders.
32All rights reserved.
33*/
34
35
36#include <Alert.h>
37#include <Box.h>
38#include <Catalog.h>
39#include <Locale.h>
40#include <MenuItem.h>
41#include <MessageFilter.h>
42
43#include "AutoLock.h"
44#include "ContainerWindow.h"
45#include "Commands.h"
46#include "Screen.h"
47#include "SelectionWindow.h"
48
49
50const uint32 kSelectButtonPressed = 'sbpr';
51
52
53#undef B_TRANSLATION_CONTEXT
54#define B_TRANSLATION_CONTEXT "SelectionWindow"
55
56
57SelectionWindow::SelectionWindow(BContainerWindow* window)
58	:
59	BWindow(BRect(0, 0, 270, 0), B_TRANSLATE("Select"),	B_TITLED_WINDOW,
60		B_NOT_ZOOMABLE | B_NOT_MINIMIZABLE | B_NOT_V_RESIZABLE
61		| B_NO_WORKSPACE_ACTIVATION | B_ASYNCHRONOUS_CONTROLS
62		| B_NOT_ANCHORED_ON_ACTIVATE),
63	fParentWindow(window)
64{
65	if (window->Feel() & kPrivateDesktopWindowFeel) {
66		// The window will not show up if we have
67		// B_FLOATING_SUBSET_WINDOW_FEEL and use it with the desktop window
68		// since it's never in front.
69		SetFeel(B_NORMAL_WINDOW_FEEL);
70	}
71
72	AddToSubset(fParentWindow);
73
74	BView* backgroundView = new BView(Bounds(), "bgView", B_FOLLOW_ALL,
75		B_WILL_DRAW);
76	backgroundView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
77	AddChild(backgroundView);
78
79	BMenu* menu = new BPopUpMenu("");
80	menu->AddItem(new BMenuItem(B_TRANSLATE("starts with"),	NULL));
81	menu->AddItem(new BMenuItem(B_TRANSLATE("ends with"), NULL));
82	menu->AddItem(new BMenuItem(B_TRANSLATE("contains"), NULL));
83	menu->AddItem(new BMenuItem(B_TRANSLATE("matches wildcard expression"),
84		NULL));
85	menu->AddItem(new BMenuItem(B_TRANSLATE("matches regular expression"),
86		NULL));
87
88	menu->SetLabelFromMarked(true);
89	menu->ItemAt(3)->SetMarked(true);
90		// Set wildcard matching to default.
91
92	// Set up the menu field
93	fMatchingTypeMenuField = new BMenuField(BRect(7, 6,
94		Bounds().right - 5, 0), NULL, B_TRANSLATE("Name"), menu);
95	backgroundView->AddChild(fMatchingTypeMenuField);
96	fMatchingTypeMenuField->SetDivider(fMatchingTypeMenuField->StringWidth(
97		B_TRANSLATE("Name")) + 8);
98	fMatchingTypeMenuField->ResizeToPreferred();
99
100	// Set up the expression text control
101	fExpressionTextControl = new BTextControl(BRect(7,
102			fMatchingTypeMenuField->Bounds().bottom + 11,
103			Bounds().right - 6, 0),
104		NULL, NULL, NULL, NULL, B_FOLLOW_LEFT_RIGHT);
105	backgroundView->AddChild(fExpressionTextControl);
106	fExpressionTextControl->ResizeToPreferred();
107	fExpressionTextControl->MakeFocus(true);
108
109	// Set up the Invert checkbox
110	fInverseCheckBox = new BCheckBox(
111		BRect(7, fExpressionTextControl->Frame().bottom + 6, 6, 6), NULL,
112		B_TRANSLATE("Invert"), NULL);
113	backgroundView->AddChild(fInverseCheckBox);
114	fInverseCheckBox->ResizeToPreferred();
115
116	// Set up the Ignore Case checkbox
117	fIgnoreCaseCheckBox = new BCheckBox(
118		BRect(fInverseCheckBox->Frame().right + 10,
119			fInverseCheckBox->Frame().top, 6, 6),
120		NULL, B_TRANSLATE("Ignore case"), NULL);
121	fIgnoreCaseCheckBox->SetValue(1);
122	backgroundView->AddChild(fIgnoreCaseCheckBox);
123	fIgnoreCaseCheckBox->ResizeToPreferred();
124
125	// Set up the Select button
126	fSelectButton = new BButton(BRect(0, 0, 5, 5), NULL,
127		B_TRANSLATE("Select"), new BMessage(kSelectButtonPressed),
128		B_FOLLOW_RIGHT);
129
130	backgroundView->AddChild(fSelectButton);
131	fSelectButton->ResizeToPreferred();
132	fSelectButton->MoveTo(Bounds().right - 10 - fSelectButton->Bounds().right,
133		fExpressionTextControl->Frame().bottom + 9);
134	fSelectButton->MakeDefault(true);
135#if !B_BEOS_VERSION_DANO
136	fSelectButton->SetLowColor(backgroundView->ViewColor());
137	fSelectButton->SetViewColor(B_TRANSPARENT_COLOR);
138#endif
139
140	font_height fh;
141	be_plain_font->GetHeight(&fh);
142	// Center the checkboxes vertically to the button
143	float topMiddleButton =
144		(fSelectButton->Bounds().Height() / 2 -
145		(fh.ascent + fh.descent + fh.leading + 4) / 2)
146		+ fSelectButton->Frame().top;
147	fInverseCheckBox->MoveTo(fInverseCheckBox->Frame().left, topMiddleButton);
148	fIgnoreCaseCheckBox->MoveTo(fIgnoreCaseCheckBox->Frame().left,
149		topMiddleButton);
150
151	float bottomMinWidth = 32 + fSelectButton->Bounds().Width()
152		+ fInverseCheckBox->Bounds().Width()
153		+ fIgnoreCaseCheckBox->Bounds().Width();
154	float topMinWidth = be_plain_font->StringWidth(
155		B_TRANSLATE("Name matches wildcard expression:###"));
156	float minWidth = bottomMinWidth > topMinWidth
157		? bottomMinWidth : topMinWidth;
158
159	class EscapeFilter : public BMessageFilter {
160	public:
161		EscapeFilter(BWindow* target)
162			:
163			BMessageFilter(B_KEY_DOWN),
164			fTarget(target)
165		{
166		}
167
168		virtual filter_result Filter(BMessage* message, BHandler** _target)
169		{
170			int8 byte;
171			if (message->what == B_KEY_DOWN
172				&& message->FindInt8("byte", &byte) == B_OK
173				&& byte == B_ESCAPE) {
174				fTarget->Hide();
175				return B_SKIP_MESSAGE;
176			}
177			return B_DISPATCH_MESSAGE;
178		}
179
180	private:
181		BWindow* fTarget;
182	};
183	AddCommonFilter(new(std::nothrow) EscapeFilter(this));
184
185	Run();
186
187	Lock();
188	ResizeTo(minWidth, fSelectButton->Frame().bottom + 6);
189
190	SetSizeLimits(minWidth, 1280, Bounds().bottom, Bounds().bottom);
191
192	MoveCloseToMouse();
193	Unlock();
194}
195
196
197void
198SelectionWindow::MessageReceived(BMessage* message)
199{
200	switch (message->what) {
201		case kSelectButtonPressed:
202		{
203			Hide();
204				// Order of posting and hiding important
205				// since we want to activate the target
206				// window when the message arrives.
207				// (Hide is synhcronous, while PostMessage is not.)
208				// See PoseView::SelectMatchingEntries().
209
210			BMessage* selectionInfo = new BMessage(kSelectMatchingEntries);
211			selectionInfo->AddInt32("ExpressionType", ExpressionType());
212			BString expression;
213			Expression(expression);
214			selectionInfo->AddString("Expression", expression.String());
215			selectionInfo->AddBool("InvertSelection", Invert());
216			selectionInfo->AddBool("IgnoreCase", IgnoreCase());
217			fParentWindow->PostMessage(selectionInfo);
218			break;
219		}
220
221		default:
222			_inherited::MessageReceived(message);
223	}
224}
225
226
227bool
228SelectionWindow::QuitRequested()
229{
230	Hide();
231	return false;
232}
233
234
235void
236SelectionWindow::MoveCloseToMouse()
237{
238	uint32 buttons;
239	BPoint mousePosition;
240
241	ChildAt((int32)0)->GetMouse(&mousePosition, &buttons);
242	ConvertToScreen(&mousePosition);
243
244	// Position the window centered around the mouse...
245	BPoint windowPosition = BPoint(mousePosition.x - Frame().Width() / 2,
246		mousePosition.y	- Frame().Height() / 2);
247
248	// ... unless that's outside of the current screen size:
249	BScreen screen;
250	windowPosition.x
251		= MAX(20, MIN(screen.Frame().right - 20 - Frame().Width(),
252		windowPosition.x));
253	windowPosition.y = MAX(20,
254		MIN(screen.Frame().bottom - 20 - Frame().Height(), windowPosition.y));
255
256	MoveTo(windowPosition);
257	SetWorkspaces(1UL << current_workspace());
258}
259
260
261TrackerStringExpressionType
262SelectionWindow::ExpressionType() const
263{
264	if (!fMatchingTypeMenuField->LockLooper())
265		return kNone;
266
267	BMenuItem* item = fMatchingTypeMenuField->Menu()->FindMarked();
268	if (!item) {
269		fMatchingTypeMenuField->UnlockLooper();
270		return kNone;
271	}
272
273	int32 index = fMatchingTypeMenuField->Menu()->IndexOf(item);
274
275	fMatchingTypeMenuField->UnlockLooper();
276
277	if (index < kStartsWith || index > kRegexpMatch)
278		return kNone;
279
280	TrackerStringExpressionType typeArray[] = {	kStartsWith, kEndsWith,
281		kContains, kGlobMatch, kRegexpMatch};
282
283	return typeArray[index];
284}
285
286
287void
288SelectionWindow::Expression(BString &result) const
289{
290	if (!fExpressionTextControl->LockLooper())
291		return;
292
293	result = fExpressionTextControl->Text();
294
295	fExpressionTextControl->UnlockLooper();
296}
297
298
299bool
300SelectionWindow::IgnoreCase() const
301{
302	if (!fIgnoreCaseCheckBox->LockLooper()) {
303		// default action.
304		return true;
305	}
306
307	bool ignore = fIgnoreCaseCheckBox->Value() != 0;
308
309	fIgnoreCaseCheckBox->UnlockLooper();
310
311	return ignore;
312}
313
314
315bool
316SelectionWindow::Invert() const
317{
318	if (!fInverseCheckBox->LockLooper()) {
319		// default action.
320		return false;
321	}
322
323	bool inverse = fInverseCheckBox->Value() != 0;
324
325	fInverseCheckBox->UnlockLooper();
326
327	return inverse;
328}
329