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.
9 #include "BootPromptWindow.h"
17 #include <ControlLook.h>
18 #include <Directory.h>
21 #include <FindDirectory.h>
23 #include <FormattingConventions.h>
24 #include <LayoutBuilder.h>
28 #include <MutableLocaleRoster.h>
29 #include <ObjectList.h>
32 #include <ScrollView.h>
33 #include <SeparatorView.h>
34 #include <StringItem.h>
35 #include <StringView.h>
37 #include <UnicodeChar.h>
39 #include "BootPrompt.h"
43 using BPrivate::MutableLocaleRoster
;
47 MSG_LANGUAGE_SELECTED
= 'lngs',
48 MSG_KEYMAP_SELECTED
= 'kmps'
52 #undef B_TRANSLATION_CONTEXT
53 #define B_TRANSLATION_CONTEXT "BootPromptWindow"
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
{
72 LanguageItem(const char* label
, const char* language
)
77 fIcon
= new(std::nothrow
) BBitmap(BRect(0, 0, 15, 15), B_RGBA32
);
80 || BLocaleRoster::Default()->GetFlagIconForLanguage(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*/);
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
);
116 static const int kFlagWidth
= 16;
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());
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());
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
,
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
,
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,
201 fKeymapsMenuField
= new BMenuField("", "", new BMenu(""));
202 fKeymapsMenuField
->Menu()->SetLabelFromMarked(true);
205 _PopulateLanguages();
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
)
214 .AddGroup(B_HORIZONTAL
)
215 .Add(languagesScrollView
)
217 .SetInsets(B_USE_WINDOW_SPACING
, 0)
219 .AddGroup(B_HORIZONTAL
)
220 .Add(fKeymapsMenuField
)
222 .SetInsets(B_USE_WINDOW_SPACING
, B_USE_DEFAULT_SPACING
)
224 .Add(new BSeparatorView(B_HORIZONTAL
))
225 .AddGroup(B_HORIZONTAL
)
227 .Add(fInstallerButton
)
229 .SetInsets(B_USE_WINDOW_SPACING
, B_USE_DEFAULT_SPACING
,
230 B_USE_WINDOW_SPACING
, B_USE_WINDOW_SPACING
)
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");
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
);
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.
272 case MSG_KEYMAP_SELECTED
:
273 _ActivateKeymap(message
);
277 BWindow::MessageReceived(message
);
283 BootPromptWindow::_InitCatalog(bool saveSettings
)
285 // Initilialize the Locale Kit
286 BPrivate::ForceUnloadCatalog();
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(
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 "
316 "Do you wish to run the Installer or continue booting to the "
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"));
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
)
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");
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
)
366 if (BLocaleRoster::Default()->GetLanguage(languageID
, &language
)
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
) {
377 // replace by name translated to current language
378 language
->GetName(name
);
383 LanguageItem
* item
= new LanguageItem(name
.String(),
385 fLanguagesListView
->AddItem(item
);
386 // Select this item if it is the first preferred language
387 if (strcmp(firstPreferredLanguage
, languageID
) == 0)
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
));
407 BootPromptWindow::_PopulateKeymaps()
409 // Get the name of the current keymap, so we can mark the correct entry
412 entry_ref currentRef
;
413 if (_GetCurrentKeymapRef(currentRef
) == B_OK
) {
414 BNode
node(¤tRef
);
415 node
.ReadAttrString("keymap:name", ¤tName
);
418 // TODO: common keymaps!
420 if (find_directory(B_SYSTEM_DATA_DIRECTORY
, &path
) != B_OK
421 || path
.Append("Keymaps") != B_OK
) {
425 // US-International is the default keymap, if we could not found a
427 BString
usInternational("US-International");
430 BDirectory directory
;
431 if (directory
.SetTo(path
.Path()) == B_OK
) {
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);
452 BootPromptWindow::_ActivateKeymap(const BMessage
* message
) const
455 if (message
== NULL
|| message
->FindRef("ref", &ref
) != B_OK
)
458 // Load and use the new keymap
460 if (keymap
.Load(ref
) != B_OK
) {
461 fprintf(stderr
, "Failed to load new keymap file (%s).\n", ref
.name
);
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");
472 if (keymap
.Save(currentRef
) != B_OK
) {
473 fprintf(stderr
, "Failed to save new keymap file (%s).\n", ref
.name
);
482 BootPromptWindow::_GetCurrentKeymapRef(entry_ref
& ref
) const
485 if (find_directory(B_USER_SETTINGS_DIRECTORY
, &path
) != B_OK
486 || path
.Append("Key_map") != B_OK
) {
490 return get_ref_for_path(path
.Path(), &ref
);
495 BootPromptWindow::_KeymapItemForLanguage(BLanguage
& language
) const
497 BLanguage
english("en");
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];
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();
516 if (message
->FindRef("ref", &ref
) == B_OK
521 return fDefaultKeymapItem
;