xref: /haiku/src/apps/diskusage/ControlsView.cpp (revision 12e4ac5d8157bb9eda511995f002158f79c23644)
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 <string.h>
14 
15 #include <Bitmap.h>
16 #include <Box.h>
17 #include <TabView.h>
18 #include <NodeMonitor.h>
19 #include <Path.h>
20 #include <PopUpMenu.h>
21 #include <String.h>
22 #include <SupportDefs.h>
23 #include <View.h>
24 #include <Volume.h>
25 #include <VolumeRoster.h>
26 #include <Window.h>
27 
28 #include <LayoutBuilder.h>
29 
30 #include "DiskUsage.h"
31 #include "VolumeView.h"
32 
33 
34 class VolumeTab: public BTab {
35 public:
36 								VolumeTab(BVolume* volume);
37 	virtual						~VolumeTab();
38 
Volume() const39 			BVolume*			Volume() const
40 									{ return fVolume; }
41 			float				IconWidth() const;
42 
43 	virtual	void				DrawLabel(BView* owner, BRect frame);
44 	virtual	void				DrawFocusMark(BView* owner, BRect frame);
45 
46 private:
47 			BBitmap*			fIcon;
48 			BVolume*			fVolume;
49 };
50 
51 
VolumeTab(BVolume * volume)52 VolumeTab::VolumeTab(BVolume* volume)
53 	:
54 	BTab(),
55 	fIcon(new BBitmap(BRect(0, 0, 15, 15), B_RGBA32)),
56 	fVolume(volume)
57 {
58 	if (fVolume->GetIcon(fIcon, B_MINI_ICON) < B_OK) {
59 		delete fIcon;
60 		fIcon = NULL;
61 	}
62 }
63 
64 
65 float
IconWidth() const66 VolumeTab::IconWidth() const
67 {
68 	if (fIcon != NULL)
69 		// add a small margin
70 		return fIcon->Bounds().Width() + kSmallHMargin;
71 	else
72 		return 0.0f;
73 }
74 
75 
76 void
DrawLabel(BView * owner,BRect frame)77 VolumeTab::DrawLabel(BView* owner, BRect frame)
78 {
79 	owner->SetDrawingMode(B_OP_OVER);
80 	if (fIcon != NULL) {
81 		owner->MovePenTo(frame.left + kSmallHMargin,
82 			(frame.top + frame.bottom - fIcon->Bounds().Height()) / 2.0);
83 		owner->DrawBitmap(fIcon);
84 	}
85 	font_height fh;
86 	owner->GetFontHeight(&fh);
87 
88 	BString label = Label();
89 
90 	owner->TruncateString(&label, B_TRUNCATE_END,
91 		frame.Width() - IconWidth() - kSmallHMargin);
92 
93 	owner->SetHighColor(ui_color(B_CONTROL_TEXT_COLOR));
94 	owner->DrawString(label,
95 		BPoint(frame.left + IconWidth() + kSmallHMargin,
96 			(frame.top + frame.bottom - fh.ascent - fh.descent) / 2.0
97 				+ fh.ascent));
98 }
99 
100 
101 void
DrawFocusMark(BView * owner,BRect frame)102 VolumeTab::DrawFocusMark(BView* owner, BRect frame)
103 {
104 	frame.left += IconWidth();
105 	BTab::DrawFocusMark(owner, frame);
106 }
107 
108 
~VolumeTab()109 VolumeTab::~VolumeTab()
110 {
111 	delete fIcon;
112 	delete fVolume;
113 }
114 
115 
116 // #pragma mark -
117 
118 
119 class ControlsView::VolumeTabView: public BTabView {
120 public:
121 								VolumeTabView();
122 	virtual						~VolumeTabView();
123 
124 	virtual	void				AttachedToWindow();
125 	virtual	void				MessageReceived(BMessage* message);
126 	virtual BRect				TabFrame(int32 index) const;
127 
128 			BVolume*			FindDeviceFor(dev_t device,
129 									bool invoke = false);
130 
131 private:
132 			void				_AddVolume(dev_t device);
133 			void				_RemoveVolume(dev_t device);
134 
135 			BVolumeRoster*		fVolumeRoster;
136 };
137 
138 
VolumeTabView()139 ControlsView::VolumeTabView::VolumeTabView()
140 	:
141 	BTabView("volume_tabs", B_WIDTH_FROM_LABEL)
142 {
143 	SetBorder(B_NO_BORDER);
144 }
145 
146 
~VolumeTabView()147 ControlsView::VolumeTabView::~VolumeTabView()
148 {
149 	fVolumeRoster->StopWatching();
150 	delete fVolumeRoster;
151 }
152 
153 
154 BRect
TabFrame(int32 index) const155 ControlsView::VolumeTabView::TabFrame(int32 index) const
156 {
157 	float height = BTabView::TabFrame(index).Height();
158 	float x = 0.0f;
159 	float width = 0.0f;
160 	float minStringWidth = StringWidth("Haiku");
161 	int32 countTabs = CountTabs();
162 
163 	// calculate the total width if no truncation is made at all
164 	float averageWidth = Frame().Width() / countTabs;
165 
166 	// margins are the deltas with the average widths
167 	float* margins = new float[countTabs];
168 	for (int32 i = 0; i < countTabs; i++) {
169 		float tabLabelWidth = StringWidth(TabAt(i)->Label());
170 		if (tabLabelWidth < minStringWidth)
171 			tabLabelWidth = minStringWidth;
172 
173 		float tabWidth = tabLabelWidth + 3.0f * kSmallHMargin
174 			+ ((VolumeTab*)TabAt(i))->IconWidth();
175 		margins[i] = tabWidth - averageWidth;
176 		width += tabWidth;
177 	}
178 
179 	// determine how much we should shave to show all tabs (truncating)
180 	float toShave = width - Frame().Width();
181 
182 	if (toShave > 0.0f) {
183 		// the thinest a tab can be to hold the minimum string
184 		float minimumMargin = minStringWidth + 3.0f * kSmallHMargin
185 			- averageWidth;
186 
187 		float averageToShave;
188 		float oldToShave;
189 		/*
190 			we might have to do multiple passes because of the minimum
191 			tab width we are imposing.
192 			we could also fail to totally fit all tabs.
193 			TODO: allow paging.
194 		*/
195 
196 		do {
197 			averageToShave = toShave / countTabs;
198 			oldToShave = toShave;
199 			for (int32 i = 0; i < countTabs; i++) {
200 				float iconWidth = ((VolumeTab*)TabAt(i))->IconWidth();
201 				float newMargin = max_c(margins[i] - averageToShave,
202 					minimumMargin + iconWidth);
203 				toShave -= margins[i] - newMargin;
204 				margins[i] = newMargin;
205 			}
206 		} while (toShave > 0 && fabs(oldToShave - toShave) >= 1.0f);
207 	}
208 
209 	for (int i = 0; i < index; i++)
210 		x += averageWidth + margins[i];
211 
212 	float margin = margins[index];
213 	delete[] margins;
214 
215 	return BRect(x, 0.0f, x + averageWidth + margin, height);
216 }
217 
218 
219 void
AttachedToWindow()220 ControlsView::VolumeTabView::AttachedToWindow()
221 {
222 	// Populate the menu with the persistent volumes.
223 	fVolumeRoster = new BVolumeRoster();
224 
225 	BVolume tempVolume;
226 	while (fVolumeRoster->GetNextVolume(&tempVolume) == B_OK) {
227 		if (!tempVolume.IsPersistent())
228 			continue;
229 
230 		char name[B_PATH_NAME_LENGTH];
231 		if (tempVolume.GetName(name) != B_OK)
232 			continue;
233 
234 		if (strcmp(name, "system") == 0
235 			|| strcmp(name, "config") == 0) {
236 			// Don't include virtual volumes.
237 			continue;
238 		}
239 
240 		BVolume* volume = new BVolume(tempVolume);
241 		VolumeView* volumeView = new VolumeView(name, volume);
242 		VolumeTab* volumeTab = new VolumeTab(volume);
243 		AddTab(volumeView, volumeTab);
244 	}
245 
246 	// Begin watching mount and unmount events.
247 	fVolumeRoster->StartWatching(BMessenger(this));
248 }
249 
250 
251 void
MessageReceived(BMessage * message)252 ControlsView::VolumeTabView::MessageReceived(BMessage* message)
253 {
254 	switch (message->what) {
255 		case B_NODE_MONITOR:
256 			switch (message->FindInt32("opcode")) {
257 				case B_DEVICE_MOUNTED:
258 					_AddVolume(message->FindInt32("new device"));
259 					break;
260 
261 				case B_DEVICE_UNMOUNTED:
262 					_RemoveVolume(message->FindInt32("device"));
263 					break;
264 			}
265 			break;
266 
267 		case kBtnCancel:
268 		case kBtnRescan:
269 			ViewForTab(Selection())->MessageReceived(message);
270 			break;
271 
272 		case B_SIMPLE_DATA:
273 		case B_REFS_RECEIVED:
274 		{
275 			entry_ref ref;
276 
277 			for (int i = 0; message->FindRef("refs", i, &ref) == B_OK; i++) {
278 				BEntry entry(&ref, true);
279 				BPath path;
280 				entry.GetPath(&path);
281 				dev_t device = dev_for_path(path.Path());
282 
283 				for (int j = 0; VolumeTab* item = (VolumeTab*)TabAt(j); j++) {
284 					if (item->Volume()->Device() == device) {
285 						Select(j);
286 						((VolumeView*)(item->View()))->SetPath(path);
287 						break;
288 					}
289 				}
290 			}
291 			break;
292 		}
293 
294 		default:
295 			BTabView::MessageReceived(message);
296 			break;
297 	}
298 }
299 
300 
301 BVolume*
FindDeviceFor(dev_t device,bool invoke)302 ControlsView::VolumeTabView::FindDeviceFor(dev_t device, bool invoke)
303 {
304 	BVolume* volume = NULL;
305 
306 	// Iterate through items looking for a BVolume representing this device.
307 	for (int i = 0; VolumeTab* item = (VolumeTab*)TabAt(i); i++) {
308 		if (item->Volume()->Device() == device) {
309 			volume = item->Volume();
310 			if (invoke)
311 				Select(i);
312 			break;
313 		}
314 	}
315 
316 	return volume;
317 }
318 
319 
320 void
_AddVolume(dev_t device)321 ControlsView::VolumeTabView::_AddVolume(dev_t device)
322 {
323 	// Make sure the volume is not already in the menu.
324 	for (int i = 0; VolumeTab* item = (VolumeTab*)TabAt(i); i++) {
325 		if (item->Volume()->Device() == device)
326 			return;
327 	}
328 
329 	BVolume* volume = new BVolume(device);
330 
331 	VolumeTab* item = new VolumeTab(volume);
332 	char name[B_PATH_NAME_LENGTH];
333 	volume->GetName(name);
334 
335 	AddTab(new VolumeView(name, volume), item);
336 	Invalidate();
337 }
338 
339 
340 void
_RemoveVolume(dev_t device)341 ControlsView::VolumeTabView::_RemoveVolume(dev_t device)
342 {
343 	for (int i = 0; VolumeTab* item = (VolumeTab*)TabAt(i); i++) {
344 		if (item->Volume()->Device() == device) {
345 			if (i == 0)
346 				Select(1);
347 			else
348 				Select(i - 1);
349 			RemoveTab(i);
350 			delete item;
351 			return;
352 		}
353 	}
354 }
355 
356 
357 // #pragma mark -
358 
359 
ControlsView()360 ControlsView::ControlsView()
361 	:
362 	BView(NULL, B_WILL_DRAW)
363 {
364 	SetLayout(new BGroupLayout(B_VERTICAL));
365 	fVolumeTabView = new VolumeTabView();
366 	AddChild(BLayoutBuilder::Group<>(B_VERTICAL)
367 		.Add(fVolumeTabView)
368 	);
369 }
370 
371 
372 void
MessageReceived(BMessage * msg)373 ControlsView::MessageReceived(BMessage* msg)
374 {
375 	switch (msg->what) {
376 		case B_SIMPLE_DATA:
377 		case B_REFS_RECEIVED:
378 			fVolumeTabView->MessageReceived(msg);
379 			break;
380 
381 		case kBtnCancel:
382 		case kBtnRescan:
383 			fVolumeTabView->MessageReceived(msg);
384 			break;
385 
386 		default:
387 			BView::MessageReceived(msg);
388 	}
389 }
390 
391 
~ControlsView()392 ControlsView::~ControlsView()
393 {
394 }
395 
396 
397 void
ShowInfo(const FileInfo * info)398 ControlsView::ShowInfo(const FileInfo* info)
399 {
400 	((VolumeView*)fVolumeTabView->ViewForTab(
401 		fVolumeTabView->Selection()))->ShowInfo(info);
402 }
403 
404 
405 void
EnableRescan()406 ControlsView::EnableRescan()
407 {
408 	((VolumeView*)fVolumeTabView->ViewForTab(
409 		fVolumeTabView->Selection()))->EnableRescan();
410 }
411 
412 
413 void
EnableCancel()414 ControlsView::EnableCancel()
415 {
416 	((VolumeView*)fVolumeTabView->ViewForTab(
417 		fVolumeTabView->Selection()))->EnableCancel();
418 }
419 
420 
421 BVolume*
FindDeviceFor(dev_t device,bool invoke)422 ControlsView::FindDeviceFor(dev_t device, bool invoke)
423 {
424 	return fVolumeTabView->FindDeviceFor(device, invoke);
425 }
426