vfs: check userland buffers before reading them.
[haiku.git] / src / apps / diskusage / ControlsView.cpp
blob67c1c84d00e2bd8c2a4c48901451895f3d4cdc5f
1 /*
2 * Copyright (c) 2008 Stephan Aßmus <superstippi@gmx.de>. All rights reserved.
3 * Distributed under the terms of the MIT/X11 license.
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 */
11 #include "ControlsView.h"
13 #include <string.h>
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>
28 #include <LayoutBuilder.h>
30 #include "DiskUsage.h"
31 #include "VolumeView.h"
34 class VolumeTab: public BTab {
35 public:
36 VolumeTab(BVolume* volume);
37 virtual ~VolumeTab();
39 BVolume* Volume() const
40 { return fVolume; }
41 float IconWidth() const;
43 virtual void DrawLabel(BView* owner, BRect frame);
44 virtual void DrawFocusMark(BView* owner, BRect frame);
46 private:
47 BBitmap* fIcon;
48 BVolume* fVolume;
52 VolumeTab::VolumeTab(BVolume* volume)
54 BTab(),
55 fIcon(new BBitmap(BRect(0, 0, 15, 15), B_RGBA32)),
56 fVolume(volume)
58 if (fVolume->GetIcon(fIcon, B_MINI_ICON) < B_OK) {
59 delete fIcon;
60 fIcon = NULL;
65 float
66 VolumeTab::IconWidth() const
68 if (fIcon != NULL)
69 // add a small margin
70 return fIcon->Bounds().Width() + kSmallHMargin;
71 else
72 return 0.0f;
76 void
77 VolumeTab::DrawLabel(BView* owner, BRect frame)
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);
85 font_height fh;
86 owner->GetFontHeight(&fh);
88 BString label = Label();
90 owner->TruncateString(&label, B_TRUNCATE_END,
91 frame.Width() - IconWidth() - kSmallHMargin);
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));
101 void
102 VolumeTab::DrawFocusMark(BView* owner, BRect frame)
104 frame.left += IconWidth();
105 BTab::DrawFocusMark(owner, frame);
109 VolumeTab::~VolumeTab()
111 delete fIcon;
112 delete fVolume;
116 // #pragma mark -
119 class ControlsView::VolumeTabView: public BTabView {
120 public:
121 VolumeTabView();
122 virtual ~VolumeTabView();
124 virtual void AttachedToWindow();
125 virtual void MessageReceived(BMessage* message);
126 virtual BRect TabFrame(int32 index) const;
128 BVolume* FindDeviceFor(dev_t device,
129 bool invoke = false);
131 private:
132 void _AddVolume(dev_t device);
133 void _RemoveVolume(dev_t device);
135 BVolumeRoster* fVolumeRoster;
139 ControlsView::VolumeTabView::VolumeTabView()
141 BTabView("volume_tabs", B_WIDTH_FROM_LABEL)
143 SetBorder(B_NO_BORDER);
147 ControlsView::VolumeTabView::~VolumeTabView()
149 fVolumeRoster->StopWatching();
150 delete fVolumeRoster;
154 BRect
155 ControlsView::VolumeTabView::TabFrame(int32 index) const
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();
163 // calculate the total width if no truncation is made at all
164 float averageWidth = Frame().Width() / countTabs;
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;
173 float tabWidth = tabLabelWidth + 3.0f * kSmallHMargin
174 + ((VolumeTab*)TabAt(i))->IconWidth();
175 margins[i] = tabWidth - averageWidth;
176 width += tabWidth;
179 // determine how much we should shave to show all tabs (truncating)
180 float toShave = width - Frame().Width();
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;
187 float averageToShave;
188 float oldToShave;
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.
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;
206 } while (toShave > 0 && oldToShave != toShave);
209 for (int i = 0; i < index; i++)
210 x += averageWidth + margins[i];
212 float margin = margins[index];
213 delete[] margins;
215 return BRect(x, 0.0f, x + averageWidth + margin, height);
219 void
220 ControlsView::VolumeTabView::AttachedToWindow()
222 // Populate the menu with the persistent volumes.
223 fVolumeRoster = new BVolumeRoster();
225 BVolume tempVolume;
226 while (fVolumeRoster->GetNextVolume(&tempVolume) == B_OK) {
227 if (!tempVolume.IsPersistent())
228 continue;
230 char name[B_PATH_NAME_LENGTH];
231 if (tempVolume.GetName(name) != B_OK)
232 continue;
234 if (strcmp(name, "system") == 0
235 || strcmp(name, "config") == 0) {
236 // Don't include virtual volumes.
237 continue;
240 BVolume* volume = new BVolume(tempVolume);
241 VolumeView* volumeView = new VolumeView(name, volume);
242 VolumeTab* volumeTab = new VolumeTab(volume);
243 AddTab(volumeView, volumeTab);
246 // Begin watching mount and unmount events.
247 fVolumeRoster->StartWatching(BMessenger(this));
251 void
252 ControlsView::VolumeTabView::MessageReceived(BMessage* message)
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;
261 case B_DEVICE_UNMOUNTED:
262 _RemoveVolume(message->FindInt32("device"));
263 break;
265 break;
267 case kBtnCancel:
268 case kBtnRescan:
269 ViewForTab(Selection())->MessageReceived(message);
270 break;
272 case B_SIMPLE_DATA:
273 case B_REFS_RECEIVED:
275 entry_ref ref;
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());
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;
291 break;
294 default:
295 BTabView::MessageReceived(message);
296 break;
301 BVolume*
302 ControlsView::VolumeTabView::FindDeviceFor(dev_t device, bool invoke)
304 BVolume* volume = NULL;
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;
316 return volume;
320 void
321 ControlsView::VolumeTabView::_AddVolume(dev_t device)
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;
329 BVolume* volume = new BVolume(device);
331 VolumeTab* item = new VolumeTab(volume);
332 char name[B_PATH_NAME_LENGTH];
333 volume->GetName(name);
335 AddTab(new VolumeView(name, volume), item);
336 Invalidate();
340 void
341 ControlsView::VolumeTabView::_RemoveVolume(dev_t device)
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;
357 // #pragma mark -
360 ControlsView::ControlsView()
362 BView(NULL, B_WILL_DRAW)
364 SetLayout(new BGroupLayout(B_VERTICAL));
365 fVolumeTabView = new VolumeTabView();
366 AddChild(BLayoutBuilder::Group<>(B_VERTICAL)
367 .Add(fVolumeTabView)
372 void
373 ControlsView::MessageReceived(BMessage* msg)
375 switch (msg->what) {
376 case B_SIMPLE_DATA:
377 case B_REFS_RECEIVED:
378 fVolumeTabView->MessageReceived(msg);
379 break;
381 case kBtnCancel:
382 case kBtnRescan:
383 fVolumeTabView->MessageReceived(msg);
384 break;
386 default:
387 BView::MessageReceived(msg);
392 ControlsView::~ControlsView()
397 void
398 ControlsView::ShowInfo(const FileInfo* info)
400 ((VolumeView*)fVolumeTabView->ViewForTab(
401 fVolumeTabView->Selection()))->ShowInfo(info);
405 void
406 ControlsView::EnableRescan()
408 ((VolumeView*)fVolumeTabView->ViewForTab(
409 fVolumeTabView->Selection()))->EnableRescan();
413 void
414 ControlsView::EnableCancel()
416 ((VolumeView*)fVolumeTabView->ViewForTab(
417 fVolumeTabView->Selection()))->EnableCancel();
421 BVolume*
422 ControlsView::FindDeviceFor(dev_t device, bool invoke)
424 return fVolumeTabView->FindDeviceFor(device, invoke);