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