HaikuDepot: notify work status from main window
[haiku.git] / src / apps / firstbootprompt / BootPromptWindow.cpp
blob7708f69f14cab8ce476731c88aef8667768acbed
1 /*
2 * Copyright 2010, Stephan Aßmus <superstippi@gmx.de>
3 * Copyright 2010, Adrien Destugues <pulkomandy@pulkomandy.ath.cx>
4 * Copyright 2011, Axel Dörfler, axeld@pinc-software.de.
5 * All rights reserved. Distributed under the terms of the MIT License.
6 */
9 #include "BootPromptWindow.h"
11 #include <new>
12 #include <stdio.h>
14 #include <Bitmap.h>
15 #include <Button.h>
16 #include <Catalog.h>
17 #include <ControlLook.h>
18 #include <Directory.h>
19 #include <Entry.h>
20 #include <Font.h>
21 #include <FindDirectory.h>
22 #include <File.h>
23 #include <FormattingConventions.h>
24 #include <LayoutBuilder.h>
25 #include <ListView.h>
26 #include <Locale.h>
27 #include <Menu.h>
28 #include <MutableLocaleRoster.h>
29 #include <ObjectList.h>
30 #include <Path.h>
31 #include <Screen.h>
32 #include <ScrollView.h>
33 #include <SeparatorView.h>
34 #include <StringItem.h>
35 #include <StringView.h>
36 #include <TextView.h>
37 #include <UnicodeChar.h>
39 #include "BootPrompt.h"
40 #include "Keymap.h"
43 using BPrivate::MutableLocaleRoster;
46 enum {
47 MSG_LANGUAGE_SELECTED = 'lngs',
48 MSG_KEYMAP_SELECTED = 'kmps'
52 #undef B_TRANSLATION_CONTEXT
53 #define B_TRANSLATION_CONTEXT "BootPromptWindow"
56 namespace BPrivate {
57 void ForceUnloadCatalog();
61 static const char* kLanguageKeymapMappings[] = {
62 // While there is a "Dutch" keymap, it apparently has not been widely
63 // adopted, and the US-International keymap is common
64 "Dutch", "US-International"
66 static const size_t kLanguageKeymapMappingsSize
67 = sizeof(kLanguageKeymapMappings) / sizeof(kLanguageKeymapMappings[0]);
70 class LanguageItem : public BStringItem {
71 public:
72 LanguageItem(const char* label, const char* language)
74 BStringItem(label),
75 fLanguage(language)
77 fIcon = new(std::nothrow) BBitmap(BRect(0, 0, 15, 15), B_RGBA32);
78 if (fIcon != NULL
79 && (!fIcon->IsValid()
80 || BLocaleRoster::Default()->GetFlagIconForLanguage(fIcon,
81 language) != B_OK)) {
82 delete fIcon;
83 fIcon = NULL;
87 ~LanguageItem()
89 delete fIcon;
92 const char* Language() const
94 return fLanguage.String();
97 void DrawItem(BView* owner, BRect frame, bool complete)
99 BStringItem::DrawItem(owner, frame, true/*complete*/);
101 // Draw the icon
102 if (fIcon != NULL) {
103 frame.left = frame.right - kFlagWidth;
104 BRect iconFrame(frame);
105 iconFrame.Set(iconFrame.left, iconFrame.top + 1,
106 iconFrame.left + kFlagWidth - 2,
107 iconFrame.top + kFlagWidth - 1);
109 owner->SetDrawingMode(B_OP_OVER);
110 owner->DrawBitmap(fIcon, iconFrame);
111 owner->SetDrawingMode(B_OP_COPY);
115 private:
116 static const int kFlagWidth = 16;
118 BString fLanguage;
119 BBitmap* fIcon;
123 static int
124 compare_void_list_items(const void* _a, const void* _b)
126 static BCollator collator;
128 LanguageItem* a = *(LanguageItem**)_a;
129 LanguageItem* b = *(LanguageItem**)_b;
131 return collator.Compare(a->Text(), b->Text());
135 static int
136 compare_void_menu_items(const void* _a, const void* _b)
138 static BCollator collator;
140 BMenuItem* a = *(BMenuItem**)_a;
141 BMenuItem* b = *(BMenuItem**)_b;
143 return collator.Compare(a->Label(), b->Label());
147 // #pragma mark -
150 BootPromptWindow::BootPromptWindow()
152 BWindow(BRect(0, 0, 530, 400), "",
153 B_TITLED_WINDOW, B_NOT_ZOOMABLE | B_NOT_MINIMIZABLE | B_NOT_CLOSABLE
154 | B_NOT_RESIZABLE | B_AUTO_UPDATE_SIZE_LIMITS,
155 B_ALL_WORKSPACES),
156 fDefaultKeymapItem(NULL)
158 SetSizeLimits(450, 16384, 350, 16384);
160 rgb_color textColor = ui_color(B_PANEL_TEXT_COLOR);
161 fInfoTextView = new BTextView("");
162 fInfoTextView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
163 fInfoTextView->SetFontAndColor(be_plain_font, B_FONT_ALL, &textColor);
164 fInfoTextView->MakeEditable(false);
165 fInfoTextView->MakeSelectable(false);
166 fInfoTextView->MakeResizable(false);
168 // Carefully designed to not exceed the 640x480 resolution with a 12pt font.
169 float width = fInfoTextView->StringWidth("Thank you for trying out Haiku,"
170 " We hope you like it!") * 1.5;
171 float height = be_plain_font->Size() * 23;
173 fInfoTextView->SetExplicitMinSize(BSize(width, height));
175 fDesktopButton = new BButton("", new BMessage(MSG_BOOT_DESKTOP));
176 fDesktopButton->SetTarget(be_app);
177 fDesktopButton->MakeDefault(true);
179 fInstallerButton = new BButton("", new BMessage(MSG_RUN_INSTALLER));
180 fInstallerButton->SetTarget(be_app);
182 fLanguagesLabelView = new BStringView("languagesLabel", "");
183 fLanguagesLabelView->SetFont(be_bold_font);
184 fLanguagesLabelView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED,
185 B_SIZE_UNSET));
187 fLanguagesListView = new BListView();
188 fLanguagesListView->SetFlags(
189 fLanguagesListView->Flags() | B_FULL_UPDATE_ON_RESIZE);
190 // Our ListItem rendering depends on the width of the view, so
191 // we need a full update
192 BScrollView* languagesScrollView = new BScrollView("languagesScroll",
193 fLanguagesListView, B_WILL_DRAW, false, true);
195 // Make sure the language list view is always wide enough to show the
196 // largest language, with some extra space
197 fLanguagesListView->SetExplicitSize(
198 BSize(fLanguagesListView->StringWidth("Portuguese (Brazil)") + 32,
199 height));
201 fKeymapsMenuField = new BMenuField("", "", new BMenu(""));
202 fKeymapsMenuField->Menu()->SetLabelFromMarked(true);
204 _InitCatalog(true);
205 _PopulateLanguages();
206 _PopulateKeymaps();
208 BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
209 .AddGroup(B_HORIZONTAL)
210 .Add(fLanguagesLabelView)
211 .SetInsets(B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING,
212 B_USE_WINDOW_SPACING, B_USE_DEFAULT_SPACING)
213 .End()
214 .AddGroup(B_HORIZONTAL)
215 .Add(languagesScrollView)
216 .Add(fInfoTextView)
217 .SetInsets(B_USE_WINDOW_SPACING, 0)
218 .End()
219 .AddGroup(B_HORIZONTAL)
220 .Add(fKeymapsMenuField)
221 .AddGlue()
222 .SetInsets(B_USE_WINDOW_SPACING, B_USE_DEFAULT_SPACING)
223 .End()
224 .Add(new BSeparatorView(B_HORIZONTAL))
225 .AddGroup(B_HORIZONTAL)
226 .AddGlue()
227 .Add(fInstallerButton)
228 .Add(fDesktopButton)
229 .SetInsets(B_USE_WINDOW_SPACING, B_USE_DEFAULT_SPACING,
230 B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING)
231 .End();
233 fLanguagesListView->MakeFocus();
235 // Force the info text view to use a reasonable size
236 fInfoTextView->SetText("x\n\n\n\n\n\n\n\n\n\n\n\n\n\nx");
237 ResizeToPreferred();
239 _UpdateStrings();
240 CenterOnScreen();
241 Show();
245 void
246 BootPromptWindow::MessageReceived(BMessage* message)
248 switch (message->what) {
249 case MSG_LANGUAGE_SELECTED:
250 if (LanguageItem* item = static_cast<LanguageItem*>(
251 fLanguagesListView->ItemAt(
252 fLanguagesListView->CurrentSelection(0)))) {
253 BMessage preferredLanguages;
254 preferredLanguages.AddString("language", item->Language());
255 MutableLocaleRoster::Default()->SetPreferredLanguages(
256 &preferredLanguages);
257 _InitCatalog(true);
259 // Select default keymap by language
260 BLanguage language(item->Language());
261 BMenuItem* keymapItem = _KeymapItemForLanguage(language);
262 if (keymapItem != NULL) {
263 keymapItem->SetMarked(true);
264 _ActivateKeymap(keymapItem->Message());
267 // Calling it here is a cheap way of preventing the user to have
268 // no item selected. Always the current item will be selected.
269 _UpdateStrings();
270 break;
272 case MSG_KEYMAP_SELECTED:
273 _ActivateKeymap(message);
274 break;
276 default:
277 BWindow::MessageReceived(message);
282 void
283 BootPromptWindow::_InitCatalog(bool saveSettings)
285 // Initilialize the Locale Kit
286 BPrivate::ForceUnloadCatalog();
288 if (!saveSettings)
289 return;
291 BMessage settings;
292 BString language;
293 if (BLocaleRoster::Default()->GetCatalog()->GetLanguage(&language) == B_OK)
294 settings.AddString("language", language.String());
296 MutableLocaleRoster::Default()->SetPreferredLanguages(&settings);
298 BFormattingConventions conventions(language.String());
299 MutableLocaleRoster::Default()->SetDefaultFormattingConventions(
300 conventions);
304 void
305 BootPromptWindow::_UpdateStrings()
307 SetTitle(B_TRANSLATE("Welcome to Haiku!"));
309 fInfoTextView->SetText(B_TRANSLATE_COMMENT(
310 "Thank you for trying out Haiku! We hope you'll like it!\n\n"
311 "You can select your preferred language and keyboard "
312 "layout from the list on the left which will then be used instantly. "
313 "You can easily change both settings from the Desktop later on on "
314 "the fly.\n\n"
316 "Do you wish to run the Installer or continue booting to the "
317 "Desktop?\n",
319 "For other languages, a note could be added: \""
320 "Note: Localization of Haiku applications and other components is "
321 "an on-going effort. You will frequently encounter untranslated "
322 "strings, but if you like, you can join in the work at "
323 "<www.haiku-os.org>.\""));
325 fDesktopButton->SetLabel(B_TRANSLATE("Boot to Desktop"));
326 fInstallerButton->SetLabel(B_TRANSLATE("Run Installer"));
328 fLanguagesLabelView->SetText(B_TRANSLATE("Language"));
329 fKeymapsMenuField->SetLabel(B_TRANSLATE("Keymap"));
330 if (fKeymapsMenuField->Menu()->FindMarked() == NULL)
331 fKeymapsMenuField->MenuItem()->SetLabel(B_TRANSLATE("Custom"));
335 void
336 BootPromptWindow::_PopulateLanguages()
338 // TODO: detect language/country from IP address
340 // Get current first preferred language of the user
341 BMessage preferredLanguages;
342 BLocaleRoster::Default()->GetPreferredLanguages(&preferredLanguages);
343 const char* firstPreferredLanguage;
344 if (preferredLanguages.FindString("language", &firstPreferredLanguage)
345 != B_OK) {
346 // Fall back to built-in language of this application.
347 firstPreferredLanguage = "en";
350 BMessage installedCatalogs;
351 BLocaleRoster::Default()->GetAvailableCatalogs(&installedCatalogs,
352 "x-vnd.Haiku-FirstBootPrompt");
354 BFont font;
355 fLanguagesListView->GetFont(&font);
357 // Try to instantiate a BCatalog for each language, it will only work
358 // for translations of this application. So the list of languages will be
359 // limited to catalogs written for this application, which is on purpose!
361 const char* languageID;
362 LanguageItem* currentItem = NULL;
363 for (int32 i = 0; installedCatalogs.FindString("language", i, &languageID)
364 == B_OK; i++) {
365 BLanguage* language;
366 if (BLocaleRoster::Default()->GetLanguage(languageID, &language)
367 == B_OK) {
368 BString name;
369 language->GetNativeName(name);
371 // TODO: the following block fails to detect a couple of language
372 // names as containing glyphs we can't render. Why's that?
373 bool hasGlyphs[name.CountChars()];
374 font.GetHasGlyphs(name.String(), name.CountChars(), hasGlyphs);
375 for (int32 i = 0; i < name.CountChars(); ++i) {
376 if (!hasGlyphs[i]) {
377 // replace by name translated to current language
378 language->GetName(name);
379 break;
383 LanguageItem* item = new LanguageItem(name.String(),
384 languageID);
385 fLanguagesListView->AddItem(item);
386 // Select this item if it is the first preferred language
387 if (strcmp(firstPreferredLanguage, languageID) == 0)
388 currentItem = item;
390 delete language;
391 } else
392 fprintf(stderr, "failed to get BLanguage for %s\n", languageID);
395 fLanguagesListView->SortItems(compare_void_list_items);
396 if (currentItem != NULL)
397 fLanguagesListView->Select(fLanguagesListView->IndexOf(currentItem));
398 fLanguagesListView->ScrollToSelection();
400 // Re-enable sending the selection message.
401 fLanguagesListView->SetSelectionMessage(
402 new BMessage(MSG_LANGUAGE_SELECTED));
406 void
407 BootPromptWindow::_PopulateKeymaps()
409 // Get the name of the current keymap, so we can mark the correct entry
410 // in the list view.
411 BString currentName;
412 entry_ref currentRef;
413 if (_GetCurrentKeymapRef(currentRef) == B_OK) {
414 BNode node(&currentRef);
415 node.ReadAttrString("keymap:name", &currentName);
418 // TODO: common keymaps!
419 BPath path;
420 if (find_directory(B_SYSTEM_DATA_DIRECTORY, &path) != B_OK
421 || path.Append("Keymaps") != B_OK) {
422 return;
425 // US-International is the default keymap, if we could not found a
426 // matching one
427 BString usInternational("US-International");
429 // Populate the menu
430 BDirectory directory;
431 if (directory.SetTo(path.Path()) == B_OK) {
432 entry_ref ref;
433 BList itemsList;
434 while (directory.GetNextRef(&ref) == B_OK) {
435 BMessage* message = new BMessage(MSG_KEYMAP_SELECTED);
436 message->AddRef("ref", &ref);
437 BMenuItem* item = new BMenuItem(ref.name, message);
438 itemsList.AddItem(item);
439 if (currentName == ref.name)
440 item->SetMarked(true);
442 if (usInternational == ref.name)
443 fDefaultKeymapItem = item;
445 itemsList.SortItems(compare_void_menu_items);
446 fKeymapsMenuField->Menu()->AddList(&itemsList, 0);
451 void
452 BootPromptWindow::_ActivateKeymap(const BMessage* message) const
454 entry_ref ref;
455 if (message == NULL || message->FindRef("ref", &ref) != B_OK)
456 return;
458 // Load and use the new keymap
459 Keymap keymap;
460 if (keymap.Load(ref) != B_OK) {
461 fprintf(stderr, "Failed to load new keymap file (%s).\n", ref.name);
462 return;
465 // Get entry_ref to the Key_map file in the user settings.
466 entry_ref currentRef;
467 if (_GetCurrentKeymapRef(currentRef) != B_OK) {
468 fprintf(stderr, "Failed to get ref to user keymap file.\n");
469 return;
472 if (keymap.Save(currentRef) != B_OK) {
473 fprintf(stderr, "Failed to save new keymap file (%s).\n", ref.name);
474 return;
477 keymap.Use();
481 status_t
482 BootPromptWindow::_GetCurrentKeymapRef(entry_ref& ref) const
484 BPath path;
485 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK
486 || path.Append("Key_map") != B_OK) {
487 return B_ERROR;
490 return get_ref_for_path(path.Path(), &ref);
494 BMenuItem*
495 BootPromptWindow::_KeymapItemForLanguage(BLanguage& language) const
497 BLanguage english("en");
498 BString name;
499 if (language.GetName(name, &english) != B_OK)
500 return fDefaultKeymapItem;
502 // Check special mappings first
503 for (size_t i = 0; i < kLanguageKeymapMappingsSize; i += 2) {
504 if (!strcmp(name, kLanguageKeymapMappings[i])) {
505 name = kLanguageKeymapMappings[i + 1];
506 break;
510 BMenu* menu = fKeymapsMenuField->Menu();
511 for (int32 i = 0; i < menu->CountItems(); i++) {
512 BMenuItem* item = menu->ItemAt(i);
513 BMessage* message = item->Message();
515 entry_ref ref;
516 if (message->FindRef("ref", &ref) == B_OK
517 && name == ref.name)
518 return item;
521 return fDefaultKeymapItem;