xref: /haiku/src/kits/tracker/SelectionWindow.cpp (revision 39d6c466d8217dcb3004074f29c27c60f0fa1ac1)
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 //	#pragma mark - SelectionWindow
54 
55 
56 #undef B_TRANSLATION_CONTEXT
57 #define B_TRANSLATION_CONTEXT "SelectionWindow"
58 
59 
60 SelectionWindow::SelectionWindow(BContainerWindow* window)
61 	:
62 	BWindow(BRect(0, 0, 270, 0), B_TRANSLATE("Select"),	B_TITLED_WINDOW,
63 		B_NOT_ZOOMABLE | B_NOT_MINIMIZABLE | B_NOT_V_RESIZABLE
64 			| B_NO_WORKSPACE_ACTIVATION | B_ASYNCHRONOUS_CONTROLS
65 			| B_NOT_ANCHORED_ON_ACTIVATE),
66 	fParentWindow(window)
67 {
68 	if (window->Feel() & kPrivateDesktopWindowFeel) {
69 		// The window will not show up if we have
70 		// B_FLOATING_SUBSET_WINDOW_FEEL and use it with the desktop window
71 		// since it's never in front.
72 		SetFeel(B_NORMAL_WINDOW_FEEL);
73 	}
74 
75 	AddToSubset(fParentWindow);
76 
77 	BView* backgroundView = new BView(Bounds(), "bgView", B_FOLLOW_ALL,
78 		B_WILL_DRAW);
79 	backgroundView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
80 	AddChild(backgroundView);
81 
82 	BMenu* menu = new BPopUpMenu("");
83 	menu->AddItem(new BMenuItem(B_TRANSLATE("starts with"),	NULL));
84 	menu->AddItem(new BMenuItem(B_TRANSLATE("ends with"), NULL));
85 	menu->AddItem(new BMenuItem(B_TRANSLATE("contains"), NULL));
86 	menu->AddItem(new BMenuItem(B_TRANSLATE("matches wildcard expression"),
87 		NULL));
88 	menu->AddItem(new BMenuItem(B_TRANSLATE("matches regular expression"),
89 		NULL));
90 
91 	menu->SetLabelFromMarked(true);
92 	menu->ItemAt(3)->SetMarked(true);
93 		// Set wildcard matching to default.
94 
95 	// Set up the menu field
96 	fMatchingTypeMenuField = new BMenuField(BRect(7, 6,
97 		Bounds().right - 5, 0), NULL, B_TRANSLATE("Name"), menu);
98 	backgroundView->AddChild(fMatchingTypeMenuField);
99 	fMatchingTypeMenuField->SetDivider(fMatchingTypeMenuField->StringWidth(
100 		B_TRANSLATE("Name")) + 8);
101 	fMatchingTypeMenuField->ResizeToPreferred();
102 
103 	// Set up the expression text control
104 	fExpressionTextControl = new BTextControl(BRect(7,
105 			fMatchingTypeMenuField->Bounds().bottom + 11,
106 			Bounds().right - 6, 0),
107 		NULL, NULL, NULL, NULL, B_FOLLOW_LEFT_RIGHT);
108 	backgroundView->AddChild(fExpressionTextControl);
109 	fExpressionTextControl->ResizeToPreferred();
110 	fExpressionTextControl->MakeFocus(true);
111 
112 	// Set up the Invert checkbox
113 	fInverseCheckBox = new BCheckBox(
114 		BRect(7, fExpressionTextControl->Frame().bottom + 6, 6, 6), NULL,
115 		B_TRANSLATE("Invert"), NULL);
116 	backgroundView->AddChild(fInverseCheckBox);
117 	fInverseCheckBox->ResizeToPreferred();
118 
119 	// Set up the Ignore Case checkbox
120 	fIgnoreCaseCheckBox = new BCheckBox(
121 		BRect(fInverseCheckBox->Frame().right + 10,
122 			fInverseCheckBox->Frame().top, 6, 6),
123 		NULL, B_TRANSLATE("Ignore case"), NULL);
124 	fIgnoreCaseCheckBox->SetValue(1);
125 	backgroundView->AddChild(fIgnoreCaseCheckBox);
126 	fIgnoreCaseCheckBox->ResizeToPreferred();
127 
128 	// Set up the Select button
129 	fSelectButton = new BButton(BRect(0, 0, 5, 5), NULL,
130 		B_TRANSLATE("Select"), new BMessage(kSelectButtonPressed),
131 		B_FOLLOW_RIGHT);
132 
133 	backgroundView->AddChild(fSelectButton);
134 	fSelectButton->ResizeToPreferred();
135 	fSelectButton->MoveTo(Bounds().right - 10 - fSelectButton->Bounds().right,
136 		fExpressionTextControl->Frame().bottom + 9);
137 	fSelectButton->MakeDefault(true);
138 #if !B_BEOS_VERSION_DANO
139 	fSelectButton->SetLowColor(backgroundView->ViewColor());
140 	fSelectButton->SetViewColor(B_TRANSPARENT_COLOR);
141 #endif
142 
143 	font_height fh;
144 	be_plain_font->GetHeight(&fh);
145 	// Center the checkboxes vertically to the button
146 	float topMiddleButton =
147 		(fSelectButton->Bounds().Height() / 2 -
148 		(fh.ascent + fh.descent + fh.leading + 4) / 2)
149 		+ fSelectButton->Frame().top;
150 	fInverseCheckBox->MoveTo(fInverseCheckBox->Frame().left, topMiddleButton);
151 	fIgnoreCaseCheckBox->MoveTo(fIgnoreCaseCheckBox->Frame().left,
152 		topMiddleButton);
153 
154 	float bottomMinWidth = 32 + fSelectButton->Bounds().Width()
155 		+ fInverseCheckBox->Bounds().Width()
156 		+ fIgnoreCaseCheckBox->Bounds().Width();
157 	float topMinWidth = be_plain_font->StringWidth(
158 		B_TRANSLATE("Name matches wildcard expression:###"));
159 	float minWidth = bottomMinWidth > topMinWidth
160 		? bottomMinWidth : topMinWidth;
161 
162 	class EscapeFilter : public BMessageFilter {
163 	public:
164 		EscapeFilter(BWindow* target)
165 			:
166 			BMessageFilter(B_KEY_DOWN),
167 			fTarget(target)
168 		{
169 		}
170 
171 		virtual filter_result Filter(BMessage* message, BHandler** _target)
172 		{
173 			int8 byte;
174 			if (message->what == B_KEY_DOWN
175 				&& message->FindInt8("byte", &byte) == B_OK
176 				&& byte == B_ESCAPE) {
177 				fTarget->Hide();
178 				return B_SKIP_MESSAGE;
179 			}
180 			return B_DISPATCH_MESSAGE;
181 		}
182 
183 	private:
184 		BWindow* fTarget;
185 	};
186 	AddCommonFilter(new(std::nothrow) EscapeFilter(this));
187 
188 	Run();
189 
190 	Lock();
191 	ResizeTo(minWidth, fSelectButton->Frame().bottom + 6);
192 
193 	SetSizeLimits(minWidth, 1280, Bounds().bottom, Bounds().bottom);
194 
195 	MoveCloseToMouse();
196 	Unlock();
197 }
198 
199 
200 void
201 SelectionWindow::MessageReceived(BMessage* message)
202 {
203 	switch (message->what) {
204 		case kSelectButtonPressed:
205 		{
206 			Hide();
207 				// Order of posting and hiding important
208 				// since we want to activate the target
209 				// window when the message arrives.
210 				// (Hide is synhcronous, while PostMessage is not.)
211 				// See PoseView::SelectMatchingEntries().
212 
213 			BMessage* selectionInfo = new BMessage(kSelectMatchingEntries);
214 			selectionInfo->AddInt32("ExpressionType", ExpressionType());
215 			BString expression;
216 			Expression(expression);
217 			selectionInfo->AddString("Expression", expression.String());
218 			selectionInfo->AddBool("InvertSelection", Invert());
219 			selectionInfo->AddBool("IgnoreCase", IgnoreCase());
220 			fParentWindow->PostMessage(selectionInfo);
221 			break;
222 		}
223 
224 		default:
225 			_inherited::MessageReceived(message);
226 			break;
227 	}
228 }
229 
230 
231 bool
232 SelectionWindow::QuitRequested()
233 {
234 	Hide();
235 	return false;
236 }
237 
238 
239 void
240 SelectionWindow::MoveCloseToMouse()
241 {
242 	uint32 buttons;
243 	BPoint mousePosition;
244 
245 	ChildAt((int32)0)->GetMouse(&mousePosition, &buttons);
246 	ConvertToScreen(&mousePosition);
247 
248 	// Position the window centered around the mouse...
249 	BPoint windowPosition = BPoint(mousePosition.x - Frame().Width() / 2,
250 		mousePosition.y	- Frame().Height() / 2);
251 
252 	// ... unless that's outside of the current screen size:
253 	BScreen screen;
254 	windowPosition.x
255 		= MAX(20, MIN(screen.Frame().right - 20 - Frame().Width(),
256 		windowPosition.x));
257 	windowPosition.y = MAX(20,
258 		MIN(screen.Frame().bottom - 20 - Frame().Height(), windowPosition.y));
259 
260 	MoveTo(windowPosition);
261 	SetWorkspaces(1UL << current_workspace());
262 }
263 
264 
265 TrackerStringExpressionType
266 SelectionWindow::ExpressionType() const
267 {
268 	if (!fMatchingTypeMenuField->LockLooper())
269 		return kNone;
270 
271 	BMenuItem* item = fMatchingTypeMenuField->Menu()->FindMarked();
272 	if (item == NULL) {
273 		fMatchingTypeMenuField->UnlockLooper();
274 		return kNone;
275 	}
276 
277 	int32 index = fMatchingTypeMenuField->Menu()->IndexOf(item);
278 
279 	fMatchingTypeMenuField->UnlockLooper();
280 
281 	if (index < kStartsWith || index > kRegexpMatch)
282 		return kNone;
283 
284 	TrackerStringExpressionType typeArray[] = {	kStartsWith, kEndsWith,
285 		kContains, kGlobMatch, kRegexpMatch};
286 
287 	return typeArray[index];
288 }
289 
290 
291 void
292 SelectionWindow::Expression(BString &result) const
293 {
294 	if (!fExpressionTextControl->LockLooper())
295 		return;
296 
297 	result = fExpressionTextControl->Text();
298 
299 	fExpressionTextControl->UnlockLooper();
300 }
301 
302 
303 bool
304 SelectionWindow::IgnoreCase() const
305 {
306 	if (!fIgnoreCaseCheckBox->LockLooper()) {
307 		// default action
308 		return true;
309 	}
310 
311 	bool ignore = fIgnoreCaseCheckBox->Value() != 0;
312 
313 	fIgnoreCaseCheckBox->UnlockLooper();
314 
315 	return ignore;
316 }
317 
318 
319 bool
320 SelectionWindow::Invert() const
321 {
322 	if (!fInverseCheckBox->LockLooper()) {
323 		// default action
324 		return false;
325 	}
326 
327 	bool inverse = fInverseCheckBox->Value() != 0;
328 
329 	fInverseCheckBox->UnlockLooper();
330 
331 	return inverse;
332 }
333