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