xref: /haiku/src/apps/diskusage/ControlsView.cpp (revision e0ef64750f3169cd634bb2f7a001e22488b05231)
1 /*
2  * Copyright (c) 2008 Stephan Aßmus <superstippi@gmx.de>. All rights reserved.
3  * Distributed under the terms of the MIT/X11 license.
4  *
5  * Copyright (c) 1999 Mike Steed. You are free to use and distribute this software
6  * as long as it is accompanied by it's documentation and this copyright notice.
7  * The software comes with no warranty, etc.
8  */
9 
10 
11 #include "ControlsView.h"
12 
13 #include <Bitmap.h>
14 #include <Box.h>
15 #include <MenuField.h>
16 #include <MenuItem.h>
17 #include <NodeMonitor.h>
18 #include <PopUpMenu.h>
19 #include <SupportDefs.h>
20 #include <Volume.h>
21 #include <VolumeRoster.h>
22 #include <Window.h>
23 
24 #include "Common.h"
25 
26 
27 class VolumeMenuItem: public BMenuItem {
28 public:
29 						VolumeMenuItem(BVolume* volume, BMessage* message);
30 	virtual				~VolumeMenuItem();
31 
32 	virtual	void		GetContentSize(float* width, float* height);
33 	virtual	void		DrawContent();
34 
35 			BVolume*	Volume() const
36 							{ return fVolume; }
37 			status_t	Invoke(BMessage* mesage = NULL)
38 							{ return BMenuItem::Invoke(); }
39 
40 private:
41 			BBitmap*	fIcon;
42 			BVolume*	fVolume;
43 };
44 
45 
46 VolumeMenuItem::VolumeMenuItem(BVolume* volume, BMessage* message)
47 	:
48 	BMenuItem(kEmptyStr, message),
49 	fIcon(new BBitmap(BRect(0, 0, 15, 15), B_RGBA32)),
50 	fVolume(volume)
51 {
52 	char name[B_PATH_NAME_LENGTH];
53 	fVolume->GetName(name);
54 	SetLabel(name);
55 
56 	if (fVolume->GetIcon(fIcon, B_MINI_ICON) < B_OK) {
57 		delete fIcon;
58 		fIcon = NULL;
59 	}
60 }
61 
62 
63 VolumeMenuItem::~VolumeMenuItem()
64 {
65 	delete fIcon;
66 	delete fVolume;
67 }
68 
69 
70 void
71 VolumeMenuItem::GetContentSize(float* width, float* height)
72 {
73 	*width = be_plain_font->StringWidth(Label());
74 
75 	struct font_height fh;
76 	be_plain_font->GetHeight(&fh);
77 	float fontHeight = fh.ascent + fh.descent + fh.leading;
78 	if (fIcon) {
79 		*height = max_c(fontHeight, fIcon->Bounds().Height());
80 		*width += fIcon->Bounds().Width() + kSmallHMargin;
81 	} else
82 		*height = fontHeight;
83 }
84 
85 
86 void
87 VolumeMenuItem::DrawContent()
88 {
89 	if (fIcon) {
90 		Menu()->SetDrawingMode(B_OP_OVER);
91 		Menu()->MovePenBy(0.0, -1.0);
92 		Menu()->DrawBitmap(fIcon);
93 		Menu()->SetDrawingMode(B_OP_COPY);
94 		Menu()->MovePenBy(fIcon->Bounds().Width() + kSmallHMargin, 0.0);
95 	}
96 	BMenuItem::DrawContent();
97 }
98 
99 
100 // #pragma mark -
101 
102 
103 class ControlsView::VolumePopup: public BMenuField {
104 public:
105 								VolumePopup(BRect r);
106 	virtual						~VolumePopup();
107 
108 	virtual	void				AttachedToWindow();
109 	virtual	void				MessageReceived(BMessage* message);
110 
111 			BVolume*			FindDeviceFor(dev_t device,
112 									bool invoke = false);
113 
114 private:
115 			void				_AddVolume(dev_t device);
116 			void				_RemoveVolume(dev_t device);
117 
118 			BVolumeRoster*		fVolumeRoster;
119 };
120 
121 
122 ControlsView::VolumePopup::VolumePopup(BRect r)
123 	:
124 	BMenuField(r, NULL, kVolMenuLabel, new BPopUpMenu(kVolMenuDefault), false,
125 		B_FOLLOW_LEFT)
126 {
127 	SetViewColor(kWindowColor);
128 	SetLowColor(kWindowColor);
129 
130 	SetDivider(kSmallHMargin + StringWidth(kVolMenuLabel));
131 
132 	// Populate the menu with the persistent volumes.
133 	fVolumeRoster = new BVolumeRoster();
134 
135 	BVolume tempVolume;
136 	while (fVolumeRoster->GetNextVolume(&tempVolume) == B_OK) {
137 		if (tempVolume.IsPersistent()) {
138 			BVolume* volume = new BVolume(tempVolume);
139 			BMessage* message = new BMessage(kMenuSelectVol);
140 			message->AddPointer(kNameVolPtr, volume);
141 			VolumeMenuItem *item = new VolumeMenuItem(volume, message);
142 			Menu()->AddItem(item);
143 		}
144 	}
145 }
146 
147 
148 ControlsView::VolumePopup::~VolumePopup()
149 {
150 	fVolumeRoster->StopWatching();
151 	delete fVolumeRoster;
152 }
153 
154 
155 void
156 ControlsView::VolumePopup::AttachedToWindow()
157 {
158 	// Begin watching mount and unmount events.
159 	fVolumeRoster->StartWatching(BMessenger(this));
160 }
161 
162 
163 void
164 ControlsView::VolumePopup::MessageReceived(BMessage* message)
165 {
166 	switch (message->what) {
167 		case B_NODE_MONITOR:
168 			switch (message->FindInt32("opcode")) {
169 				case B_DEVICE_MOUNTED:
170 					_AddVolume(message->FindInt32("new device"));
171 					break;
172 
173 				case B_DEVICE_UNMOUNTED:
174 					_RemoveVolume(message->FindInt32("device"));
175 					break;
176 			}
177 			break;
178 
179 		default:
180 			BMenuField::MessageReceived(message);
181 			break;
182 	}
183 }
184 
185 
186 BVolume*
187 ControlsView::VolumePopup::FindDeviceFor(dev_t device, bool invoke)
188 {
189 	BVolume* volume = NULL;
190 
191 	// Iterate through items looking for a BVolume representing this device.
192 	for (int i = 0; VolumeMenuItem* item = (VolumeMenuItem*)Menu()->ItemAt(i); i++) {
193 		if (item->Volume()->Device() == device) {
194 			volume = item->Volume();
195 			if (invoke)
196 				item->Invoke();
197 			break;
198 		}
199 	}
200 
201 	return volume;
202 }
203 
204 
205 void
206 ControlsView::VolumePopup::_AddVolume(dev_t device)
207 {
208 	// Make sure the volume is not already in the menu.
209 	for (int i = 0; i < Menu()->CountItems(); i++) {
210 		VolumeMenuItem* item = (VolumeMenuItem*)Menu()->ItemAt(i);
211 		if (item->Volume()->Device() == device)
212 			return;
213 	}
214 
215 	// Add the newly mounted volume to the menu.
216 	BVolume* volume = new BVolume(device);
217 	BMessage* message = new BMessage(kMenuSelectVol);
218 	message->AddPointer(kNameVolPtr, volume);
219 	VolumeMenuItem* item = new VolumeMenuItem(volume, message);
220 	Menu()->AddItem(item);
221 }
222 
223 
224 void
225 ControlsView::VolumePopup::_RemoveVolume(dev_t device)
226 {
227 	for (int i = 0; i < Menu()->CountItems(); i++) {
228 		VolumeMenuItem* item = (VolumeMenuItem*)Menu()->ItemAt(i);
229 		if (item->Volume()->Device() == device) {
230 			// If the volume being removed is currently selected, prompt for a
231 			// different volume.
232 			if (item->IsMarked()) {
233 				// update the displayed volume label now that there is no marked
234 				// item:
235 				Menu()->Superitem()->SetLabel(kVolMenuDefault);
236 
237 				BMessage messae(kMenuSelectVol);
238 				messae.AddPointer(kNameVolPtr, NULL);
239 				Window()->PostMessage(&messae);
240 			}
241 
242 			Menu()->RemoveItem(item);
243 			return;
244 		}
245 	}
246 }
247 
248 
249 // #pragma mark -
250 
251 
252 ControlsView::ControlsView(BRect r)
253 	: BView(r, NULL, B_FOLLOW_LEFT_RIGHT, 0)
254 {
255 	SetViewColor(kWindowColor);
256 
257 	r.top += kSmallVMargin;
258 	r.right -= kSmallHMargin;
259 	float buttonWidth = kButtonMargin + StringWidth(kHelpBtnLabel);
260 	r.left = r.right - buttonWidth;
261 	BButton* helpButton = new BButton(r, NULL, kHelpBtnLabel, new BMessage(kBtnHelp),
262 		B_FOLLOW_RIGHT);
263 	if (!kFoundHelpFile)
264 		helpButton->SetEnabled(false);
265 
266 	r.right = r.left - kSmallHMargin;
267 	buttonWidth = kButtonMargin + StringWidth(kStrRescan);
268 	r.left = r.right - max_c(kMinButtonWidth, buttonWidth);
269 	fRescanButton = new BButton(r, NULL, kStrRescan, new BMessage(kBtnRescan),
270 		B_FOLLOW_RIGHT);
271 
272 	fVolumePopup = new VolumePopup(
273 		BRect(kSmallHMargin, kSmallVMargin, r.left - kSmallHMargin, kSmallVMargin));
274 
275 	float width, height;
276 	fRescanButton->GetPreferredSize(&width, &height);
277 	ResizeTo(Bounds().Width(), height + 6.0);
278 
279 	// Horizontal divider
280 	r = Bounds();
281 	r.top = r.bottom - 1.0;
282 	r.left -= 5.0; r.right += 5.0;
283 	BBox* divider = new BBox(r, NULL, B_FOLLOW_LEFT_RIGHT, B_WILL_DRAW);
284 
285 	AddChild(fVolumePopup);
286 	AddChild(divider);
287 	AddChild(fRescanButton);
288 	AddChild(helpButton);
289 }
290 
291 
292 ControlsView::~ControlsView()
293 {
294 }
295 
296 
297 BVolume*
298 ControlsView::FindDeviceFor(dev_t device, bool invoke)
299 {
300 	return fVolumePopup->FindDeviceFor(device, invoke);
301 }
302