vfs: check userland buffers before reading them.
[haiku.git] / src / apps / drivesetup / DiskView.cpp
blobe560e5beb3444ec90f0473e446639ce08b50d8b7
1 /*
2 * Copyright 2007-2010 Stephan Aßmus <superstippi@gmx.de>.
3 * All rights reserved. Distributed under the terms of the MIT license.
4 */
6 #include "DiskView.h"
8 #include <stdio.h>
10 #include <DiskDeviceVisitor.h>
11 #include <Catalog.h>
12 #include <GroupLayout.h>
13 #include <HashMap.h>
14 #include <LayoutItem.h>
15 #include <Locale.h>
16 #include <PartitioningInfo.h>
17 #include <String.h>
19 #include "MainWindow.h"
22 #undef B_TRANSLATION_CONTEXT
23 #define B_TRANSLATION_CONTEXT "DiskView"
25 using BPrivate::HashMap;
26 using BPrivate::HashKey32;
28 static const pattern kStripes = { { 0xc7, 0x8f, 0x1f, 0x3e,
29 0x7c, 0xf8, 0xf1, 0xe3 } };
31 static const float kLayoutInset = 6;
34 class PartitionView : public BView {
35 public:
36 PartitionView(const char* name, float weight, off_t offset,
37 int32 level, partition_id id)
39 BView(name, B_WILL_DRAW | B_SUPPORTS_LAYOUT | B_FULL_UPDATE_ON_RESIZE),
40 fID(id),
41 fWeight(weight),
42 fOffset(offset),
43 fLevel(level),
44 fSelected(false),
45 fMouseOver(false),
46 fGroupLayout(new BGroupLayout(B_HORIZONTAL, kLayoutInset))
48 SetLayout(fGroupLayout);
50 SetViewColor(B_TRANSPARENT_COLOR);
51 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
52 base = tint_color(base, B_LIGHTEN_2_TINT);
53 base = tint_color(base, 1 + 0.13 * (level - 1));
54 SetLowColor(base);
55 SetHighColor(tint_color(base, B_DARKEN_1_TINT));
57 BFont font;
58 GetFont(&font);
59 font.SetSize(ceilf(font.Size() * 0.85));
60 font.SetRotation(90.0);
61 SetFont(&font);
63 fGroupLayout->SetInsets(kLayoutInset, kLayoutInset + font.Size(),
64 kLayoutInset, kLayoutInset);
66 SetExplicitMinSize(BSize(font.Size() + 6, 30));
69 virtual void MouseDown(BPoint where)
71 BMessage message(MSG_SELECTED_PARTITION_ID);
72 message.AddInt32("partition_id", fID);
73 Window()->PostMessage(&message);
76 virtual void MouseMoved(BPoint where, uint32 transit, const BMessage*)
78 uint32 buttons;
79 if (Window()->CurrentMessage()->FindInt32("buttons",
80 (int32*)&buttons) < B_OK)
81 buttons = 0;
83 _SetMouseOver(buttons == 0
84 && (transit == B_ENTERED_VIEW || transit == B_INSIDE_VIEW));
87 virtual void Draw(BRect updateRect)
89 if (fMouseOver) {
90 float tint = (B_NO_TINT + B_LIGHTEN_1_TINT) / 2.0;
91 SetHighColor(tint_color(HighColor(), tint));
92 SetLowColor(tint_color(LowColor(), tint));
95 BRect b(Bounds());
96 if (fSelected) {
97 SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
98 StrokeRect(b, B_SOLID_HIGH);
99 b.InsetBy(1, 1);
100 StrokeRect(b, B_SOLID_HIGH);
101 b.InsetBy(1, 1);
102 } else if (fLevel > 0) {
103 StrokeRect(b, B_SOLID_HIGH);
104 b.InsetBy(1, 1);
107 FillRect(b, B_SOLID_LOW);
109 // prevent the text from moving when border width changes
110 if (!fSelected)
111 b.InsetBy(1, 1);
113 float width;
114 BFont font;
115 GetFont(&font);
117 font_height fh;
118 font.GetHeight(&fh);
120 // draw the partition label, but only if we have no child partition
121 // views
122 BPoint textOffset;
123 if (CountChildren() > 0) {
124 font.SetRotation(0.0);
125 SetFont(&font);
126 width = b.Width();
127 textOffset = b.LeftTop();
128 textOffset.x += 3;
129 textOffset.y += ceilf(fh.ascent);
130 } else {
131 width = b.Height();
132 textOffset = b.LeftBottom();
133 textOffset.x += ceilf(fh.ascent);
136 BString name(Name());
137 font.TruncateString(&name, B_TRUNCATE_END, width);
139 SetHighColor(tint_color(LowColor(), B_DARKEN_4_TINT));
140 DrawString(name.String(), textOffset);
143 float Weight() const
145 return fWeight;
148 off_t Offset() const
150 return fOffset;
153 int32 Level() const
155 return fLevel;
158 BGroupLayout* GroupLayout() const
160 return fGroupLayout;
163 void SetSelected(bool selected)
165 if (fSelected == selected)
166 return;
168 fSelected = selected;
169 Invalidate();
172 private:
173 void _SetMouseOver(bool mouseOver)
175 if (fMouseOver == mouseOver)
176 return;
177 fMouseOver = mouseOver;
178 Invalidate();
181 private:
182 partition_id fID;
183 float fWeight;
184 off_t fOffset;
185 int32 fLevel;
186 bool fSelected;
187 bool fMouseOver;
188 BGroupLayout* fGroupLayout;
192 class DiskView::PartitionLayout : public BDiskDeviceVisitor {
193 public:
194 PartitionLayout(BView* view, SpaceIDMap& spaceIDMap)
196 fView(view),
197 fViewMap(),
198 fSelectedPartition(-1),
199 fSpaceIDMap(spaceIDMap)
203 virtual bool Visit(BDiskDevice* device)
205 const char* name;
206 if (device->Name() != NULL && device->Name()[0] != '\0')
207 name = device->Name();
208 else
209 name = B_TRANSLATE("Device");
211 PartitionView* view = new PartitionView(name, 1.0,
212 device->Offset(), 0, device->ID());
213 fViewMap.Put(device->ID(), view);
214 fView->GetLayout()->AddView(view);
215 _AddSpaces(device, view);
216 return false;
219 virtual bool Visit(BPartition* partition, int32 level)
221 if (!partition->Parent()
222 || !fViewMap.ContainsKey(partition->Parent()->ID()))
223 return false;
225 // calculate size factor within parent frame
226 off_t offset = partition->Offset();
227 // off_t parentOffset = partition->Parent()->Offset();
228 off_t size = partition->Size();
229 off_t parentSize = partition->Parent()->Size();
230 double scale = (double)size / parentSize;
232 BString name = partition->ContentName();
233 if (name.Length() == 0) {
234 if (partition->CountChildren() > 0)
235 name << partition->Type();
236 else {
237 char buffer[64];
238 snprintf(buffer, 64, B_TRANSLATE("Partition %ld"),
239 partition->ID());
240 name << buffer;
243 partition_id id = partition->ID();
244 PartitionView* view = new PartitionView(name.String(), scale, offset,
245 level, id);
246 view->SetSelected(id == fSelectedPartition);
247 PartitionView* parent = fViewMap.Get(partition->Parent()->ID());
248 BGroupLayout* layout = parent->GroupLayout();
249 layout->AddView(_FindInsertIndex(view, layout), view, scale);
251 fViewMap.Put(partition->ID(), view);
252 _AddSpaces(partition, view);
254 return false;
257 void SetSelectedPartition(partition_id id)
259 if (fSelectedPartition == id)
260 return;
262 if (fViewMap.ContainsKey(fSelectedPartition)) {
263 PartitionView* view = fViewMap.Get(fSelectedPartition);
264 view->SetSelected(false);
267 fSelectedPartition = id;
269 if (fViewMap.ContainsKey(fSelectedPartition)) {
270 PartitionView* view = fViewMap.Get(fSelectedPartition);
271 view->SetSelected(true);
275 void Unset()
277 fViewMap.Clear();
280 private:
281 void _AddSpaces(BPartition* partition, PartitionView* parentView)
283 // add any available space on the partition
284 BPartitioningInfo info;
285 if (partition->GetPartitioningInfo(&info) >= B_OK) {
286 off_t parentSize = partition->Size();
287 off_t offset;
288 off_t size;
289 for (int32 i = 0;
290 info.GetPartitionableSpaceAt(i, &offset, &size) >= B_OK;
291 i++) {
292 // TODO: remove again once Disk Device API is fixed
293 if (!is_valid_partitionable_space(size))
294 continue;
296 double scale = (double)size / parentSize;
297 partition_id id
298 = fSpaceIDMap.SpaceIDFor(partition->ID(), offset);
299 PartitionView* view = new PartitionView(B_TRANSLATE("<empty>"),
300 scale, offset, parentView->Level() + 1, id);
302 fViewMap.Put(id, view);
303 BGroupLayout* layout = parentView->GroupLayout();
304 layout->AddView(_FindInsertIndex(view, layout), view, scale);
308 int32 _FindInsertIndex(PartitionView* view, BGroupLayout* layout) const
310 int32 insertIndex = 0;
311 int32 count = layout->CountItems();
312 for (int32 i = 0; i < count; i++) {
313 BLayoutItem* item = layout->ItemAt(i);
314 if (!item)
315 break;
316 PartitionView* sibling
317 = dynamic_cast<PartitionView*>(item->View());
318 if (sibling && sibling->Offset() > view->Offset())
319 break;
320 insertIndex++;
322 return insertIndex;
325 typedef HashKey32<partition_id> PartitionKey;
326 typedef HashMap<PartitionKey, PartitionView* > PartitionViewMap;
328 BView* fView;
329 PartitionViewMap fViewMap;
330 partition_id fSelectedPartition;
331 SpaceIDMap& fSpaceIDMap;
335 // #pragma mark -
338 DiskView::DiskView(const BRect& frame, uint32 resizeMode,
339 SpaceIDMap& spaceIDMap)
341 Inherited(frame, "diskview", resizeMode,
342 B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
343 fDiskCount(0),
344 fDisk(NULL),
345 fSpaceIDMap(spaceIDMap),
346 fPartitionLayout(new PartitionLayout(this, fSpaceIDMap))
348 BGroupLayout* layout = new BGroupLayout(B_HORIZONTAL, kLayoutInset);
349 SetLayout(layout);
351 SetViewColor(B_TRANSPARENT_COLOR);
352 SetHighUIColor(B_PANEL_BACKGROUND_COLOR, B_DARKEN_2_TINT);
353 SetLowUIColor(B_PANEL_BACKGROUND_COLOR, 1.221f);
355 #ifdef HAIKU_TARGET_PLATFORM_LIBBE_TEST
356 PartitionView* view;
357 float scale = 1.0;
358 view = new PartitionView("Disk", scale, 0, 0, -1);
359 layout->AddView(view, scale);
361 layout = view->GroupLayout();
363 scale = 0.3;
364 view = new PartitionView("Primary", scale, 1, 50, -1);
365 layout->AddView(view, scale);
366 scale = 0.7;
367 view = new PartitionView("Extended", scale, 1, 100, -1);
368 layout->AddView(view, scale);
370 layout = view->GroupLayout();
372 scale = 0.2;
373 view = new PartitionView("Logical", scale, 2, 200, -1);
374 layout->AddView(view, scale);
376 scale = 0.5;
377 view = new PartitionView("Logical", scale, 2, 250, -1);
378 layout->AddView(view, scale);
380 scale = 0.005;
381 view = new PartitionView("Logical", scale, 2, 290, -1);
382 layout->AddView(view, scale);
384 scale = 0.295;
385 view = new PartitionView("Logical", scale, 2, 420, -1);
386 layout->AddView(view, scale);
387 #endif
391 DiskView::~DiskView()
393 SetDisk(NULL, -1);
394 delete fPartitionLayout;
398 void
399 DiskView::Draw(BRect updateRect)
401 BRect bounds(Bounds());
403 if (fDisk)
404 return;
406 FillRect(bounds, kStripes);
408 const char* helpfulMessage;
409 if (fDiskCount == 0)
410 helpfulMessage = B_TRANSLATE("No disk devices have been recognized.");
411 else
412 helpfulMessage =
413 B_TRANSLATE("Select a partition from the list below.");
415 float width = StringWidth(helpfulMessage);
416 font_height fh;
417 GetFontHeight(&fh);
418 BRect messageBounds(bounds);
419 messageBounds.InsetBy((bounds.Width() - width - fh.ascent * 2) / 2.0,
420 (bounds.Height() - (fh.ascent + fh.descent) * 2) / 2.0);
422 FillRoundRect(messageBounds, 4, 4, B_SOLID_LOW);
423 rgb_color color = LowColor();
424 if (color.Brightness() > 100)
425 color = tint_color(color, B_DARKEN_4_TINT);
426 else
427 color = tint_color(color, B_LIGHTEN_2_TINT);
429 SetHighColor(color);
430 BPoint textOffset;
431 textOffset.x = messageBounds.left + fh.ascent;
432 textOffset.y = (messageBounds.top + messageBounds.bottom
433 - fh.ascent - fh.descent) / 2 + fh.ascent;
434 DrawString(helpfulMessage, textOffset);
438 void
439 DiskView::SetDiskCount(int32 count)
441 fDiskCount = count;
445 void
446 DiskView::SetDisk(BDiskDevice* disk, partition_id selectedPartition)
448 if (fDisk != disk) {
449 fDisk = disk;
450 ForceUpdate();
453 fPartitionLayout->SetSelectedPartition(selectedPartition);
457 void
458 DiskView::ForceUpdate()
460 while (BView* view = ChildAt(0)) {
461 view->RemoveSelf();
462 delete view;
465 fPartitionLayout->Unset();
467 if (fDisk) {
468 // we need to prepare the disk for modifications, otherwise
469 // we cannot get information about available spaces on the
470 // device or any of its child partitions
471 // TODO: cancelling modifications here is of course undesired
472 // once we hold off the real modifications until an explicit
473 // command to write them to disk...
474 bool prepared = fDisk->PrepareModifications() == B_OK;
475 fDisk->VisitEachDescendant(fPartitionLayout);
476 if (prepared)
477 fDisk->CancelModifications();
480 Invalidate();