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