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