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