1//--------------------------------------------------------------------
2//
3//	MenuWindow.cpp
4//
5//	Written by: Owen Smith
6//
7//--------------------------------------------------------------------
8
9/*
10	Copyright 1999, Be Incorporated.   All Rights Reserved.
11	This file may be used under the terms of the Be Sample Code License.
12*/
13
14#include <Application.h>
15#include <Box.h>
16#include <CheckBox.h>
17#include <Menu.h>
18#include <MenuBar.h>
19#include <MenuItem.h>
20#include <StringView.h>
21#include <stdio.h>
22#include <string.h>
23
24#include "constants.h"
25#include "MenuView.h"
26#include "MenuWindow.h"
27#include "stddlg.h"
28
29//====================================================================
30//	MenuWindow Implementation
31
32#define MAX_TEST_STATUS_CHARS 25
33
34
35//--------------------------------------------------------------------
36//	MenuWindow constructors, destructors, operators
37
38MenuWindow::MenuWindow(const char* name)
39	: BWindow(BRect(60,60,60,60), name, B_TITLED_WINDOW,
40		B_NOT_RESIZABLE | B_NOT_ZOOMABLE)
41{
42	m_bUsingFullMenuBar = true;
43
44	// menu bars
45	BRect dummyFrame(0, 0, 0, 0);
46	m_pFullMenuBar = new BMenuBar(dummyFrame, "Full Menu Bar");
47	m_pHiddenMenuBar = new BMenuBar(dummyFrame, "Menu Bar w. Hidden User Menus");
48
49	// File menu
50	BMenu* pMenu = BuildFileMenu();
51	if (pMenu) {
52		m_pFullMenuBar->AddItem(pMenu);
53	}
54	pMenu = BuildFileMenu();
55	if (pMenu) {
56		m_pHiddenMenuBar->AddItem(pMenu);
57	}
58
59	// Test menu
60	pMenu = m_testMenuBuilder.BuildTestMenu(B_MINI_ICON);
61	if (pMenu) {
62		m_pFullMenuBar->AddItem(pMenu);
63	}
64	pMenu = m_testMenuBuilder.BuildTestMenu(B_MINI_ICON);
65	if (pMenu) {
66		m_pHiddenMenuBar->AddItem(pMenu);
67	}
68
69	// add child after menus are added so its initially
70	// calculated app_server bounds take added menus into
71	// account
72	AddChild(m_pFullMenuBar);
73
74	float menuHeight = m_pFullMenuBar->Bounds().Height();
75
76	// Menu view
77	m_pMenuView = new MenuView(B_FOLLOW_NONE); // don't follow window just yet!
78	m_pMenuView->MoveBy(0, menuHeight + 1);
79	AddChild(m_pMenuView);
80
81	// Status view
82	BRect menuViewRect = m_pMenuView->Frame();
83	float top = menuViewRect.bottom + 1;
84	font_height plainHeight;
85	be_plain_font->GetHeight(&plainHeight);
86
87	// Simulate a vertical divider by making a BBox where only the top side
88	// can be seen in the window.
89	BRect boxFrame;
90	boxFrame.Set(menuViewRect.left - 2,
91		top,
92		menuViewRect.right + 2,
93		top + plainHeight.ascent + plainHeight.descent + plainHeight.leading + 4);
94
95	BBox* pStatusBox = new BBox(boxFrame);
96	AddChild(pStatusBox);
97
98	BRect statusFrame = pStatusBox->Bounds();
99	statusFrame.InsetBy(2,2);
100	m_pStatusView = new BStringView(statusFrame, "Status View", STR_STATUS_DEFAULT,
101		B_FOLLOW_ALL); // don't follow window just yet!
102	m_pStatusView->SetViewColor(BKG_GREY);
103	pStatusBox->AddChild(m_pStatusView);
104
105	// Resize window dynamically to fit MenuView (and Status View)
106	float windowWidth = m_pMenuView->Frame().right;
107	float windowHeight = boxFrame.bottom - 4;
108	ResizeTo(windowWidth, windowHeight);
109}
110
111
112
113//--------------------------------------------------------------------
114//	MenuWindow virtual function overrides
115
116void MenuWindow::MenusBeginning(void)
117{
118	if ((! Valid()) || (! m_bUsingFullMenuBar)) {
119		return;
120	}
121
122	int32 len = m_pFullMenuBar->CountItems();
123	for (int32 i = 2; i < len; i++) // skipping File and Test menus
124	{
125		BMenu* pMenu = m_pFullMenuBar->SubmenuAt(i);
126		if (pMenu) {
127			m_pMenuView->PopulateUserMenu(pMenu, i - 2);
128		}
129	}
130}
131
132void MenuWindow::MessageReceived(BMessage* message)
133{
134	switch (message->what) {
135	case MSG_WIN_ADD_MENU:
136		AddMenu(message);
137		break;
138	case MSG_WIN_DELETE_MENU:
139		DeleteMenu(message);
140		break;
141	case MSG_TEST_ITEM:
142		TestMenu(message);
143		break;
144	case MSG_USER_ITEM:
145		UserMenu(message);
146		break;
147	case MSG_WIN_HIDE_USER_MENUS:
148		ToggleUserMenus(message);
149		break;
150	case MSG_WIN_LARGE_TEST_ICONS:
151		ToggleTestIcons(message);
152		break;
153	default:
154		BWindow::MessageReceived(message);
155		break;
156	}
157}
158
159bool MenuWindow::QuitRequested(void)
160{
161	be_app->PostMessage(B_QUIT_REQUESTED);
162	return true;
163}
164
165
166
167//--------------------------------------------------------------------
168//	MenuWindow operations
169
170void MenuWindow::UpdateStatus(const char* str1, const char* str2)
171{
172	uint32 lenTotal = 0, len1 = 0, len2 = 0;
173
174	if (str1)
175		len1 = strlen(str1);
176	if (str2)
177		len2 = strlen(str2);
178
179	lenTotal = len1 + len2;
180	char* updateText = new char[lenTotal+1];
181	updateText[0] = '\0'; // in case str1 and str2 are both NULL
182
183	if (str1)
184		strcpy(updateText, str1);
185	if (str2)
186		strcpy(updateText + len1, str2);
187
188	if (Lock() && Valid()) {
189		m_pStatusView->SetText(updateText);
190		Unlock();
191	}
192
193	delete [] updateText;
194}
195
196
197
198//--------------------------------------------------------------------
199//	MenuWindow message handlers
200
201void MenuWindow::AddMenu(BMessage* message)
202{
203	if (! Valid()) {
204		return;
205	}
206
207	const char* menuName;
208	if (message->FindString("Menu Name", &menuName) == B_OK) {
209		m_pFullMenuBar->AddItem(new BMenu(menuName));
210		UpdateStatus(STR_STATUS_ADD_MENU, menuName);
211	}
212}
213
214void MenuWindow::DeleteMenu(BMessage* message)
215{
216	if (! Valid()) {
217		return;
218	}
219
220	int32 i;
221	if (message->FindInt32("Menu Index", &i) == B_OK) {
222		BMenuItem* pItem = m_pFullMenuBar->ItemAt(i+2);
223		if (pItem) {
224			// menu index is the above index + 2 (for File and Test menus)
225			m_pFullMenuBar->RemoveItem(pItem);
226			UpdateStatus(STR_STATUS_DELETE_MENU, pItem->Label());
227			delete pItem;
228		}
229	}
230}
231
232void MenuWindow::TestMenu(BMessage* message)
233{
234	if (! Valid()) {
235		return;
236	}
237
238	int32 i;
239	if (message->FindInt32("Item Index", &i) == B_OK) {
240		char numText[3];
241		sprintf(numText, "%ld", i);
242		UpdateStatus(STR_STATUS_TEST, numText);
243	}
244}
245
246void MenuWindow::UserMenu(BMessage* message)
247{
248	if (! Valid()) {
249		return;
250	}
251
252	const char* itemName;
253	if (message->FindString("Item Name", &itemName) == B_OK) {
254		UpdateStatus(STR_STATUS_USER, itemName);
255	}
256}
257
258void MenuWindow::ToggleUserMenus(BMessage* message)
259{
260	if (! Valid()) {
261		return;
262	}
263
264	void* pSrc;
265	bool useFullMenus = false;
266
267	if (message->FindPointer("source", &pSrc) == B_OK) {
268		BCheckBox* pCheckBox = reinterpret_cast<BCheckBox*>(pSrc);
269		useFullMenus = (pCheckBox->Value() == B_CONTROL_OFF);
270	}
271
272	if ((! useFullMenus) && m_bUsingFullMenuBar) {
273		RemoveChild(m_pFullMenuBar);
274		AddChild(m_pHiddenMenuBar);
275		m_bUsingFullMenuBar = false;
276	} else if (useFullMenus && (! m_bUsingFullMenuBar)) {
277		RemoveChild(m_pHiddenMenuBar);
278		AddChild(m_pFullMenuBar);
279		m_bUsingFullMenuBar = true;
280	}
281}
282
283void MenuWindow::ToggleTestIcons(BMessage* message)
284{
285	if (! Valid()) {
286		return;
287	}
288
289	void* pSrc;
290	icon_size size = B_MINI_ICON;
291
292	if (message->FindPointer("source", &pSrc) == B_OK) {
293		BCheckBox* pCheckBox = reinterpret_cast<BCheckBox*>(pSrc);
294		size = (pCheckBox->Value() == B_CONTROL_ON) ? B_LARGE_ICON : B_MINI_ICON;
295	}
296
297	ReplaceTestMenu(m_pFullMenuBar, size);
298	ReplaceTestMenu(m_pHiddenMenuBar, size);
299}
300
301
302
303//--------------------------------------------------------------------
304//	MenuWindow implementation member functions
305
306bool MenuWindow::Valid(void) const
307{
308	if (! m_pFullMenuBar) {
309		ierror(STR_NO_FULL_MENU_BAR);
310		return false;
311	}
312	if (! m_pHiddenMenuBar) {
313		ierror(STR_NO_HIDDEN_MENU_BAR);
314		return false;
315	}
316	if (! m_pMenuView) {
317		ierror(STR_NO_MENU_VIEW);
318		return false;
319	}
320	if (! m_pStatusView) {
321		ierror(STR_NO_STATUS_VIEW);
322		return false;
323	}
324	return true;
325}
326
327BMenu* MenuWindow::BuildFileMenu(void) const
328{
329	BMenu* pMenu = new BMenu(STR_MNU_FILE);
330
331	BMenuItem* pAboutItem = new BMenuItem(STR_MNU_FILE_ABOUT,
332		new BMessage(B_ABOUT_REQUESTED));
333	pAboutItem->SetTarget(NULL, be_app);
334	pMenu->AddItem(pAboutItem);
335
336	pMenu->AddSeparatorItem();
337
338	pMenu->AddItem(new BMenuItem(STR_MNU_FILE_CLOSE,
339		new BMessage(B_QUIT_REQUESTED), CMD_FILE_CLOSE));
340
341	return pMenu;
342}
343
344void MenuWindow::ReplaceTestMenu(BMenuBar* pMenuBar, icon_size size)
345{
346	if (! pMenuBar) {
347		return;
348	}
349
350	BMenu* pTestMenu = m_testMenuBuilder.BuildTestMenu(size);
351	if (pTestMenu) {
352		BMenuItem* pPrevItem = pMenuBar->RemoveItem(1);
353		if (pPrevItem) {
354			delete pPrevItem;
355		}
356		pMenuBar->AddItem(pTestMenu, 1);
357	}
358}
359