1 /*
2 * Copyright (c) 2007-2010, Haiku, Inc.
3 * Distributed under the terms of the MIT license.
4 *
5 * Author:
6 * Łukasz 'Sil2100' Zemczak <sil2100@vexillium.org>
7 */
8
9
10 #include "UninstallView.h"
11
12 #include <stdio.h>
13 #include <string.h>
14
15 #include <Alert.h>
16 #include <Box.h>
17 #include <Button.h>
18 #include <Catalog.h>
19 #include <ControlLook.h>
20 #include <Directory.h>
21 #include <Entry.h>
22 #include <File.h>
23 #include <FilePanel.h>
24 #include <FindDirectory.h>
25 #include <LayoutBuilder.h>
26 #include <ListView.h>
27 #include <Locale.h>
28 #include <NodeMonitor.h>
29 #include <ScrollView.h>
30 #include <SeparatorView.h>
31 #include <String.h>
32 #include <StringView.h>
33 #include <SpaceLayoutItem.h>
34 #include <TextView.h>
35
36 #include "main.h"
37
38
39 #undef B_TRANSLATION_CONTEXT
40 #define B_TRANSLATION_CONTEXT "UninstallView"
41
42
43 enum {
44 P_MSG_INSTALL = 'umin',
45 P_MSG_REMOVE = 'umrm',
46 P_MSG_SELECT
47 };
48
49
50 // TODO list:
51 // - B_ENTRY_MOVED
52 // - Right now the installed package info naming convention is the same
53 // as at SoftwareValet. Maybe there would be a better one?
54 // - Add a status window (reuse the one from PackageInstall)
55
56
57 class UninstallView::InfoItem : public BStringItem {
58 public:
InfoItem(const BString & name,const BString & version,const char * filename,const node_ref & ref)59 InfoItem(const BString& name, const BString& version,
60 const char* filename, const node_ref& ref)
61 :
62 BStringItem(name.String()),
63 fName(name),
64 fVersion(version),
65 fNodeRef(ref)
66 {
67 if (fName.Length() == 0)
68 SetText(filename);
69 }
70
GetName()71 const char* GetName() { return fName.String(); }
GetVersion()72 const char* GetVersion() { return fVersion.String(); };
GetNodeRef()73 node_ref GetNodeRef() { return fNodeRef; };
74
75 private:
76 BString fName;
77 BString fVersion;
78 node_ref fNodeRef;
79 };
80
81
82
83
UninstallView()84 UninstallView::UninstallView()
85 :
86 BGroupView(B_VERTICAL),
87 fOpenPanel(new BFilePanel(B_OPEN_PANEL))
88 {
89 fNoPackageSelectedString = B_TRANSLATE("No package selected.");
90 _InitView();
91 }
92
93
~UninstallView()94 UninstallView::~UninstallView()
95 {
96 // Stop all node watching
97 stop_watching(this);
98 }
99
100
101 void
AttachedToWindow()102 UninstallView::AttachedToWindow()
103 {
104 fAppList->SetTarget(this);
105 fInstallButton->SetTarget(this);
106 fRemoveButton->SetTarget(this);
107
108 _ReloadAppList();
109
110 // We loaded the list, but now let's set up a node watcher for the packages
111 // directory, so that we can update the list of installed packages in real
112 // time
113 _CachePathToPackages();
114 node_ref ref;
115 fWatcherRunning = false;
116 BDirectory dir(fToPackages.Path());
117 if (dir.InitCheck() != B_OK) {
118 // The packages/ directory obviously does not exist.
119 // Since this is the case, we need to watch for it to appear first
120
121 BPath path;
122 fToPackages.GetParent(&path);
123 if (dir.SetTo(path.Path()) != B_OK)
124 return;
125 } else
126 fWatcherRunning = true;
127
128 dir.GetNodeRef(&ref);
129
130 if (watch_node(&ref, B_WATCH_DIRECTORY, this) != B_OK) {
131 fWatcherRunning = false;
132 return;
133 }
134 }
135
136
137 void
MessageReceived(BMessage * msg)138 UninstallView::MessageReceived(BMessage* msg)
139 {
140 switch (msg->what) {
141 case B_NODE_MONITOR:
142 {
143 int32 opcode;
144 if (msg->FindInt32("opcode", &opcode) != B_OK)
145 break;
146
147 fprintf(stderr, "Got an opcoded node monitor message\n");
148 if (opcode == B_ENTRY_CREATED) {
149 fprintf(stderr, "Created?...\n");
150 BString filename, name, version;
151 node_ref ref;
152 if (msg->FindString("name", &filename) != B_OK
153 || msg->FindInt32("device", &ref.device) != B_OK
154 || msg->FindInt64("node", &ref.node) != B_OK)
155 break;
156
157 // TODO: This obviously is a hack
158 // The node watcher informs the view a bit to early, and
159 // because of this the data of the node is not ready at this
160 // moment. For this reason, we must give the filesystem some
161 // time before continuing.
162 usleep(10000);
163
164 if (fWatcherRunning) {
165 _AddFile(filename.String(), ref);
166 } else {
167 // This most likely means we were waiting for
168 // the packages/ dir to appear
169 if (filename == "packages") {
170 if (watch_node(&ref, B_WATCH_DIRECTORY, this) == B_OK)
171 fWatcherRunning = true;
172 }
173 }
174 } else if (opcode == B_ENTRY_REMOVED) {
175 node_ref ref;
176 if (msg->FindInt32("device", &ref.device) != B_OK
177 || msg->FindInt64("node", &ref.node) != B_OK)
178 break;
179
180 int32 i, count = fAppList->CountItems();
181 InfoItem* iter;
182 for (i = 0; i < count; i++) {
183 iter = static_cast<InfoItem *>(fAppList->ItemAt(i));
184 if (iter->GetNodeRef() == ref) {
185 if (i == fAppList->CurrentSelection())
186 fDescription->SetText(fNoPackageSelectedString);
187 fAppList->RemoveItem(i);
188 delete iter;
189 }
190 }
191 } else if (opcode == B_ENTRY_MOVED) {
192 ino_t from, to;
193 if (msg->FindInt64("from directory", &from) != B_OK
194 || msg->FindInt64("to directory", &to) != B_OK)
195 break;
196
197 BDirectory packagesDir(fToPackages.Path());
198 node_ref ref;
199 packagesDir.GetNodeRef(&ref);
200
201 if (ref.node == to) {
202 // Package added
203 // TODO
204 } else if (ref.node == from) {
205 // Package removed
206 // TODO
207 }
208 }
209 break;
210 }
211 case P_MSG_SELECT:
212 {
213 fRemoveButton->SetEnabled(false);
214 fDescription->SetText(fNoPackageSelectedString);
215
216 int32 index = fAppList->CurrentSelection();
217 if (index < 0)
218 break;
219
220 fprintf(stderr, "Another debug message...\n");
221
222 InfoItem* item = dynamic_cast<InfoItem*>(fAppList->ItemAt(index));
223 if (!item)
224 break;
225
226 fprintf(stderr, "Uh: %s and %s\n", item->GetName(),
227 item->GetVersion());
228
229 if (fCurrentSelection.SetTo(item->GetName(),
230 item->GetVersion()) != B_OK)
231 break;
232
233 fRemoveButton->SetEnabled(true);
234 fDescription->SetText(fCurrentSelection.Description());
235 break;
236 }
237 case P_MSG_INSTALL:
238 {
239 fOpenPanel->Show();
240 break;
241 }
242 case P_MSG_REMOVE:
243 {
244 if (fCurrentSelection.InitCheck() != B_OK)
245 break;
246
247 int32 index = fAppList->CurrentSelection();
248 if (index < 0)
249 break;
250
251 BAlert* notify;
252 if (fCurrentSelection.Uninstall() == B_OK) {
253 BListItem* item = fAppList->RemoveItem(index);
254 delete item;
255
256 fDescription->SetText(fNoPackageSelectedString);
257
258 notify = new BAlert("removal_success",
259 B_TRANSLATE("The package you selected has been "
260 "successfully removed from your system."),
261 B_TRANSLATE("OK"));
262 } else {
263 notify = new BAlert("removal_failed",
264 B_TRANSLATE(
265 "The selected package was not removed from your system. "
266 "The given installed package information file might have "
267 "been corrupted."), B_TRANSLATE("OK"), NULL,
268 NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
269 }
270 notify->SetFlags(notify->Flags() | B_CLOSE_ON_ESCAPE);
271 notify->Go();
272 break;
273 }
274 default:
275 BView::MessageReceived(msg);
276 break;
277 }
278 }
279
280
281 void
RefsReceived(BMessage * message)282 UninstallView::RefsReceived(BMessage* message)
283 {
284 static_cast<PackageInstaller*>(be_app)->RefsReceived(message);
285 }
286
287
288 void
_InitView()289 UninstallView::_InitView()
290 {
291 SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
292
293 fAppList = new BListView("pkg_list", B_SINGLE_SELECTION_LIST);
294 fAppList->SetSelectionMessage(new BMessage(P_MSG_SELECT));
295 BScrollView* scrollView = new BScrollView("list_scroll", fAppList,
296 0, false, true, B_NO_BORDER);
297
298 BStringView* descriptionLabel = new BStringView("desc_label",
299 B_TRANSLATE("Package description"));
300 descriptionLabel->SetFont(be_bold_font);
301
302 fDescription = new BTextView("description", B_WILL_DRAW);
303 fDescription->MakeSelectable(false);
304 fDescription->MakeEditable(false);
305 fDescription->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
306 fDescription->SetText(fNoPackageSelectedString);
307
308 fInstallButton = new BButton("install", B_TRANSLATE("Install" B_UTF8_ELLIPSIS),
309 new BMessage(P_MSG_INSTALL));
310 fRemoveButton = new BButton("removal", B_TRANSLATE("Remove"),
311 new BMessage(P_MSG_REMOVE));
312 fRemoveButton->SetEnabled(false);
313
314 const float spacing = be_control_look->DefaultItemSpacing();
315
316 BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
317 .Add(scrollView, 10)
318 .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER))
319 .AddGroup(B_VERTICAL)
320 .SetInsets(spacing)
321 .AddGroup(B_HORIZONTAL, 0)
322 .Add(descriptionLabel)
323 .AddGlue()
324 .End()
325 .AddGroup(B_HORIZONTAL, 0)
326 .Add(BSpaceLayoutItem::CreateHorizontalStrut(10))
327 .Add(fDescription)
328 .End()
329 .End()
330 .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER))
331 .AddGroup(B_HORIZONTAL)
332 .SetInsets(spacing)
333 .AddGlue()
334 .Add(fInstallButton)
335 .Add(fRemoveButton)
336 .End()
337 .End();
338 }
339
340
341 status_t
_ReloadAppList()342 UninstallView::_ReloadAppList()
343 {
344 _ClearAppList();
345
346 if (fToPackages.InitCheck() != B_OK)
347 _CachePathToPackages();
348
349 BDirectory dir(fToPackages.Path());
350 status_t ret = dir.InitCheck();
351 if (ret != B_OK)
352 return ret;
353
354 BEntry iter;
355 while (dir.GetNextEntry(&iter) == B_OK) {
356 char filename[B_FILE_NAME_LENGTH];
357 if (iter.GetName(filename) != B_OK)
358 continue;
359
360 node_ref ref;
361 if (iter.GetNodeRef(&ref) != B_OK)
362 continue;
363
364 BString filenameString(filename);
365 if (!filenameString.IEndsWith(".pdb")) {
366 printf("Ignoring non-package '%s'\n", filename);
367 continue;
368 }
369
370 printf("Found package '%s'\n", filename);
371 _AddFile(filename, ref);
372 }
373
374 if (ret != B_ENTRY_NOT_FOUND)
375 return ret;
376
377 return B_OK;
378 }
379
380
381 void
_ClearAppList()382 UninstallView::_ClearAppList()
383 {
384 while (BListItem* item = fAppList->RemoveItem((int32)0))
385 delete item;
386 }
387
388
389 void
_AddFile(const char * filename,const node_ref & ref)390 UninstallView::_AddFile(const char* filename, const node_ref& ref)
391 {
392 BString name;
393 status_t ret = info_get_package_name(filename, name);
394 if (ret != B_OK || name.Length() == 0)
395 fprintf(stderr, "Error extracting package name: %s\n", strerror(ret));
396 BString version;
397 ret = info_get_package_version(filename, version);
398 if (ret != B_OK || version.Length() == 0) {
399 fprintf(stderr, "Error extracting package version: %s\n",
400 strerror(ret));
401 }
402 fAppList->AddItem(new InfoItem(name, version, filename, ref));
403 }
404
405
406 void
_CachePathToPackages()407 UninstallView::_CachePathToPackages()
408 {
409 if (find_directory(B_USER_CONFIG_DIRECTORY, &fToPackages) != B_OK)
410 return;
411 if (fToPackages.Append(kPackagesDir) != B_OK)
412 return;
413 }
414
415