/*
* Copyright 2005-2010, Axel Dörfler, axeld@pinc-software.de.
* Copyright 2009-2010, Adrien Destugues <pulkomandy@gmail.com>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "LocaleWindow.h"
#include <iostream>
#include <Alert.h>
#include <Application.h>
#include <Button.h>
#include <Catalog.h>
#include <CheckBox.h>
#include <ControlLook.h>
#include <FormattingConventions.h>
#include <GroupLayout.h>
#include <LayoutBuilder.h>
#include <Locale.h>
#include <MutableLocaleRoster.h>
#include <Screen.h>
#include <ScrollView.h>
#include <SeparatorView.h>
#include <StringView.h>
#include <TabView.h>
#include <UnicodeChar.h>
#include "FormatSettingsView.h"
#include "LocalePreflet.h"
#include "LanguageListView.h"
using BPrivate::MutableLocaleRoster;
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "Locale Preflet Window"
static const uint32 kMsgLanguageInvoked = 'LaIv';
static const uint32 kMsgLanguageDragged = 'LaDr';
static const uint32 kMsgPreferredLanguageInvoked = 'PLIv';
static const uint32 kMsgPreferredLanguageDragged = 'PLDr';
static const uint32 kMsgPreferredLanguageDeleted = 'PLDl';
static const uint32 kMsgConventionsSelection = 'csel';
static const uint32 kMsgDefaults = 'dflt';
static const uint32 kMsgPreferredLanguagesChanged = 'lang';
static const uint32 kMsgFilesystemTranslationChanged = 'fsys';
static int
compare_typed_list_items(const BListItem* _a, const BListItem* _b)
{
static BCollator collator;
LanguageListItem* a = (LanguageListItem*)_a;
LanguageListItem* b = (LanguageListItem*)_b;
return collator.Compare(a->Text(), b->Text());
}
// #pragma mark -
LocaleWindow::LocaleWindow()
:
BWindow(BRect(0, 0, 0, 0), B_TRANSLATE_SYSTEM_NAME("Locale"), B_TITLED_WINDOW,
B_QUIT_ON_WINDOW_CLOSE | B_ASYNCHRONOUS_CONTROLS
| B_AUTO_UPDATE_SIZE_LIMITS),
fInitialConventionsItem(NULL),
fDefaultConventionsItem(NULL),
fFilesystemTranslationCheckbox(NULL)
{
SetLayout(new BGroupLayout(B_HORIZONTAL));
float spacing = be_control_look->DefaultItemSpacing();
BTabView* tabView = new BTabView("tabview", B_WIDTH_FROM_WIDEST);
tabView->SetBorder(B_NO_BORDER);
BGroupView* languageTab = new BGroupView(B_TRANSLATE("Language"),
B_HORIZONTAL, spacing);
// first list: available languages
fLanguageListView = new LanguageListView("available",
B_MULTIPLE_SELECTION_LIST);
BScrollView* scrollView = new BScrollView("scroller", fLanguageListView,
B_WILL_DRAW | B_FRAME_EVENTS, true, true);
fLanguageListView->SetInvocationMessage(new BMessage(kMsgLanguageInvoked));
fLanguageListView->SetDragMessage(new BMessage(kMsgLanguageDragged));
fLanguageListView->SetGlobalDropTargetIndicator(true);
BFont font;
fLanguageListView->GetFont(&font);
// Fill the language list from the LocaleRoster data
BMessage availableLanguages;
if (BLocaleRoster::Default()->GetAvailableLanguages(&availableLanguages)
== B_OK) {
BString currentID;
LanguageListItem* currentToplevelItem = NULL;
for (int i = 0; availableLanguages.FindString("language", i, ¤tID)
== B_OK; i++) {
// Now get the human-readable, native name for each language
BString name;
BLanguage currentLanguage(currentID.String());
currentLanguage.GetNativeName(name);
int nameLength = name.CountChars();
bool hasGlyphs[nameLength];
font.GetHasGlyphs(name.String(), nameLength, hasGlyphs);
for (int32 i = 0; i < nameLength; ++i) {
if (!hasGlyphs[i]) {
// replace by name translated to current language
currentLanguage.GetName(name);
break;
}
}
LanguageListItem* item;
if (currentLanguage.IsCountrySpecific()) {
item = new LanguageListItemWithFlag(name, currentID.String(),
currentLanguage.Code(), currentLanguage.CountryCode());
} else {
item = new LanguageListItem(name, currentID.String(),
currentLanguage.Code());
}
if (currentLanguage.IsCountrySpecific()
&& currentToplevelItem != NULL
&& currentToplevelItem->Code() == item->Code()) {
fLanguageListView->AddUnder(item, currentToplevelItem);
} else if (currentLanguage.ScriptCode() != NULL
&& currentToplevelItem != NULL
&& currentToplevelItem->Code() == item->Code()) {
// This is a script for some language, skip it and add the
// country-specific variants to the parent directly
delete item;
} else {
// This is a generic language, add it at top-level
fLanguageListView->AddItem(item);
item->SetExpanded(false);
currentToplevelItem = item;
}
}
fLanguageListView->FullListSortItems(compare_typed_list_items);
} else {
BAlert* alert = new BAlert("Error",
B_TRANSLATE("Unable to find the available languages! You can't "
"use this preflet!"),
B_TRANSLATE("OK"), NULL, NULL,
B_WIDTH_AS_USUAL, B_OFFSET_SPACING, B_STOP_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
}
// Second list: active languages
fPreferredListView = new LanguageListView("preferred",
B_MULTIPLE_SELECTION_LIST);
BScrollView* scrollViewEnabled = new BScrollView("scroller",
fPreferredListView, B_WILL_DRAW | B_FRAME_EVENTS, true, true);
fPreferredListView->SetInvocationMessage(
new BMessage(kMsgPreferredLanguageInvoked));
fPreferredListView->SetDeleteMessage(
new BMessage(kMsgPreferredLanguageDeleted));
fPreferredListView->SetDragMessage(
new BMessage(kMsgPreferredLanguageDragged));
BLayoutBuilder::Group<>(languageTab)
.AddGroup(B_VERTICAL)
.Add(new BStringView("", B_TRANSLATE("Available languages")))
.Add(scrollView)
.End()
.AddGroup(B_VERTICAL)
.Add(new BStringView("", B_TRANSLATE("Preferred languages")))
.Add(scrollViewEnabled)
.End()
.SetInsets(B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING,
B_USE_WINDOW_SPACING, B_USE_DEFAULT_SPACING);
BView* countryTab = new BView(B_TRANSLATE("Formatting"), B_WILL_DRAW);
countryTab->SetLayout(new BGroupLayout(B_VERTICAL, 0));
fConventionsListView = new LanguageListView("formatting",
B_SINGLE_SELECTION_LIST);
scrollView = new BScrollView("scroller", fConventionsListView,
B_WILL_DRAW | B_FRAME_EVENTS, true, true);
fConventionsListView->SetSelectionMessage(
new BMessage(kMsgConventionsSelection));
// get all available formatting conventions (by language)
BFormattingConventions initialConventions;
BLocale::Default()->GetFormattingConventions(&initialConventions);
BString conventionsID;
fInitialConventionsItem = NULL;
LanguageListItem* currentToplevelItem = NULL;
for (int i = 0;
availableLanguages.FindString("language", i, &conventionsID) == B_OK;
i++) {
BFormattingConventions conventions(conventionsID);
BString conventionsName;
conventions.GetName(conventionsName);
LanguageListItem* item;
if (conventions.AreCountrySpecific()) {
item = new LanguageListItemWithFlag(conventionsName, conventionsID,
conventions.LanguageCode(), conventions.CountryCode());
} else {
item = new LanguageListItem(conventionsName, conventionsID,
conventions.LanguageCode());
}
if (!strcmp(conventionsID, "en_US"))
fDefaultConventionsItem = item;
if (conventions.AreCountrySpecific()
&& currentToplevelItem != NULL
&& currentToplevelItem->Code() == item->Code()) {
if (!strcmp(conventionsID, initialConventions.ID())) {
fConventionsListView->Expand(currentToplevelItem);
fInitialConventionsItem = item;
}
fConventionsListView->AddUnder(item, currentToplevelItem);
} else {
// This conventions-item isn't country-specific, add it at top-level
fConventionsListView->AddItem(item);
item->SetExpanded(false);
currentToplevelItem = item;
if (!strcmp(conventionsID, initialConventions.ID()))
fInitialConventionsItem = item;
}
}
fConventionsListView->FullListSortItems(compare_typed_list_items);
if (fInitialConventionsItem != NULL) {
fConventionsListView->Select(fConventionsListView->IndexOf(
fInitialConventionsItem));
}
fConventionsListView->SetExplicitMinSize(BSize(20 * be_plain_font->Size(),
B_SIZE_UNSET));
fFormatView = new FormatSettingsView();
countryTab->AddChild(BLayoutBuilder::Group<>(B_HORIZONTAL, spacing)
.AddGroup(B_VERTICAL, 3)
.Add(scrollView)
.End()
.Add(fFormatView)
.SetInsets(B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING,
B_USE_WINDOW_SPACING, B_USE_DEFAULT_SPACING));
BView* optionsTab = new BView(B_TRANSLATE("Options"), B_WILL_DRAW);
optionsTab->SetLayout(new BGroupLayout(B_VERTICAL, 0));
fFilesystemTranslationCheckbox = new BCheckBox("filesystemTranslation",
B_TRANSLATE("Translate application and folder names in Deskbar and Tracker."),
new BMessage(kMsgFilesystemTranslationChanged));
fFilesystemTranslationCheckbox->SetValue(
BLocaleRoster::Default()->IsFilesystemTranslationPreferred());
optionsTab->AddChild(BLayoutBuilder::Group<>(B_VERTICAL)
.Add(fFilesystemTranslationCheckbox)
.AddGlue()
.SetInsets(B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING,
B_USE_WINDOW_SPACING, B_USE_DEFAULT_SPACING));
tabView->AddTab(languageTab);
tabView->AddTab(countryTab);
tabView->AddTab(optionsTab);
BButton* button
= new BButton(B_TRANSLATE("Defaults"), new BMessage(kMsgDefaults));
fRevertButton
= new BButton(B_TRANSLATE("Revert"), new BMessage(kMsgRevert));
fRevertButton->SetEnabled(false);
BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
.SetInsets(0, B_USE_DEFAULT_SPACING, 0, B_USE_WINDOW_SPACING)
.Add(tabView)
.Add(new BSeparatorView(B_HORIZONTAL))
.AddGroup(B_HORIZONTAL)
.Add(button)
.Add(fRevertButton)
.SetInsets(B_USE_WINDOW_SPACING, B_USE_DEFAULT_SPACING,
B_USE_WINDOW_SPACING, 0)
.AddGlue();
_Refresh(true);
_SettingsReverted();
CenterOnScreen();
}
LocaleWindow::~LocaleWindow()
{
}
void
LocaleWindow::MessageReceived(BMessage* message)
{
switch (message->what) {
case B_LOCALE_CHANGED:
fFormatView->MessageReceived(message);
break;
case kMsgDefaults:
_Defaults();
break;
case kMsgRevert:
{
_Revert();
fFormatView->Revert();
fConventionsListView->DeselectAll();
if (fInitialConventionsItem != NULL) {
BListItem* superitem
= fConventionsListView->Superitem(fInitialConventionsItem);
if (superitem != NULL)
superitem->SetExpanded(true);
fConventionsListView->Select(fConventionsListView->IndexOf(
fInitialConventionsItem));
fConventionsListView->ScrollToSelection();
}
_SettingsReverted();
break;
}
case kMsgSettingsChanged:
_SettingsChanged();
break;
case kMsgLanguageDragged:
{
void* target = NULL;
if (message->FindPointer("drop_target", &target) != B_OK
|| target != fPreferredListView)
break;
// Add from available languages to preferred languages
int32 dropIndex;
if (message->FindInt32("drop_index", &dropIndex) != B_OK)
dropIndex = fPreferredListView->CountItems();
int32 index = 0;
for (int32 i = 0; message->FindInt32("index", i, &index) == B_OK;
i++) {
LanguageListItem* item = static_cast<LanguageListItem*>(
fLanguageListView->ItemAt(index));
_InsertPreferredLanguage(item, dropIndex++);
}
break;
}
case kMsgLanguageInvoked:
{
int32 index = 0;
for (int32 i = 0; message->FindInt32("index", i, &index) == B_OK;
i++) {
LanguageListItem* item = static_cast<LanguageListItem*>(
fLanguageListView->ItemAt(index));
_InsertPreferredLanguage(item);
}
break;
}
case kMsgPreferredLanguageDragged:
{
void* target = NULL;
if (fPreferredListView->CountItems() == 1
|| message->FindPointer("drop_target", &target) != B_OK)
break;
if (target == fPreferredListView) {
// change ordering
int32 dropIndex = message->FindInt32("drop_index");
int32 index = 0;
for (int32 i = 0;
message->FindInt32("index", i, &index) == B_OK;
i++, dropIndex++) {
if (dropIndex > index) {
dropIndex--;
index -= i;
}
BListItem* item = fPreferredListView->RemoveItem(index);
fPreferredListView->AddItem(item, dropIndex);
}
_PreferredLanguagesChanged();
break;
}
// supposed to fall through - remove item
}
case kMsgPreferredLanguageDeleted:
case kMsgPreferredLanguageInvoked:
{
if (fPreferredListView->CountItems() == 1)
break;
// Remove from preferred languages
int32 index = 0;
for (int32 i = 0; message->FindInt32("index", i, &index) == B_OK;
i++) {
delete fPreferredListView->RemoveItem(index - i);
if (message->what == kMsgPreferredLanguageDeleted) {
int32 count = fPreferredListView->CountItems();
fPreferredListView->Select(
index < count ? index : count - 1);
}
}
_PreferredLanguagesChanged();
break;
}
case kMsgConventionsSelection:
{
// Country selection changed.
// Get the new selected country from the ListView and send it to the
// main app event handler.
void* listView;
if (message->FindPointer("source", &listView) != B_OK)
break;
BListView* conventionsList = static_cast<BListView*>(listView);
if (conventionsList == NULL)
break;
LanguageListItem* item = static_cast<LanguageListItem*>
(conventionsList->ItemAt(conventionsList->CurrentSelection()));
if (item == NULL)
break;
BFormattingConventions conventions(item->ID());
MutableLocaleRoster::Default()->SetDefaultFormattingConventions(
conventions);
_SettingsChanged();
fFormatView->Refresh();
break;
}
case kMsgFilesystemTranslationChanged:
{
MutableLocaleRoster::Default()->SetFilesystemTranslationPreferred(
fFilesystemTranslationCheckbox->Value());
BAlert* alert = new BAlert(B_TRANSLATE("Locale"),
B_TRANSLATE("Deskbar and Tracker need to be restarted for this "
"change to take effect. Would you like to restart them now?"),
B_TRANSLATE("Cancel"), B_TRANSLATE("Restart"), NULL,
B_WIDTH_FROM_WIDEST, B_IDEA_ALERT);
alert->SetShortcut(0, B_ESCAPE);
alert->Go(new BInvoker(new BMessage(kMsgRestartTrackerAndDeskbar),
NULL, be_app));
break;
}
default:
BWindow::MessageReceived(message);
break;
}
}
bool
LocaleWindow::QuitRequested()
{
return true;
}
void
LocaleWindow::Show()
{
BWindow::Show();
Lock();
if (IsLocked()) {
fConventionsListView->ScrollToSelection();
Unlock();
}
}
void
LocaleWindow::_SettingsChanged()
{
fRevertButton->SetEnabled(fFormatView->IsReversible() || _IsReversible());
}
void
LocaleWindow::_SettingsReverted()
{
fRevertButton->SetEnabled(false);
}
bool
LocaleWindow::_IsReversible() const
{
BMessage preferredLanguages;
BLocaleRoster::Default()->GetPreferredLanguages(&preferredLanguages);
return !preferredLanguages.HasSameData(fInitialPreferredLanguages);
}
void
LocaleWindow::_PreferredLanguagesChanged()
{
BMessage preferredLanguages;
int index = 0;
while (index < fPreferredListView->CountItems()) {
LanguageListItem* item = static_cast<LanguageListItem*>(
fPreferredListView->ItemAt(index));
if (item != NULL)
preferredLanguages.AddString("language", item->ID());
index++;
}
MutableLocaleRoster::Default()->SetPreferredLanguages(&preferredLanguages);
_EnableDisableLanguages();
}
void
LocaleWindow::_EnableDisableLanguages()
{
DisableUpdates();
for (int32 i = 0; i < fLanguageListView->FullListCountItems(); i++) {
LanguageListItem* item = static_cast<LanguageListItem*>(
fLanguageListView->FullListItemAt(i));
bool enable = fPreferredListView->ItemForLanguageID(item->ID()) == NULL;
if (item->IsEnabled() != enable) {
item->SetEnabled(enable);
int32 visibleIndex = fLanguageListView->IndexOf(item);
if (visibleIndex >= 0) {
if (!enable)
fLanguageListView->Deselect(visibleIndex);
fLanguageListView->InvalidateItem(visibleIndex);
}
}
}
EnableUpdates();
}
void
LocaleWindow::_Refresh(bool setInitial)
{
BMessage preferredLanguages;
BLocaleRoster::Default()->GetPreferredLanguages(&preferredLanguages);
if (setInitial)
fInitialPreferredLanguages = preferredLanguages;
_SetPreferredLanguages(preferredLanguages);
}
void
LocaleWindow::_Revert()
{
_SetPreferredLanguages(fInitialPreferredLanguages);
}
void
LocaleWindow::_SetPreferredLanguages(const BMessage& languages)
{
DisableUpdates();
// Delete all existing items
for (int32 index = fPreferredListView->CountItems(); index-- > 0; ) {
delete fPreferredListView->ItemAt(index);
}
fPreferredListView->MakeEmpty();
BString languageID;
for (int32 index = 0;
languages.FindString("language", index, &languageID) == B_OK; index++) {
int32 listIndex;
LanguageListItem* item = fLanguageListView->ItemForLanguageID(
languageID.String(), &listIndex);
if (item != NULL) {
// We found the item we were looking for, now copy it to
// the other list
fPreferredListView->AddItem(new LanguageListItem(*item));
}
}
_EnableDisableLanguages();
EnableUpdates();
}
void
LocaleWindow::_InsertPreferredLanguage(LanguageListItem* item, int32 atIndex)
{
if (item == NULL || fPreferredListView->ItemForLanguageID(
item->ID().String()) != NULL)
return;
if (atIndex == -1)
atIndex = fPreferredListView->CountItems();
BLanguage language(item->Code());
LanguageListItem* baseItem
= fPreferredListView->ItemForLanguageCode(language.Code(), &atIndex);
DisableUpdates();
fPreferredListView->AddItem(new LanguageListItem(*item), atIndex);
// Replace other languages sharing the same base
if (baseItem != NULL) {
fPreferredListView->RemoveItem(baseItem);
delete baseItem;
}
_PreferredLanguagesChanged();
EnableUpdates();
}
void
LocaleWindow::_Defaults()
{
BMessage preferredLanguages;
preferredLanguages.AddString("language", "en");
MutableLocaleRoster::Default()->SetPreferredLanguages(&preferredLanguages);
_SetPreferredLanguages(preferredLanguages);
BFormattingConventions conventions("en_US");
MutableLocaleRoster::Default()->SetDefaultFormattingConventions(
conventions);
fConventionsListView->DeselectAll();
if (fDefaultConventionsItem != NULL) {
BListItem* superitem
= fConventionsListView->Superitem(fDefaultConventionsItem);
if (superitem != NULL && !superitem->IsExpanded())
fConventionsListView->Expand(superitem);
fConventionsListView->Select(fConventionsListView->IndexOf(
fDefaultConventionsItem));
fConventionsListView->ScrollToSelection();
}
}
↑ V773 Visibility scope of the 'alert' pointer was exited without releasing the memory. A memory leak is possible.