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