xref: /haiku/src/preferences/locale/LocaleWindow.cpp (revision 6aff37d1c79e20748c683ae224bd629f88a5b0be)
1 /*
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.
5  */
6 
7 
8 #include "LocaleWindow.h"
9 
10 #include <iostream>
11 
12 #include <Alert.h>
13 #include <Application.h>
14 #include <Button.h>
15 #include <Catalog.h>
16 #include <CheckBox.h>
17 #include <ControlLook.h>
18 #include <FormattingConventions.h>
19 #include <GroupLayout.h>
20 #include <LayoutBuilder.h>
21 #include <Locale.h>
22 #include <MutableLocaleRoster.h>
23 #include <Screen.h>
24 #include <ScrollView.h>
25 #include <SeparatorView.h>
26 #include <StringView.h>
27 #include <TabView.h>
28 #include <UnicodeChar.h>
29 
30 #include "FormatSettingsView.h"
31 #include "LocalePreflet.h"
32 #include "LanguageListView.h"
33 
34 
35 using BPrivate::MutableLocaleRoster;
36 
37 
38 #undef B_TRANSLATION_CONTEXT
39 #define B_TRANSLATION_CONTEXT "Locale Preflet Window"
40 
41 
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';
49 
50 static const uint32 kMsgPreferredLanguagesChanged = 'lang';
51 static const uint32 kMsgFilesystemTranslationChanged = 'fsys';
52 
53 
54 static int
55 compare_typed_list_items(const BListItem* _a, const BListItem* _b)
56 {
57 	static BCollator collator;
58 
59 	LanguageListItem* a = (LanguageListItem*)_a;
60 	LanguageListItem* b = (LanguageListItem*)_b;
61 
62 	return collator.Compare(a->Text(), b->Text());
63 }
64 
65 
66 // #pragma mark -
67 
68 LocaleWindow::LocaleWindow()
69 	:
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)
76 {
77 	SetLayout(new BGroupLayout(B_HORIZONTAL));
78 
79 	float spacing = be_control_look->DefaultItemSpacing();
80 
81 	BTabView* tabView = new BTabView("tabview", B_WIDTH_FROM_WIDEST);
82 	tabView->SetBorder(B_NO_BORDER);
83 
84 	BGroupView* languageTab = new BGroupView(B_TRANSLATE("Language"),
85 		B_HORIZONTAL, spacing);
86 
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);
92 
93 	fLanguageListView->SetInvocationMessage(new BMessage(kMsgLanguageInvoked));
94 	fLanguageListView->SetDragMessage(new BMessage(kMsgLanguageDragged));
95 	fLanguageListView->SetGlobalDropTargetIndicator(true);
96 
97 	BFont font;
98 	fLanguageListView->GetFont(&font);
99 
100 	// Fill the language list from the LocaleRoster data
101 	BMessage availableLanguages;
102 	if (BLocaleRoster::Default()->GetAvailableLanguages(&availableLanguages)
103 			== B_OK) {
104 		BString currentID;
105 		LanguageListItem* currentToplevelItem = NULL;
106 
107 		for (int i = 0; availableLanguages.FindString("language", i, &currentID)
108 				== B_OK; i++) {
109 			// Now get the human-readable, native name for each language
110 			BString name;
111 			BLanguage currentLanguage(currentID.String());
112 			currentLanguage.GetNativeName(name);
113 
114 			int nameLength = name.CountChars();
115 			bool hasGlyphs[nameLength];
116 			font.GetHasGlyphs(name.String(), nameLength, hasGlyphs);
117 			for (int32 i = 0; i < nameLength; ++i) {
118 				if (!hasGlyphs[i]) {
119 					// replace by name translated to current language
120 					currentLanguage.GetName(name);
121 					break;
122 				}
123 			}
124 
125 			LanguageListItem* item;
126 			if (currentLanguage.IsCountrySpecific()) {
127 				item = new LanguageListItemWithFlag(name, currentID.String(),
128 					currentLanguage.Code(), currentLanguage.CountryCode());
129 			} else {
130 				item = new LanguageListItem(name, currentID.String(),
131 					currentLanguage.Code());
132 			}
133 
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
143 				delete item;
144 			} else {
145 				// This is a generic language, add it at top-level
146 				fLanguageListView->AddItem(item);
147 				item->SetExpanded(false);
148 				currentToplevelItem = item;
149 			}
150 		}
151 
152 		fLanguageListView->FullListSortItems(compare_typed_list_items);
153 	} else {
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);
160 		alert->Go();
161 	}
162 
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);
168 
169 	fPreferredListView->SetInvocationMessage(
170 		new BMessage(kMsgPreferredLanguageInvoked));
171 	fPreferredListView->SetDeleteMessage(
172 		new BMessage(kMsgPreferredLanguageDeleted));
173 	fPreferredListView->SetDragMessage(
174 		new BMessage(kMsgPreferredLanguageDragged));
175 
176 	BLayoutBuilder::Group<>(languageTab)
177 		.AddGroup(B_VERTICAL)
178 			.Add(new BStringView("", B_TRANSLATE("Available languages")))
179 			.Add(scrollView)
180 			.End()
181 		.AddGroup(B_VERTICAL)
182 			.Add(new BStringView("", B_TRANSLATE("Preferred languages")))
183 			.Add(scrollViewEnabled)
184 			.End()
185 		.SetInsets(B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING,
186 			B_USE_WINDOW_SPACING, B_USE_DEFAULT_SPACING);
187 
188 	BView* countryTab = new BView(B_TRANSLATE("Formatting"), B_WILL_DRAW);
189 	countryTab->SetLayout(new BGroupLayout(B_VERTICAL, 0));
190 
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));
197 
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;
204 	for (int i = 0;
205 		availableLanguages.FindString("language", i, &conventionsID) == B_OK;
206 		i++) {
207 		BFormattingConventions conventions(conventionsID);
208 		BString conventionsName;
209 		conventions.GetName(conventionsName);
210 
211 		LanguageListItem* item;
212 		if (conventions.AreCountrySpecific()) {
213 			item = new LanguageListItemWithFlag(conventionsName, conventionsID,
214 				conventions.LanguageCode(), conventions.CountryCode());
215 		} else {
216 			item = new LanguageListItem(conventionsName, conventionsID,
217 				conventions.LanguageCode());
218 		}
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;
227 			}
228 			fConventionsListView->AddUnder(item, currentToplevelItem);
229 		} else {
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;
236 		}
237 	}
238 
239 	fConventionsListView->FullListSortItems(compare_typed_list_items);
240 	if (fInitialConventionsItem != NULL) {
241 		fConventionsListView->Select(fConventionsListView->IndexOf(
242 			fInitialConventionsItem));
243 	}
244 
245 	fConventionsListView->SetExplicitMinSize(BSize(20 * be_plain_font->Size(),
246 		B_SIZE_UNSET));
247 
248 	fFormatView = new FormatSettingsView();
249 
250 	countryTab->AddChild(BLayoutBuilder::Group<>(B_HORIZONTAL, spacing)
251 		.AddGroup(B_VERTICAL, 3)
252 			.Add(scrollView)
253 			.End()
254 		.Add(fFormatView)
255 		.SetInsets(B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING,
256 			B_USE_WINDOW_SPACING, B_USE_DEFAULT_SPACING));
257 
258 	BView* optionsTab = new BView(B_TRANSLATE("Options"), B_WILL_DRAW);
259 	optionsTab->SetLayout(new BGroupLayout(B_VERTICAL, 0));
260 
261 	fFilesystemTranslationCheckbox = new BCheckBox("filesystemTranslation",
262 		B_TRANSLATE("Translate application and folder names in Deskbar and Tracker."),
263 		new BMessage(kMsgFilesystemTranslationChanged));
264 
265 	fFilesystemTranslationCheckbox->SetValue(
266 		BLocaleRoster::Default()->IsFilesystemTranslationPreferred());
267 
268 	optionsTab->AddChild(BLayoutBuilder::Group<>(B_VERTICAL)
269 		.Add(fFilesystemTranslationCheckbox)
270 		.AddGlue()
271 		.SetInsets(B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING,
272 			B_USE_WINDOW_SPACING, B_USE_DEFAULT_SPACING));
273 
274 	tabView->AddTab(languageTab);
275 	tabView->AddTab(countryTab);
276 	tabView->AddTab(optionsTab);
277 
278 	BButton* button
279 		= new BButton(B_TRANSLATE("Defaults"), new BMessage(kMsgDefaults));
280 
281 	fRevertButton
282 		= new BButton(B_TRANSLATE("Revert"), new BMessage(kMsgRevert));
283 	fRevertButton->SetEnabled(false);
284 
285 	BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
286 		.SetInsets(0, B_USE_DEFAULT_SPACING, 0, B_USE_WINDOW_SPACING)
287 		.Add(tabView)
288 		.Add(new BSeparatorView(B_HORIZONTAL))
289 		.AddGroup(B_HORIZONTAL)
290 			.Add(button)
291 			.Add(fRevertButton)
292 			.SetInsets(B_USE_WINDOW_SPACING, B_USE_DEFAULT_SPACING,
293 				B_USE_WINDOW_SPACING, 0)
294 			.AddGlue();
295 
296 	_Refresh(true);
297 	_SettingsReverted();
298 	CenterOnScreen();
299 }
300 
301 
302 LocaleWindow::~LocaleWindow()
303 {
304 }
305 
306 
307 void
308 LocaleWindow::MessageReceived(BMessage* message)
309 {
310 	switch (message->what) {
311 		case B_LOCALE_CHANGED:
312 			fFormatView->MessageReceived(message);
313 			break;
314 
315 		case kMsgDefaults:
316 			_Defaults();
317 			break;
318 
319 		case kMsgRevert:
320 		{
321 			_Revert();
322 			fFormatView->Revert();
323 			fConventionsListView->DeselectAll();
324 			if (fInitialConventionsItem != NULL) {
325 				BListItem* superitem
326 					= fConventionsListView->Superitem(fInitialConventionsItem);
327 				if (superitem != NULL)
328 					superitem->SetExpanded(true);
329 				fConventionsListView->Select(fConventionsListView->IndexOf(
330 						fInitialConventionsItem));
331 				fConventionsListView->ScrollToSelection();
332 			}
333 			_SettingsReverted();
334 			break;
335 		}
336 
337 		case kMsgSettingsChanged:
338 			_SettingsChanged();
339 			break;
340 
341 		case kMsgLanguageDragged:
342 		{
343 			void* target = NULL;
344 			if (message->FindPointer("drop_target", &target) != B_OK
345 				|| target != fPreferredListView)
346 				break;
347 
348 			// Add from available languages to preferred languages
349 			int32 dropIndex;
350 			if (message->FindInt32("drop_index", &dropIndex) != B_OK)
351 				dropIndex = fPreferredListView->CountItems();
352 
353 			int32 index = 0;
354 			for (int32 i = 0; message->FindInt32("index", i, &index) == B_OK;
355 					i++) {
356 				LanguageListItem* item = static_cast<LanguageListItem*>(
357 					fLanguageListView->ItemAt(index));
358 				_InsertPreferredLanguage(item, dropIndex++);
359 			}
360 			break;
361 		}
362 		case kMsgLanguageInvoked:
363 		{
364 			int32 index = 0;
365 			for (int32 i = 0; message->FindInt32("index", i, &index) == B_OK;
366 					i++) {
367 				LanguageListItem* item = static_cast<LanguageListItem*>(
368 					fLanguageListView->ItemAt(index));
369 				_InsertPreferredLanguage(item);
370 			}
371 			break;
372 		}
373 
374 		case kMsgPreferredLanguageDragged:
375 		{
376 			void* target = NULL;
377 			if (fPreferredListView->CountItems() == 1
378 				|| message->FindPointer("drop_target", &target) != B_OK)
379 				break;
380 
381 			if (target == fPreferredListView) {
382 				// change ordering
383 				int32 dropIndex = message->FindInt32("drop_index");
384 				int32 index = 0;
385 				for (int32 i = 0;
386 						message->FindInt32("index", i, &index) == B_OK;
387 						i++, dropIndex++) {
388 					if (dropIndex > index) {
389 						dropIndex--;
390 						index -= i;
391 					}
392 					BListItem* item = fPreferredListView->RemoveItem(index);
393 					fPreferredListView->AddItem(item, dropIndex);
394 				}
395 
396 				_PreferredLanguagesChanged();
397 				break;
398 			}
399 
400 			// supposed to fall through - remove item
401 		}
402 		case kMsgPreferredLanguageDeleted:
403 		case kMsgPreferredLanguageInvoked:
404 		{
405 			if (fPreferredListView->CountItems() == 1)
406 				break;
407 
408 			// Remove from preferred languages
409 			int32 index = 0;
410 			for (int32 i = 0; message->FindInt32("index", i, &index) == B_OK;
411 					i++) {
412 				delete fPreferredListView->RemoveItem(index - i);
413 
414 				if (message->what == kMsgPreferredLanguageDeleted) {
415 					int32 count = fPreferredListView->CountItems();
416 					fPreferredListView->Select(
417 						index < count ? index : count - 1);
418 				}
419 			}
420 
421 			_PreferredLanguagesChanged();
422 			break;
423 		}
424 
425 		case kMsgConventionsSelection:
426 		{
427 			// Country selection changed.
428 			// Get the new selected country from the ListView and send it to the
429 			// main app event handler.
430 			void* listView;
431 			if (message->FindPointer("source", &listView) != B_OK)
432 				break;
433 
434 			BListView* conventionsList = static_cast<BListView*>(listView);
435 			if (conventionsList == NULL)
436 				break;
437 
438 			LanguageListItem* item = static_cast<LanguageListItem*>
439 				(conventionsList->ItemAt(conventionsList->CurrentSelection()));
440 			if (item == NULL)
441 				break;
442 
443 			BFormattingConventions conventions(item->ID());
444 			MutableLocaleRoster::Default()->SetDefaultFormattingConventions(
445 				conventions);
446 
447 			_SettingsChanged();
448 			fFormatView->Refresh();
449 			break;
450 		}
451 
452 		case kMsgFilesystemTranslationChanged:
453 		{
454 			MutableLocaleRoster::Default()->SetFilesystemTranslationPreferred(
455 				fFilesystemTranslationCheckbox->Value());
456 
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),
464 				NULL, be_app));
465 			break;
466 		}
467 
468 		default:
469 			BWindow::MessageReceived(message);
470 			break;
471 	}
472 }
473 
474 
475 bool
476 LocaleWindow::QuitRequested()
477 {
478 	return true;
479 }
480 
481 
482 void
483 LocaleWindow::Show()
484 {
485 	BWindow::Show();
486 
487 	Lock();
488 	if (IsLocked()) {
489 		fConventionsListView->ScrollToSelection();
490 		Unlock();
491 	}
492 }
493 
494 
495 void
496 LocaleWindow::_SettingsChanged()
497 {
498 	fRevertButton->SetEnabled(fFormatView->IsReversible() || _IsReversible());
499 }
500 
501 
502 void
503 LocaleWindow::_SettingsReverted()
504 {
505 	fRevertButton->SetEnabled(false);
506 }
507 
508 
509 bool
510 LocaleWindow::_IsReversible() const
511 {
512 	BMessage preferredLanguages;
513 	BLocaleRoster::Default()->GetPreferredLanguages(&preferredLanguages);
514 
515 	return !preferredLanguages.HasSameData(fInitialPreferredLanguages);
516 }
517 
518 
519 void
520 LocaleWindow::_PreferredLanguagesChanged()
521 {
522 	BMessage preferredLanguages;
523 	int index = 0;
524 	while (index < fPreferredListView->CountItems()) {
525 		LanguageListItem* item = static_cast<LanguageListItem*>(
526 			fPreferredListView->ItemAt(index));
527 		if (item != NULL)
528 			preferredLanguages.AddString("language", item->ID());
529 		index++;
530 	}
531 	MutableLocaleRoster::Default()->SetPreferredLanguages(&preferredLanguages);
532 
533 	_EnableDisableLanguages();
534 }
535 
536 
537 void
538 LocaleWindow::_EnableDisableLanguages()
539 {
540 	DisableUpdates();
541 
542 	for (int32 i = 0; i < fLanguageListView->FullListCountItems(); i++) {
543 		LanguageListItem* item = static_cast<LanguageListItem*>(
544 			fLanguageListView->FullListItemAt(i));
545 
546 		bool enable = fPreferredListView->ItemForLanguageID(item->ID()) == NULL;
547 		if (item->IsEnabled() != enable) {
548 			item->SetEnabled(enable);
549 
550 			int32 visibleIndex = fLanguageListView->IndexOf(item);
551 			if (visibleIndex >= 0) {
552 				if (!enable)
553 					fLanguageListView->Deselect(visibleIndex);
554 				fLanguageListView->InvalidateItem(visibleIndex);
555 			}
556 		}
557 	}
558 
559 	EnableUpdates();
560 }
561 
562 
563 void
564 LocaleWindow::_Refresh(bool setInitial)
565 {
566 	BMessage preferredLanguages;
567 	BLocaleRoster::Default()->GetPreferredLanguages(&preferredLanguages);
568 	if (setInitial)
569 		fInitialPreferredLanguages = preferredLanguages;
570 
571 	_SetPreferredLanguages(preferredLanguages);
572 }
573 
574 
575 void
576 LocaleWindow::_Revert()
577 {
578 	_SetPreferredLanguages(fInitialPreferredLanguages);
579 }
580 
581 
582 void
583 LocaleWindow::_SetPreferredLanguages(const BMessage& languages)
584 {
585 	DisableUpdates();
586 
587 	// Delete all existing items
588 	for (int32 index = fPreferredListView->CountItems(); index-- > 0; ) {
589 		delete fPreferredListView->ItemAt(index);
590 	}
591 	fPreferredListView->MakeEmpty();
592 
593 	BString languageID;
594 	for (int32 index = 0;
595 		languages.FindString("language", index, &languageID) == B_OK; index++) {
596 		int32 listIndex;
597 		LanguageListItem* item = fLanguageListView->ItemForLanguageID(
598 			languageID.String(), &listIndex);
599 		if (item != NULL) {
600 			// We found the item we were looking for, now copy it to
601 			// the other list
602 			fPreferredListView->AddItem(new LanguageListItem(*item));
603 		}
604 	}
605 
606 	_EnableDisableLanguages();
607 	EnableUpdates();
608 }
609 
610 
611 void
612 LocaleWindow::_InsertPreferredLanguage(LanguageListItem* item, int32 atIndex)
613 {
614 	if (item == NULL || fPreferredListView->ItemForLanguageID(
615 			item->ID().String()) != NULL)
616 		return;
617 
618 	if (atIndex == -1)
619 		atIndex = fPreferredListView->CountItems();
620 
621 	BLanguage language(item->Code());
622 	LanguageListItem* baseItem
623 		= fPreferredListView->ItemForLanguageCode(language.Code(), &atIndex);
624 
625 	DisableUpdates();
626 
627 	fPreferredListView->AddItem(new LanguageListItem(*item), atIndex);
628 
629 	// Replace other languages sharing the same base
630 	if (baseItem != NULL) {
631 		fPreferredListView->RemoveItem(baseItem);
632 		delete baseItem;
633 	}
634 
635 	_PreferredLanguagesChanged();
636 
637 	EnableUpdates();
638 }
639 
640 
641 void
642 LocaleWindow::_Defaults()
643 {
644 	BMessage preferredLanguages;
645 	preferredLanguages.AddString("language", "en");
646 	MutableLocaleRoster::Default()->SetPreferredLanguages(&preferredLanguages);
647 	_SetPreferredLanguages(preferredLanguages);
648 
649 	BFormattingConventions conventions("en_US");
650 	MutableLocaleRoster::Default()->SetDefaultFormattingConventions(
651 		conventions);
652 
653 	fConventionsListView->DeselectAll();
654 	if (fDefaultConventionsItem != NULL) {
655 		BListItem* superitem
656 			= fConventionsListView->Superitem(fDefaultConventionsItem);
657 		if (superitem != NULL && !superitem->IsExpanded())
658 			fConventionsListView->Expand(superitem);
659 		fConventionsListView->Select(fConventionsListView->IndexOf(
660 				fDefaultConventionsItem));
661 		fConventionsListView->ScrollToSelection();
662 	}
663 }
664