btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / apps / webpositive / CookieWindow.cpp
bloba8b7ec228fae746f54a52d595c37debbfc9692a5
1 /*
2 * Copyright 2015 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Adrien Destugues
7 */
10 #include "CookieWindow.h"
12 #include <Button.h>
13 #include <Catalog.h>
14 #include <ColumnListView.h>
15 #include <ColumnTypes.h>
16 #include <GroupLayoutBuilder.h>
17 #include <NetworkCookieJar.h>
18 #include <OutlineListView.h>
19 #include <ScrollView.h>
20 #include <StringView.h>
23 #undef B_TRANSLATION_CONTEXT
24 #define B_TRANSLATION_CONTEXT "Cookie Manager"
26 enum {
27 COOKIE_IMPORT = 'cimp',
28 COOKIE_EXPORT = 'cexp',
29 COOKIE_DELETE = 'cdel',
30 COOKIE_REFRESH = 'rfsh',
32 DOMAIN_SELECTED = 'dmsl'
36 class CookieDateColumn: public BDateColumn
38 public:
39 CookieDateColumn(const char* title, float width)
41 BDateColumn(title, width, width / 2, width * 2)
45 void DrawField(BField* field, BRect rect, BView* parent) {
46 BDateField* dateField = (BDateField*)field;
47 if (dateField->UnixTime() == -1) {
48 DrawString(B_TRANSLATE("Session cookie"), parent, rect);
49 } else {
50 BDateColumn::DrawField(field, rect, parent);
56 class CookieRow: public BRow
58 public:
59 CookieRow(BColumnListView* list, const BNetworkCookie& cookie)
61 BRow(),
62 fCookie(cookie)
64 list->AddRow(this);
65 SetField(new BStringField(cookie.Name().String()), 0);
66 SetField(new BStringField(cookie.Path().String()), 1);
67 time_t expiration = cookie.ExpirationDate();
68 SetField(new BDateField(&expiration), 2);
69 SetField(new BStringField(cookie.Value().String()), 3);
71 BString flags;
72 if (cookie.Secure())
73 flags = "https ";
74 if (cookie.HttpOnly())
75 flags = "http ";
77 if (cookie.IsHostOnly())
78 flags += "hostOnly";
79 SetField(new BStringField(flags.String()), 4);
82 BNetworkCookie& Cookie() {
83 return fCookie;
86 private:
87 BNetworkCookie fCookie;
91 class DomainItem: public BStringItem
93 public:
94 DomainItem(BString text, bool empty)
96 BStringItem(text),
97 fEmpty(empty)
101 public:
102 bool fEmpty;
106 CookieWindow::CookieWindow(BRect frame, BNetworkCookieJar& jar)
108 BWindow(frame, B_TRANSLATE("Cookie manager"), B_TITLED_WINDOW,
109 B_NORMAL_WINDOW_FEEL,
110 B_AUTO_UPDATE_SIZE_LIMITS | B_ASYNCHRONOUS_CONTROLS | B_NOT_ZOOMABLE),
111 fCookieJar(jar)
113 BGroupLayout* root = new BGroupLayout(B_HORIZONTAL, 0.0);
114 SetLayout(root);
116 fDomains = new BOutlineListView("domain list");
117 root->AddView(new BScrollView("scroll", fDomains, 0, false, true), 1);
119 fHeaderView = new BStringView("label",
120 B_TRANSLATE("The cookie jar is empty!"));
121 fCookies = new BColumnListView("cookie list", B_WILL_DRAW, B_FANCY_BORDER,
122 false);
124 int em = fCookies->StringWidth("M");
125 int flagsLength = fCookies->StringWidth("Mhttps hostOnly" B_UTF8_ELLIPSIS);
127 fCookies->AddColumn(new BStringColumn(B_TRANSLATE("Name"),
128 20 * em, 10 * em, 50 * em, 0), 0);
129 fCookies->AddColumn(new BStringColumn(B_TRANSLATE("Path"),
130 10 * em, 10 * em, 50 * em, 0), 1);
131 fCookies->AddColumn(new CookieDateColumn(B_TRANSLATE("Expiration"),
132 fCookies->StringWidth("88/88/8888 88:88:88 AM")), 2);
133 fCookies->AddColumn(new BStringColumn(B_TRANSLATE("Value"),
134 20 * em, 10 * em, 50 * em, 0), 3);
135 fCookies->AddColumn(new BStringColumn(B_TRANSLATE("Flags"),
136 flagsLength, flagsLength, flagsLength, 0), 4);
138 root->AddItem(BGroupLayoutBuilder(B_VERTICAL, B_USE_DEFAULT_SPACING)
139 .SetInsets(5, 5, 5, 5)
140 .AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING)
141 .Add(fHeaderView)
142 .AddGlue()
143 .End()
144 .Add(fCookies)
145 .AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING)
146 .SetInsets(5, 5, 5, 5)
147 #if 0
148 .Add(new BButton("import", B_TRANSLATE("Import" B_UTF8_ELLIPSIS),
149 NULL))
150 .Add(new BButton("export", B_TRANSLATE("Export" B_UTF8_ELLIPSIS),
151 NULL))
152 #endif
153 .AddGlue()
154 .Add(new BButton("delete", B_TRANSLATE("Delete"),
155 new BMessage(COOKIE_DELETE))), 3);
157 fDomains->SetSelectionMessage(new BMessage(DOMAIN_SELECTED));
161 void
162 CookieWindow::MessageReceived(BMessage* message)
164 switch(message->what) {
165 case DOMAIN_SELECTED:
167 int32 index = message->FindInt32("index");
168 BStringItem* item = (BStringItem*)fDomains->ItemAt(index);
169 if (item != NULL) {
170 BString domain = item->Text();
171 _ShowCookiesForDomain(domain);
173 return;
176 case COOKIE_REFRESH:
177 _BuildDomainList();
178 return;
180 case COOKIE_DELETE:
181 _DeleteCookies();
182 return;
184 BWindow::MessageReceived(message);
188 void
189 CookieWindow::Show()
191 BWindow::Show();
192 if (IsHidden())
193 return;
195 PostMessage(COOKIE_REFRESH);
199 bool
200 CookieWindow::QuitRequested()
202 if (!IsHidden())
203 Hide();
204 return false;
208 void
209 CookieWindow::_BuildDomainList()
211 // Empty the domain list (TODO should we do this when hiding instead?)
212 for (int i = fDomains->FullListCountItems() - 1; i >= 1; i--) {
213 delete fDomains->FullListItemAt(i);
215 fDomains->MakeEmpty();
217 // BOutlineListView does not handle parent = NULL in many methods, so let's
218 // make sure everything always has a parent.
219 DomainItem* rootItem = new DomainItem("", true);
220 fDomains->AddItem(rootItem);
222 // Populate the domain list
223 BNetworkCookieJar::Iterator it = fCookieJar.GetIterator();
225 const BNetworkCookie* cookie;
226 while ((cookie = it.NextDomain()) != NULL) {
227 _AddDomain(cookie->Domain(), false);
230 int i = 1;
231 while (i < fDomains->FullListCountItems())
233 DomainItem* item = (DomainItem*)fDomains->FullListItemAt(i);
234 // Detach items from the fake root
235 item->SetOutlineLevel(item->OutlineLevel() - 1);
236 i++;
238 fDomains->RemoveItem(rootItem);
239 delete rootItem;
241 i = 0;
242 int firstNotEmpty = i;
243 // Collapse empty items to keep the list short
244 while (i < fDomains->FullListCountItems())
246 DomainItem* item = (DomainItem*)fDomains->FullListItemAt(i);
247 if (item->fEmpty == true) {
248 if (fDomains->CountItemsUnder(item, true) == 1) {
249 // The item has no cookies, and only a single child. We can
250 // remove it and move its child one level up in the tree.
252 int count = fDomains->CountItemsUnder(item, false);
253 int index = fDomains->FullListIndexOf(item) + 1;
254 for (int j = 0; j < count; j++) {
255 BListItem* child = fDomains->FullListItemAt(index + j);
256 child->SetOutlineLevel(child->OutlineLevel() - 1);
259 fDomains->RemoveItem(item);
260 delete item;
262 // The moved child is at the same index the removed item was.
263 // We continue the loop without incrementing i to process it.
264 continue;
265 } else {
266 // The item has no cookies, but has multiple children. Mark it
267 // as disabled so it is not selectable.
268 item->SetEnabled(false);
269 if (i == firstNotEmpty)
270 firstNotEmpty++;
274 i++;
277 fDomains->Select(firstNotEmpty);
281 BStringItem*
282 CookieWindow::_AddDomain(BString domain, bool fake)
284 BStringItem* parent = NULL;
285 int firstDot = domain.FindFirst('.');
286 if (firstDot >= 0) {
287 BString parentDomain(domain);
288 parentDomain.Remove(0, firstDot + 1);
289 parent = _AddDomain(parentDomain, true);
290 } else {
291 parent = (BStringItem*)fDomains->FullListItemAt(0);
294 BListItem* existing;
295 int i = 0;
296 // check that we aren't already there
297 while ((existing = fDomains->ItemUnderAt(parent, true, i++)) != NULL) {
298 DomainItem* stringItem = (DomainItem*)existing;
299 if (stringItem->Text() == domain) {
300 if (fake == false)
301 stringItem->fEmpty = false;
302 return stringItem;
306 #if 0
307 puts("==============================");
308 for (i = 0; i < fDomains->FullListCountItems(); i++) {
309 BStringItem* t = (BStringItem*)fDomains->FullListItemAt(i);
310 for (unsigned j = 0; j < t->OutlineLevel(); j++)
311 printf(" ");
312 printf("%s\n", t->Text());
314 #endif
316 // Insert the new item, keeping the list alphabetically sorted
317 BStringItem* domainItem = new DomainItem(domain, fake);
318 domainItem->SetOutlineLevel(parent->OutlineLevel() + 1);
319 BStringItem* sibling = NULL;
320 int siblingCount = fDomains->CountItemsUnder(parent, true);
321 for (i = 0; i < siblingCount; i++) {
322 sibling = (BStringItem*)fDomains->ItemUnderAt(parent, true, i);
323 if (strcmp(sibling->Text(), domainItem->Text()) > 0) {
324 fDomains->AddItem(domainItem, fDomains->FullListIndexOf(sibling));
325 return domainItem;
329 if (sibling) {
330 // There were siblings, but all smaller than what we try to insert.
331 // Insert after the last one (and its subitems)
332 fDomains->AddItem(domainItem, fDomains->FullListIndexOf(sibling)
333 + fDomains->CountItemsUnder(sibling, false) + 1);
334 } else {
335 // There were no siblings, insert right after the parent
336 fDomains->AddItem(domainItem, fDomains->FullListIndexOf(parent) + 1);
339 return domainItem;
343 void
344 CookieWindow::_ShowCookiesForDomain(BString domain)
346 BString label;
347 label.SetToFormat(B_TRANSLATE("Cookies for %s"), domain.String());
348 fHeaderView->SetText(label);
350 // Empty the cookie list
351 fCookies->Clear();
353 // Populate the domain list
354 BNetworkCookieJar::Iterator it = fCookieJar.GetIterator();
356 const BNetworkCookie* cookie;
357 /* FIXME A direct access to a domain would be needed in BNetworkCookieJar. */
358 while ((cookie = it.Next()) != NULL) {
359 if (cookie->Domain() == domain)
360 break;
363 if (cookie == NULL)
364 return;
366 do {
367 new CookieRow(fCookies, *cookie); // Attaches itself to the list
368 cookie = it.Next();
369 } while (cookie != NULL && cookie->Domain() == domain);
373 void
374 CookieWindow::_DeleteCookies()
376 // TODO shall we handle multiple selection here?
377 CookieRow* row = (CookieRow*)fCookies->CurrentSelection();
378 if (row == NULL) {
379 // TODO see if a domain is selected in the domain list, and delete all
380 // cookies for that domain
381 return;
384 fCookies->RemoveRow(row);
386 BNetworkCookie& cookie = row->Cookie();
387 cookie.SetExpirationDate(0);
388 fCookieJar.AddCookie(cookie);
390 delete row;