xref: /haiku/src/kits/interface/BMCPrivate.cpp (revision 2600324b57fa31cdea1627d584d314f2a579c4a8)
1 /*
2  * Copyright 2001-2006, Haiku Inc.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Marc Flerackers (mflerackers@androme.be)
7  *		Stephan Aßmus <superstippi@gmx.de>
8  */
9 
10 #include <stdio.h>
11 
12 #include <BMCPrivate.h>
13 #include <LayoutUtils.h>
14 #include <MenuField.h>
15 #include <MenuItem.h>
16 #include <Message.h>
17 #include <MessageRunner.h>
18 #include <Region.h>
19 #include <Window.h>
20 
21 
22 _BMCFilter_::_BMCFilter_(BMenuField *menuField, uint32 what)
23 	:
24 	BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE, what),
25 	fMenuField(menuField)
26 {
27 }
28 
29 
30 _BMCFilter_::~_BMCFilter_()
31 {
32 }
33 
34 
35 filter_result
36 _BMCFilter_::Filter(BMessage *message, BHandler **handler)
37 {
38 	if (message->what == B_MOUSE_DOWN) {
39 		if (BView *view = dynamic_cast<BView *>(*handler)) {
40 			BPoint point;
41 			message->FindPoint("be:view_where", &point);
42 			view->ConvertToParent(&point);
43 			message->ReplacePoint("be:view_where", point);
44 			*handler = fMenuField;
45 		}
46 	}
47 
48 	return B_DISPATCH_MESSAGE;
49 }
50 
51 
52 _BMCFilter_ &
53 _BMCFilter_::operator=(const _BMCFilter_ &)
54 {
55 	return *this;
56 }
57 
58 
59 _BMCMenuBar_::_BMCMenuBar_(BRect frame, bool fixedSize, BMenuField *menuField)
60 	: BMenuBar(frame, "_mc_mb_", B_FOLLOW_LEFT | B_FOLLOW_TOP, B_ITEMS_IN_ROW,
61 		!fixedSize),
62 	fMenuField(menuField),
63 	fFixedSize(fixedSize),
64 	fRunner(NULL),
65 	fShowPopUpMarker(true)
66 {
67 	SetFlags(Flags() | B_FRAME_EVENTS);
68 	SetBorder(B_BORDER_CONTENTS);
69 
70 	float left, top, right, bottom;
71 	GetItemMargins(&left, &top, &right, &bottom);
72 	// give a bit more space to draw the small thumb
73 	left -= 1;
74 	right += 3;
75 	SetItemMargins(left, top, right, bottom);
76 
77 	SetMaxContentWidth(frame.Width() - (left + right));
78 
79 	fPreviousWidth = frame.Width();
80 }
81 
82 
83 _BMCMenuBar_::_BMCMenuBar_(BMessage *data)
84 	:	BMenuBar(data),
85 	fMenuField(NULL),
86 	fFixedSize(true),
87 	fRunner(NULL),
88 	fShowPopUpMarker(true)
89 {
90 	SetFlags(Flags() | B_FRAME_EVENTS);
91 
92 	bool resizeToFit;
93 	if (data->FindBool("_rsize_to_fit", &resizeToFit) == B_OK)
94 		fFixedSize = !resizeToFit;
95 }
96 
97 
98 _BMCMenuBar_::~_BMCMenuBar_()
99 {
100 	delete fRunner;
101 }
102 
103 
104 BArchivable *
105 _BMCMenuBar_::Instantiate(BMessage *data)
106 {
107 	if (validate_instantiation(data, "_BMCMenuBar_"))
108 		return new _BMCMenuBar_(data);
109 
110 	return NULL;
111 }
112 
113 
114 void
115 _BMCMenuBar_::AttachedToWindow()
116 {
117 	fMenuField = static_cast<BMenuField *>(Parent());
118 
119 	// Don't cause the KeyMenuBar to change by being attached
120 	BMenuBar *menuBar = Window()->KeyMenuBar();
121 	BMenuBar::AttachedToWindow();
122 	if (fFixedSize)
123 		SetResizingMode(B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP);
124 	Window()->SetKeyMenuBar(menuBar);
125 }
126 
127 
128 void
129 _BMCMenuBar_::Draw(BRect updateRect)
130 {
131 	if (!fShowPopUpMarker) {
132 		BMenuBar::Draw(updateRect);
133 		return;
134 	}
135 
136 	// draw the right side with the popup marker
137 
138 	// prevent the original BMenuBar's Draw from
139 	// drawing in those parts
140 	BRect bounds(Bounds());
141 	bounds.right -= 10.0;
142 	bounds.bottom -= 1.0;
143 
144 	BRegion clipping(bounds);
145 	ConstrainClippingRegion(&clipping);
146 
147 	BMenuBar::Draw(updateRect);
148 
149 	// restore clipping
150 	ConstrainClippingRegion(NULL);
151 	bounds.right += 10.0;
152 	bounds.bottom += 1.0;
153 
154 	// prepare some colors
155 	rgb_color normalNoTint = LowColor();
156 	rgb_color noTint = tint_color(normalNoTint, 0.74);
157 	rgb_color darken4;
158 	rgb_color normalDarken4;
159 	rgb_color darken1;
160 	rgb_color lighten1;
161 	rgb_color lighten2;
162 
163 	if (IsEnabled()) {
164 		darken4 = tint_color(noTint, B_DARKEN_4_TINT);
165 		normalDarken4 = tint_color(normalNoTint, B_DARKEN_4_TINT);
166 		darken1 = tint_color(noTint, B_DARKEN_1_TINT);
167 		lighten1 = tint_color(noTint, B_LIGHTEN_1_TINT);
168 		lighten2 = tint_color(noTint, B_LIGHTEN_2_TINT);
169 	} else {
170 		darken4 = tint_color(noTint, B_DARKEN_2_TINT);
171 		normalDarken4 = tint_color(normalNoTint, B_DARKEN_2_TINT);
172 		darken1 = tint_color(noTint, (B_NO_TINT + B_DARKEN_1_TINT) / 2.0);
173 		lighten1 = tint_color(noTint, (B_NO_TINT + B_LIGHTEN_1_TINT) / 2.0);
174 		lighten2 = tint_color(noTint, B_LIGHTEN_1_TINT);
175 	}
176 
177 	BRect r(bounds);
178 	r.left = r.right - 10.0;
179 
180 	BeginLineArray(6);
181 		// bottom below item text, darker then BMenuBar
182 		// would normaly draw it
183 		AddLine(BPoint(bounds.left, r.bottom),
184 				BPoint(r.left - 1.0, r.bottom), normalDarken4);
185 
186 		// bottom below popup marker
187 		AddLine(BPoint(r.left, r.bottom),
188 				BPoint(r.right, r.bottom), darken4);
189 		// right of popup marker
190 		AddLine(BPoint(r.right, r.bottom - 1),
191 				BPoint(r.right, r.top), darken4);
192 		// top above popup marker
193 		AddLine(BPoint(r.left, r.top),
194 				BPoint(r.right - 2, r.top), lighten2);
195 
196 		r.top += 1;
197 		r.bottom -= 1;
198 		r.right -= 1;
199 
200 		// bottom below popup marker
201 		AddLine(BPoint(r.left, r.bottom),
202 				BPoint(r.right, r.bottom), darken1);
203 		// right of popup marker
204 		AddLine(BPoint(r.right, r.bottom - 1),
205 				BPoint(r.right, r.top), darken1);
206 	EndLineArray();
207 
208 	r.bottom -= 1;
209 	r.right -= 1;
210 	SetHighColor(noTint);
211 	FillRect(r);
212 
213 	// popup marker
214 	BPoint center(roundf((r.left + r.right) / 2.0),
215 				  roundf((r.top + r.bottom) / 2.0));
216 	BPoint triangle[3];
217 	triangle[0] = center + BPoint(-2.5, -0.5);
218 	triangle[1] = center + BPoint(2.5, -0.5);
219 	triangle[2] = center + BPoint(0.0, 2.0);
220 
221 	uint32 flags = Flags();
222 	SetFlags(flags | B_SUBPIXEL_PRECISE);
223 
224 	SetHighColor(normalDarken4);
225 	FillTriangle(triangle[0], triangle[1], triangle[2]);
226 
227 	SetFlags(flags);
228 }
229 
230 
231 void
232 _BMCMenuBar_::FrameResized(float width, float height)
233 {
234 	// we need to take care of resizing and cleaning up
235 	// the parent menu field
236 	float diff;
237 	if (fFixedSize)
238 		diff = width - fPreviousWidth;
239 	else
240 		diff = Frame().right - (fMenuField->Bounds().right - 2);
241 
242 	fPreviousWidth = width;
243 
244 	if (Window()) {
245 		if (diff > 0) {
246 			// clean up the dirty right border of
247 			// the menu field when enlarging
248 			BRect dirty(fMenuField->Bounds());
249 			dirty.left = dirty.right - diff - 2;
250 			fMenuField->Invalidate(dirty);
251 
252 			// clean up the arrow part
253 			dirty = Bounds();
254 			dirty.left = dirty.right - diff - 12;
255 			Invalidate(dirty);
256 
257 		} else if (diff < 0) {
258 			// clean up the dirty right line of
259 			// the menu field when shrinking
260 			BRect dirty(fMenuField->Bounds());
261 			dirty.left = dirty.right - 2;
262 			fMenuField->Invalidate(dirty);
263 
264 			// clean up the arrow part
265 			dirty = Bounds();
266 			dirty.left = dirty.right - 12;
267 			Invalidate(dirty);
268 		}
269 	}
270 
271 	if (!fFixedSize && ResizingMode() == (B_FOLLOW_LEFT | B_FOLLOW_TOP)) {
272 		// we have been shrinked or enlarged and need to take care
273 		// of the size of the parent menu field as well
274 		// NOTE: no worries about follow mode, we follow left and top
275 		// in autosize mode
276 		fMenuField->ResizeBy(diff, 0.0);
277 	}
278 	BMenuBar::FrameResized(width, height);
279 }
280 
281 
282 void
283 _BMCMenuBar_::MessageReceived(BMessage *msg)
284 {
285 	switch (msg->what) {
286 		case 'TICK':
287 		{
288 			BMenuItem *item = ItemAt(0);
289 
290 			if (item && item->Submenu() &&  item->Submenu()->Window()) {
291 				BMessage message(B_KEY_DOWN);
292 
293 				message.AddInt8("byte", B_ESCAPE);
294 				message.AddInt8("key", B_ESCAPE);
295 				message.AddInt32("modifiers", 0);
296 				message.AddInt8("raw_char", B_ESCAPE);
297 
298 				Window()->PostMessage(&message, this, NULL);
299 			}
300 		}
301 		// fall through
302 		default:
303 			BMenuBar::MessageReceived(msg);
304 			break;
305 	}
306 }
307 
308 
309 void
310 _BMCMenuBar_::MakeFocus(bool focused)
311 {
312 	if (IsFocus() == focused)
313 		return;
314 
315 	BMenuBar::MakeFocus(focused);
316 
317 	if (focused) {
318 		BMessage message('TICK');
319 		//fRunner = new BMessageRunner(BMessenger(this, NULL, NULL), &message,
320 		//	50000, -1);
321 	} else if (fRunner) {
322 		//delete fRunner;
323 		fRunner = NULL;
324 	}
325 
326 	if (focused)
327 		return;
328 
329 	fMenuField->fSelected = false;
330 	fMenuField->fTransition = true;
331 
332 	BRect bounds(fMenuField->Bounds());
333 
334 	fMenuField->Invalidate(BRect(bounds.left, bounds.top, fMenuField->fDivider,
335 		bounds.bottom));
336 }
337 
338 
339 BSize
340 _BMCMenuBar_::MaxSize()
341 {
342 	// The maximum width of a normal BMenuBar is unlimited, but we want it
343 	// limited.
344 	BSize size;
345 	BMenuBar::GetPreferredSize(&size.width, &size.height);
346 	return BLayoutUtils::ComposeSize(ExplicitMaxSize(), size);
347 }
348 
349 
350 _BMCMenuBar_
351 &_BMCMenuBar_::operator=(const _BMCMenuBar_ &)
352 {
353 	return *this;
354 }
355