2 * Copyright 2005-2010, Axel Dörfler, axeld@pinc-software.de.
3 * Copyright 2009-2010, Adrien Destugues <pulkomandy@gmail.com>.
4 * All rights reserved. Distributed under the terms of the MIT License.
8 #include "LocaleWindow.h"
13 #include <Application.h>
17 #include <ControlLook.h>
18 #include <FormattingConventions.h>
19 #include <GroupLayout.h>
20 #include <LayoutBuilder.h>
22 #include <MutableLocaleRoster.h>
24 #include <ScrollView.h>
25 #include <SeparatorView.h>
26 #include <StringView.h>
28 #include <UnicodeChar.h>
30 #include "FormatSettingsView.h"
31 #include "LocalePreflet.h"
32 #include "LanguageListView.h"
35 using BPrivate::MutableLocaleRoster
;
38 #undef B_TRANSLATION_CONTEXT
39 #define B_TRANSLATION_CONTEXT "Locale Preflet Window"
42 static const uint32 kMsgLanguageInvoked
= 'LaIv';
43 static const uint32 kMsgLanguageDragged
= 'LaDr';
44 static const uint32 kMsgPreferredLanguageInvoked
= 'PLIv';
45 static const uint32 kMsgPreferredLanguageDragged
= 'PLDr';
46 static const uint32 kMsgPreferredLanguageDeleted
= 'PLDl';
47 static const uint32 kMsgConventionsSelection
= 'csel';
48 static const uint32 kMsgDefaults
= 'dflt';
50 static const uint32 kMsgPreferredLanguagesChanged
= 'lang';
51 static const uint32 kMsgFilesystemTranslationChanged
= 'fsys';
55 compare_typed_list_items(const BListItem
* _a
, const BListItem
* _b
)
57 static BCollator collator
;
59 LanguageListItem
* a
= (LanguageListItem
*)_a
;
60 LanguageListItem
* b
= (LanguageListItem
*)_b
;
62 return collator
.Compare(a
->Text(), b
->Text());
68 LocaleWindow::LocaleWindow()
70 BWindow(BRect(0, 0, 0, 0), B_TRANSLATE_SYSTEM_NAME("Locale"), B_TITLED_WINDOW
,
71 B_QUIT_ON_WINDOW_CLOSE
| B_ASYNCHRONOUS_CONTROLS
72 | B_AUTO_UPDATE_SIZE_LIMITS
),
73 fInitialConventionsItem(NULL
),
74 fDefaultConventionsItem(NULL
),
75 fFilesystemTranslationCheckbox(NULL
)
77 SetLayout(new BGroupLayout(B_HORIZONTAL
));
79 float spacing
= be_control_look
->DefaultItemSpacing();
81 BTabView
* tabView
= new BTabView("tabview", B_WIDTH_FROM_WIDEST
);
82 tabView
->SetBorder(B_NO_BORDER
);
84 BGroupView
* languageTab
= new BGroupView(B_TRANSLATE("Language"),
85 B_HORIZONTAL
, spacing
);
87 // first list: available languages
88 fLanguageListView
= new LanguageListView("available",
89 B_MULTIPLE_SELECTION_LIST
);
90 BScrollView
* scrollView
= new BScrollView("scroller", fLanguageListView
,
91 B_WILL_DRAW
| B_FRAME_EVENTS
, true, true);
93 fLanguageListView
->SetInvocationMessage(new BMessage(kMsgLanguageInvoked
));
94 fLanguageListView
->SetDragMessage(new BMessage(kMsgLanguageDragged
));
95 fLanguageListView
->SetGlobalDropTargetIndicator(true);
98 fLanguageListView
->GetFont(&font
);
100 // Fill the language list from the LocaleRoster data
101 BMessage availableLanguages
;
102 if (BLocaleRoster::Default()->GetAvailableLanguages(&availableLanguages
)
105 LanguageListItem
* currentToplevelItem
= NULL
;
107 for (int i
= 0; availableLanguages
.FindString("language", i
, ¤tID
)
109 // Now get the human-readable, native name for each language
111 BLanguage
currentLanguage(currentID
.String());
112 currentLanguage
.GetNativeName(name
);
114 int nameLength
= name
.CountChars();
115 bool hasGlyphs
[nameLength
];
116 font
.GetHasGlyphs(name
.String(), nameLength
, hasGlyphs
);
117 for (int32 i
= 0; i
< nameLength
; ++i
) {
119 // replace by name translated to current language
120 currentLanguage
.GetName(name
);
125 LanguageListItem
* item
;
126 if (currentLanguage
.IsCountrySpecific()) {
127 item
= new LanguageListItemWithFlag(name
, currentID
.String(),
128 currentLanguage
.Code(), currentLanguage
.CountryCode());
130 item
= new LanguageListItem(name
, currentID
.String(),
131 currentLanguage
.Code());
134 if (currentLanguage
.IsCountrySpecific()
135 && currentToplevelItem
!= NULL
136 && currentToplevelItem
->Code() == item
->Code()) {
137 fLanguageListView
->AddUnder(item
, currentToplevelItem
);
138 } else if (currentLanguage
.ScriptCode() != NULL
139 && currentToplevelItem
!= NULL
140 && currentToplevelItem
->Code() == item
->Code()) {
141 // This is a script for some language, skip it and add the
142 // country-specific variants to the parent directly
145 // This is a generic language, add it at top-level
146 fLanguageListView
->AddItem(item
);
147 item
->SetExpanded(false);
148 currentToplevelItem
= item
;
152 fLanguageListView
->FullListSortItems(compare_typed_list_items
);
154 BAlert
* alert
= new BAlert("Error",
155 B_TRANSLATE("Unable to find the available languages! You can't "
156 "use this preflet!"),
157 B_TRANSLATE("OK"), NULL
, NULL
,
158 B_WIDTH_AS_USUAL
, B_OFFSET_SPACING
, B_STOP_ALERT
);
159 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
163 // Second list: active languages
164 fPreferredListView
= new LanguageListView("preferred",
165 B_MULTIPLE_SELECTION_LIST
);
166 BScrollView
* scrollViewEnabled
= new BScrollView("scroller",
167 fPreferredListView
, B_WILL_DRAW
| B_FRAME_EVENTS
, true, true);
169 fPreferredListView
->SetInvocationMessage(
170 new BMessage(kMsgPreferredLanguageInvoked
));
171 fPreferredListView
->SetDeleteMessage(
172 new BMessage(kMsgPreferredLanguageDeleted
));
173 fPreferredListView
->SetDragMessage(
174 new BMessage(kMsgPreferredLanguageDragged
));
176 BLayoutBuilder::Group
<>(languageTab
)
177 .AddGroup(B_VERTICAL
)
178 .Add(new BStringView("", B_TRANSLATE("Available languages")))
181 .AddGroup(B_VERTICAL
)
182 .Add(new BStringView("", B_TRANSLATE("Preferred languages")))
183 .Add(scrollViewEnabled
)
185 .SetInsets(B_USE_WINDOW_SPACING
, B_USE_WINDOW_SPACING
,
186 B_USE_WINDOW_SPACING
, B_USE_DEFAULT_SPACING
);
188 BView
* countryTab
= new BView(B_TRANSLATE("Formatting"), B_WILL_DRAW
);
189 countryTab
->SetLayout(new BGroupLayout(B_VERTICAL
, 0));
191 fConventionsListView
= new LanguageListView("formatting",
192 B_SINGLE_SELECTION_LIST
);
193 scrollView
= new BScrollView("scroller", fConventionsListView
,
194 B_WILL_DRAW
| B_FRAME_EVENTS
, true, true);
195 fConventionsListView
->SetSelectionMessage(
196 new BMessage(kMsgConventionsSelection
));
198 // get all available formatting conventions (by language)
199 BFormattingConventions initialConventions
;
200 BLocale::Default()->GetFormattingConventions(&initialConventions
);
201 BString conventionsID
;
202 fInitialConventionsItem
= NULL
;
203 LanguageListItem
* currentToplevelItem
= NULL
;
205 availableLanguages
.FindString("language", i
, &conventionsID
) == B_OK
;
207 BFormattingConventions
conventions(conventionsID
);
208 BString conventionsName
;
209 conventions
.GetName(conventionsName
);
211 LanguageListItem
* item
;
212 if (conventions
.AreCountrySpecific()) {
213 item
= new LanguageListItemWithFlag(conventionsName
, conventionsID
,
214 conventions
.LanguageCode(), conventions
.CountryCode());
216 item
= new LanguageListItem(conventionsName
, conventionsID
,
217 conventions
.LanguageCode());
219 if (!strcmp(conventionsID
, "en_US"))
220 fDefaultConventionsItem
= item
;
221 if (conventions
.AreCountrySpecific()
222 && currentToplevelItem
!= NULL
223 && currentToplevelItem
->Code() == item
->Code()) {
224 if (!strcmp(conventionsID
, initialConventions
.ID())) {
225 fConventionsListView
->Expand(currentToplevelItem
);
226 fInitialConventionsItem
= item
;
228 fConventionsListView
->AddUnder(item
, currentToplevelItem
);
230 // This conventions-item isn't country-specific, add it at top-level
231 fConventionsListView
->AddItem(item
);
232 item
->SetExpanded(false);
233 currentToplevelItem
= item
;
234 if (!strcmp(conventionsID
, initialConventions
.ID()))
235 fInitialConventionsItem
= item
;
239 fConventionsListView
->FullListSortItems(compare_typed_list_items
);
240 if (fInitialConventionsItem
!= NULL
) {
241 fConventionsListView
->Select(fConventionsListView
->IndexOf(
242 fInitialConventionsItem
));
245 fConventionsListView
->SetExplicitMinSize(BSize(20 * be_plain_font
->Size(),
248 fFormatView
= new FormatSettingsView();
250 countryTab
->AddChild(BLayoutBuilder::Group
<>(B_HORIZONTAL
, spacing
)
251 .AddGroup(B_VERTICAL
, 3)
255 .SetInsets(B_USE_WINDOW_SPACING
, B_USE_WINDOW_SPACING
,
256 B_USE_WINDOW_SPACING
, B_USE_DEFAULT_SPACING
));
258 BView
* optionsTab
= new BView(B_TRANSLATE("Options"), B_WILL_DRAW
);
259 optionsTab
->SetLayout(new BGroupLayout(B_VERTICAL
, 0));
261 fFilesystemTranslationCheckbox
= new BCheckBox("filesystemTranslation",
262 B_TRANSLATE("Translate application and folder names in Deskbar and Tracker."),
263 new BMessage(kMsgFilesystemTranslationChanged
));
265 fFilesystemTranslationCheckbox
->SetValue(
266 BLocaleRoster::Default()->IsFilesystemTranslationPreferred());
268 optionsTab
->AddChild(BLayoutBuilder::Group
<>(B_VERTICAL
)
269 .Add(fFilesystemTranslationCheckbox
)
271 .SetInsets(B_USE_WINDOW_SPACING
, B_USE_WINDOW_SPACING
,
272 B_USE_WINDOW_SPACING
, B_USE_DEFAULT_SPACING
));
274 tabView
->AddTab(languageTab
);
275 tabView
->AddTab(countryTab
);
276 tabView
->AddTab(optionsTab
);
279 = new BButton(B_TRANSLATE("Defaults"), new BMessage(kMsgDefaults
));
282 = new BButton(B_TRANSLATE("Revert"), new BMessage(kMsgRevert
));
283 fRevertButton
->SetEnabled(false);
285 BLayoutBuilder::Group
<>(this, B_VERTICAL
, 0)
286 .SetInsets(0, B_USE_DEFAULT_SPACING
, 0, B_USE_WINDOW_SPACING
)
288 .Add(new BSeparatorView(B_HORIZONTAL
))
289 .AddGroup(B_HORIZONTAL
)
292 .SetInsets(B_USE_WINDOW_SPACING
, B_USE_DEFAULT_SPACING
,
293 B_USE_WINDOW_SPACING
, 0)
302 LocaleWindow::~LocaleWindow()
308 LocaleWindow::MessageReceived(BMessage
* message
)
310 switch (message
->what
) {
311 case B_LOCALE_CHANGED
:
312 fFormatView
->MessageReceived(message
);
322 fFormatView
->Revert();
323 fConventionsListView
->DeselectAll();
324 if (fInitialConventionsItem
!= NULL
) {
326 = fConventionsListView
->Superitem(fInitialConventionsItem
);
327 if (superitem
!= NULL
)
328 superitem
->SetExpanded(true);
329 fConventionsListView
->Select(fConventionsListView
->IndexOf(
330 fInitialConventionsItem
));
331 fConventionsListView
->ScrollToSelection();
337 case kMsgSettingsChanged
:
341 case kMsgLanguageDragged
:
344 if (message
->FindPointer("drop_target", &target
) != B_OK
345 || target
!= fPreferredListView
)
348 // Add from available languages to preferred languages
350 if (message
->FindInt32("drop_index", &dropIndex
) != B_OK
)
351 dropIndex
= fPreferredListView
->CountItems();
354 for (int32 i
= 0; message
->FindInt32("index", i
, &index
) == B_OK
;
356 LanguageListItem
* item
= static_cast<LanguageListItem
*>(
357 fLanguageListView
->ItemAt(index
));
358 _InsertPreferredLanguage(item
, dropIndex
++);
362 case kMsgLanguageInvoked
:
365 for (int32 i
= 0; message
->FindInt32("index", i
, &index
) == B_OK
;
367 LanguageListItem
* item
= static_cast<LanguageListItem
*>(
368 fLanguageListView
->ItemAt(index
));
369 _InsertPreferredLanguage(item
);
374 case kMsgPreferredLanguageDragged
:
377 if (fPreferredListView
->CountItems() == 1
378 || message
->FindPointer("drop_target", &target
) != B_OK
)
381 if (target
== fPreferredListView
) {
383 int32 dropIndex
= message
->FindInt32("drop_index");
386 message
->FindInt32("index", i
, &index
) == B_OK
;
388 if (dropIndex
> index
) {
392 BListItem
* item
= fPreferredListView
->RemoveItem(index
);
393 fPreferredListView
->AddItem(item
, dropIndex
);
396 _PreferredLanguagesChanged();
400 // supposed to fall through - remove item
402 case kMsgPreferredLanguageDeleted
:
403 case kMsgPreferredLanguageInvoked
:
405 if (fPreferredListView
->CountItems() == 1)
408 // Remove from preferred languages
410 for (int32 i
= 0; message
->FindInt32("index", i
, &index
) == B_OK
;
412 delete fPreferredListView
->RemoveItem(index
- i
);
414 if (message
->what
== kMsgPreferredLanguageDeleted
) {
415 int32 count
= fPreferredListView
->CountItems();
416 fPreferredListView
->Select(
417 index
< count
? index
: count
- 1);
421 _PreferredLanguagesChanged();
425 case kMsgConventionsSelection
:
427 // Country selection changed.
428 // Get the new selected country from the ListView and send it to the
429 // main app event handler.
431 if (message
->FindPointer("source", &listView
) != B_OK
)
434 BListView
* conventionsList
= static_cast<BListView
*>(listView
);
435 if (conventionsList
== NULL
)
438 LanguageListItem
* item
= static_cast<LanguageListItem
*>
439 (conventionsList
->ItemAt(conventionsList
->CurrentSelection()));
443 BFormattingConventions
conventions(item
->ID());
444 MutableLocaleRoster::Default()->SetDefaultFormattingConventions(
448 fFormatView
->Refresh();
452 case kMsgFilesystemTranslationChanged
:
454 MutableLocaleRoster::Default()->SetFilesystemTranslationPreferred(
455 fFilesystemTranslationCheckbox
->Value());
457 BAlert
* alert
= new BAlert(B_TRANSLATE("Locale"),
458 B_TRANSLATE("Deskbar and Tracker need to be restarted for this "
459 "change to take effect. Would you like to restart them now?"),
460 B_TRANSLATE("Cancel"), B_TRANSLATE("Restart"), NULL
,
461 B_WIDTH_FROM_WIDEST
, B_IDEA_ALERT
);
462 alert
->SetShortcut(0, B_ESCAPE
);
463 alert
->Go(new BInvoker(new BMessage(kMsgRestartTrackerAndDeskbar
),
469 BWindow::MessageReceived(message
);
476 LocaleWindow::QuitRequested()
489 fConventionsListView
->ScrollToSelection();
496 LocaleWindow::_SettingsChanged()
498 fRevertButton
->SetEnabled(fFormatView
->IsReversible() || _IsReversible());
503 LocaleWindow::_SettingsReverted()
505 fRevertButton
->SetEnabled(false);
510 LocaleWindow::_IsReversible() const
512 BMessage preferredLanguages
;
513 BLocaleRoster::Default()->GetPreferredLanguages(&preferredLanguages
);
515 return !preferredLanguages
.HasSameData(fInitialPreferredLanguages
);
520 LocaleWindow::_PreferredLanguagesChanged()
522 BMessage preferredLanguages
;
524 while (index
< fPreferredListView
->CountItems()) {
525 LanguageListItem
* item
= static_cast<LanguageListItem
*>(
526 fPreferredListView
->ItemAt(index
));
528 preferredLanguages
.AddString("language", item
->ID());
531 MutableLocaleRoster::Default()->SetPreferredLanguages(&preferredLanguages
);
533 _EnableDisableLanguages();
538 LocaleWindow::_EnableDisableLanguages()
542 for (int32 i
= 0; i
< fLanguageListView
->FullListCountItems(); i
++) {
543 LanguageListItem
* item
= static_cast<LanguageListItem
*>(
544 fLanguageListView
->FullListItemAt(i
));
546 bool enable
= fPreferredListView
->ItemForLanguageID(item
->ID()) == NULL
;
547 if (item
->IsEnabled() != enable
) {
548 item
->SetEnabled(enable
);
550 int32 visibleIndex
= fLanguageListView
->IndexOf(item
);
551 if (visibleIndex
>= 0) {
553 fLanguageListView
->Deselect(visibleIndex
);
554 fLanguageListView
->InvalidateItem(visibleIndex
);
564 LocaleWindow::_Refresh(bool setInitial
)
566 BMessage preferredLanguages
;
567 BLocaleRoster::Default()->GetPreferredLanguages(&preferredLanguages
);
569 fInitialPreferredLanguages
= preferredLanguages
;
571 _SetPreferredLanguages(preferredLanguages
);
576 LocaleWindow::_Revert()
578 _SetPreferredLanguages(fInitialPreferredLanguages
);
583 LocaleWindow::_SetPreferredLanguages(const BMessage
& languages
)
587 // Delete all existing items
588 for (int32 index
= fPreferredListView
->CountItems(); index
-- > 0; ) {
589 delete fPreferredListView
->ItemAt(index
);
591 fPreferredListView
->MakeEmpty();
594 for (int32 index
= 0;
595 languages
.FindString("language", index
, &languageID
) == B_OK
; index
++) {
597 LanguageListItem
* item
= fLanguageListView
->ItemForLanguageID(
598 languageID
.String(), &listIndex
);
600 // We found the item we were looking for, now copy it to
602 fPreferredListView
->AddItem(new LanguageListItem(*item
));
606 _EnableDisableLanguages();
612 LocaleWindow::_InsertPreferredLanguage(LanguageListItem
* item
, int32 atIndex
)
614 if (item
== NULL
|| fPreferredListView
->ItemForLanguageID(
615 item
->ID().String()) != NULL
)
619 atIndex
= fPreferredListView
->CountItems();
621 BLanguage
language(item
->Code());
622 LanguageListItem
* baseItem
623 = fPreferredListView
->ItemForLanguageCode(language
.Code(), &atIndex
);
627 fPreferredListView
->AddItem(new LanguageListItem(*item
), atIndex
);
629 // Replace other languages sharing the same base
630 if (baseItem
!= NULL
) {
631 fPreferredListView
->RemoveItem(baseItem
);
635 _PreferredLanguagesChanged();
642 LocaleWindow::_Defaults()
644 BMessage preferredLanguages
;
645 preferredLanguages
.AddString("language", "en");
646 MutableLocaleRoster::Default()->SetPreferredLanguages(&preferredLanguages
);
647 _SetPreferredLanguages(preferredLanguages
);
649 BFormattingConventions
conventions("en_US");
650 MutableLocaleRoster::Default()->SetDefaultFormattingConventions(
653 fConventionsListView
->DeselectAll();
654 if (fDefaultConventionsItem
!= NULL
) {
656 = fConventionsListView
->Superitem(fDefaultConventionsItem
);
657 if (superitem
!= NULL
&& !superitem
->IsExpanded())
658 fConventionsListView
->Expand(superitem
);
659 fConventionsListView
->Select(fConventionsListView
->IndexOf(
660 fDefaultConventionsItem
));
661 fConventionsListView
->ScrollToSelection();