Fix #5788 by overhauling the Locale preflet a bit:

* InitiateDrag() now makes sure that either a number of top-level items
  or a group of subitems sharing the parent is being dragged - anything
  else doesn't really make sense and results in mayhem and/or crashes
* LocaleWindow now tries harder to keep the language listview sorted
* when moving around items between the two listviews, they are no longer
  copied (and partly leaked), but the same items are now just moved over
* cleanup here and there
This is still somewhat of a mess - I recommend splitting the two listviews at least (as they actually behave differently) - maybe it would be better to pick the available languages from a popup-menu instead of keeping them in a listview?

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@36561 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Oliver Tappe
2010-04-30 22:52:03 +00:00
parent f651548ee6
commit 68a1479cc6
4 changed files with 140 additions and 93 deletions
+94 -56
View File
@@ -5,6 +5,7 @@
* Authors: * Authors:
* Stephan Aßmus <[email protected]> * Stephan Aßmus <[email protected]>
* Adrien Destugues <[email protected]> * Adrien Destugues <[email protected]>
* Oliver Tappe <[email protected]>
*/ */
#include "LanguageListView.h" #include "LanguageListView.h"
@@ -39,15 +40,6 @@ LanguageListItem::LanguageListItem(const char* text, const char* code)
} }
LanguageListItem::LanguageListItem(const LanguageListItem& other)
:
BStringItem(other.Text()),
fLanguageCode(other.fLanguageCode),
fIcon(other.fIcon ? new(std::nothrow) BBitmap(other.fIcon) : NULL)
{
}
LanguageListItem::~LanguageListItem() LanguageListItem::~LanguageListItem()
{ {
delete fIcon; delete fIcon;
@@ -131,31 +123,40 @@ LanguageListView::MoveItems(BList& items, int32 index)
// or sublevels within the same top level // or sublevels within the same top level
DeselectAll(); DeselectAll();
// we remove the items while we look at them, the insertion index is
// decreaded when the items index is lower, so that we insert at the right // collect all items that must be moved (adding subitems as necessary)
// spot after removal
BList removedItems;
int32 count = items.CountItems(); int32 count = items.CountItems();
// We loop in the reverse way so we can remove childs before their parents BList itemsToBeMoved;
for (int32 i = count - 1; i >= 0; i--) { for (int32 i = 0; i < count; i++) {
BListItem* item = (BListItem*)items.ItemAt(i); BListItem* item = (BListItem*)items.ItemAt(i);
int32 removeIndex = IndexOf(item); itemsToBeMoved.AddItem(item);
// TODO : remove all childs before removing the item itself, or else if (item->OutlineLevel() == 0) {
// they will be lost forever // add all subitems, as they need to be moved, too
if (RemoveItem(item) && removedItems.AddItem((void*)item, 0)) { int32 subItemCount = CountItemsUnder(item, true);
for (int subIndex = 0; subIndex < subItemCount ; subIndex++)
itemsToBeMoved.AddItem(ItemUnderAt(item, true, subIndex));
}
}
// now remove all the items backwards (in order to remove children before
// their parent), decreasing target index if we are removing items before
// it
count = itemsToBeMoved.CountItems();
for (int32 i = count - 1; i >= 0; i--) {
BListItem* item = (BListItem*)itemsToBeMoved.ItemAt(i);
int32 removeIndex = FullListIndexOf(item);
if (RemoveItem(item)) {
if (removeIndex < index) if (removeIndex < index)
index--; index--;
} }
// else ??? -> blow up
} }
for (int32 i = 0; BListItem* item = (BListItem*)removedItems.ItemAt(i);
i++) { // finally add all the items at the given index
if (AddItem(item, index)) { for (int32 i = 0; i < count; i++) {
// after we're done, the newly inserted items will be selected BListItem* item = (BListItem*)itemsToBeMoved.ItemAt(i);
Select(index, true); if (AddItem(item, index))
// next items will be inserted after this one
index++; index++;
} else else
delete item; delete item;
} }
} }
@@ -168,12 +169,12 @@ void LanguageListView::MessageReceived (BMessage* message)
LanguageListView* list = NULL; LanguageListView* list = NULL;
if (message->FindPointer("list", (void**)&list) == B_OK) { if (message->FindPointer("list", (void**)&list) == B_OK) {
// It comes from a list // It comes from a list
if (list == this) {
// It comes from ourselves : move the item around in the list
int32 count = CountItems(); int32 count = CountItems();
if (fDropIndex < 0 || fDropIndex > count) if (fDropIndex < 0 || fDropIndex > count)
fDropIndex = count; fDropIndex = count;
if (list == this) {
// It comes from ourselves : move the item around in the list
BList items; BList items;
int32 index; int32 index;
for (int32 i = 0; for (int32 i = 0;
@@ -209,9 +210,6 @@ void LanguageListView::MessageReceived (BMessage* message)
fDropIndex = -1; fDropIndex = -1;
} else { } else {
// It comes from another list : move it here // It comes from another list : move it here
int32 count = CountItems();
if (fDropIndex < 0 || fDropIndex > count)
fDropIndex = count;
// ensure we always drop things at top-level and not // ensure we always drop things at top-level and not
// in the middle of another outline // in the middle of another outline
@@ -223,14 +221,17 @@ void LanguageListView::MessageReceived (BMessage* message)
// Item is now a top level one - we must insert just below its // Item is now a top level one - we must insert just below its
// last child // last child
fDropIndex += CountItemsUnder(FullListItemAt(fDropIndex),false) fDropIndex += CountItemsUnder(FullListItemAt(fDropIndex), true)
+ 1; + 1;
int32 indexCount;
type_code dummy;
if (message->GetInfo("index", &dummy, &indexCount) == B_OK) {
for (int32 i = indexCount - 1; i >= 0; i--) {
int32 index; int32 index;
for (int32 i = 0; if (message->FindInt32("index", i, &index) == B_OK)
message->FindInt32("index", i, &index) == B_OK; i++) {
MoveItemFrom(list, index, fDropIndex); MoveItemFrom(list, index, fDropIndex);
fDropIndex++; }
} }
fDropIndex = -1; fDropIndex = -1;
@@ -247,27 +248,36 @@ LanguageListView::MoveItemFrom(BOutlineListView* origin, int32 index,
int32 dropSpot) int32 dropSpot)
{ {
// Check that the node we are going to move is a top-level one. // Check that the node we are going to move is a top-level one.
// If not, we want his parent instead // If not, we want its parent instead
LanguageListItem* itemToMove = static_cast<LanguageListItem*>( LanguageListItem* itemToMove = static_cast<LanguageListItem*>(
origin->Superitem(origin->FullListItemAt(index))); origin->Superitem(origin->FullListItemAt(index)));
if (itemToMove == NULL) { if (itemToMove == NULL) {
itemToMove = static_cast<LanguageListItem*>(origin->FullListItemAt( itemToMove = static_cast<LanguageListItem*>(
index)); origin->FullListItemAt(index));
if (itemToMove == NULL)
return;
} else } else
index = origin->FullListIndexOf(itemToMove); index = origin->FullListIndexOf(itemToMove);
int itemCount = origin->CountItemsUnder(itemToMove, true); // collect all items that must be moved (adding subitems as necessary)
LanguageListItem* newItem = new LanguageListItem(*itemToMove); BList itemsToBeMoved;
this->AddItem(newItem, dropSpot); itemsToBeMoved.AddItem(itemToMove);
newItem->SetExpanded(itemToMove->IsExpanded()); // add all subitems, as they need to be moved, too
int32 subItemCount = origin->CountItemsUnder(itemToMove, true);
for (int i = 0; i < itemCount ; i++) { for (int32 subIndex = 0; subIndex < subItemCount; subIndex++) {
LanguageListItem* subItem = static_cast<LanguageListItem*>( itemsToBeMoved.AddItem(origin->ItemUnderAt(itemToMove, true,
origin->ItemUnderAt(itemToMove, true, i)); subIndex));
this->AddUnder(new LanguageListItem(*subItem), newItem);
} }
origin->RemoveItem(index);
// This will also remove the children // now remove all items from origin in reverse order (to remove the children
// before the parent) ...
// TODO: using RemoveItem() on the parent will delete the subitems, which
// may be a bug, actually.
int32 itemCount = itemsToBeMoved.CountItems();
for (int32 i = itemCount - 1; i >= 0; i--)
origin->RemoveItem(index + i);
// ... and add all the items to this list
AddList(&itemsToBeMoved, dropSpot);
} }
@@ -277,7 +287,7 @@ LanguageListView::InitiateDrag(BPoint point, int32 index, bool)
bool success = false; bool success = false;
BListItem* item = FullListItemAt(CurrentSelection(0)); BListItem* item = FullListItemAt(CurrentSelection(0));
if (!item) { if (!item) {
// workarround a timing problem // workaround for a timing problem
Select(index); Select(index);
item = FullListItemAt(index); item = FullListItemAt(index);
} }
@@ -285,18 +295,46 @@ LanguageListView::InitiateDrag(BPoint point, int32 index, bool)
// create drag message // create drag message
BMessage msg('DRAG'); BMessage msg('DRAG');
msg.AddPointer("list", (void*)(this)); msg.AddPointer("list", (void*)(this));
// first selection round, consider only superitems
int32 index; int32 index;
for (int32 i = 0; (index = FullListCurrentSelection(i)) >= 0; i++) for (int32 i = 0; (index = FullListCurrentSelection(i)) >= 0; i++) {
BListItem* item = FullListItemAt(index);
if (item == NULL)
return false;
if (item->OutlineLevel() == 0)
msg.AddInt32("index", index); msg.AddInt32("index", index);
}
if (!msg.HasInt32("index")) {
// second selection round, consider only subitems of the same
// (i.e. the first) parent
BListItem* seenSuperItem = NULL;
for (int32 i = 0; (index = FullListCurrentSelection(i)) >= 0; i++) {
BListItem* item = FullListItemAt(index);
if (item == NULL)
return false;
if (item->OutlineLevel() != 0) {
BListItem* superItem = Superitem(item);
if (seenSuperItem == NULL)
seenSuperItem = superItem;
if (superItem == seenSuperItem)
msg.AddInt32("index", index);
else
break;
}
}
}
// figure out drag rect // figure out drag rect
float width = Bounds().Width(); float width = Bounds().Width();
BRect dragRect(0.0, 0.0, width, -1.0); BRect dragRect(0.0, 0.0, width, -1.0);
// figure out, how many items fit into our bitmap // figure out, how many items fit into our bitmap
int32 numItems;
bool fade = false; bool fade = false;
int32 numItems;
int32 currIndex;
BListItem* item;
for (numItems = 0; for (numItems = 0;
BListItem* item = FullListItemAt(CurrentSelection(numItems)); msg.FindInt32("index", numItems, &currIndex) == B_OK
numItems++) { && (item = FullListItemAt(currIndex)) != NULL; numItems++) {
dragRect.bottom += ceilf(item->Height()) + 1.0; dragRect.bottom += ceilf(item->Height()) + 1.0;
if (dragRect.Height() > MAX_DRAG_HEIGHT) { if (dragRect.Height() > MAX_DRAG_HEIGHT) {
fade = true; fade = true;
@@ -315,7 +353,7 @@ LanguageListView::InitiateDrag(BPoint point, int32 index, bool)
itemBounds.bottom = 0.0; itemBounds.bottom = 0.0;
// let all selected items, that fit into our drag_bitmap, draw // let all selected items, that fit into our drag_bitmap, draw
for (int32 i = 0; i < numItems; i++) { for (int32 i = 0; i < numItems; i++) {
int32 index = FullListCurrentSelection(i); int32 index = msg.FindInt32("index", i);
LanguageListItem* item LanguageListItem* item
= static_cast<LanguageListItem*>(FullListItemAt(index)); = static_cast<LanguageListItem*>(FullListItemAt(index));
itemBounds.bottom = itemBounds.top + ceilf(item->Height()); itemBounds.bottom = itemBounds.top + ceilf(item->Height());
+1 -1
View File
@@ -5,6 +5,7 @@
* Authors: * Authors:
* Stephan Aßmus <[email protected]> * Stephan Aßmus <[email protected]>
* Adrien Destugues <[email protected]> * Adrien Destugues <[email protected]>
* Oliver Tappe <[email protected]>
*/ */
#ifndef __LANGUAGE_LIST_VIEW_H #ifndef __LANGUAGE_LIST_VIEW_H
#define __LANGUAGE_LIST_VIEW_H #define __LANGUAGE_LIST_VIEW_H
@@ -19,7 +20,6 @@ class LanguageListItem : public BStringItem {
public: public:
LanguageListItem(const char* text, LanguageListItem(const char* text,
const char* code); const char* code);
LanguageListItem(const LanguageListItem& other);
virtual ~LanguageListItem(); virtual ~LanguageListItem();
const BString& LanguageCode() { return fLanguageCode; } const BString& LanguageCode() { return fLanguageCode; }
+2 -5
View File
@@ -109,10 +109,8 @@ void
Settings::UpdateFrom(BMessage* message) Settings::UpdateFrom(BMessage* message)
{ {
BPoint point; BPoint point;
if (message->FindPoint("window_location", &point) == B_OK) { if (message->FindPoint("window_location", &point) == B_OK)
fMessage.RemoveName("window_location"); fMessage.ReplacePoint("window_location", point);
fMessage.AddPoint("window_location", point);
}
BString langName; BString langName;
// We make sure there is at least one string before erasing the previous // We make sure there is at least one string before erasing the previous
@@ -130,7 +128,6 @@ Settings::UpdateFrom(BMessage* message)
if (message->FindString("country", &langName) == B_OK) if (message->FindString("country", &langName) == B_OK)
fMessage.ReplaceString("country", langName); fMessage.ReplaceString("country", langName);
fUpdated = true; fUpdated = true;
} }
+28 -16
View File
@@ -43,6 +43,15 @@ compare_list_items(const void* _a, const void* _b)
} }
static int
compare_typed_list_items(const BListItem* _a, const BListItem* _b)
{
LanguageListItem* a = *(LanguageListItem**)_a;
LanguageListItem* b = *(LanguageListItem**)_b;
return strcasecmp(a->Text(), b->Text());
}
LocaleWindow::LocaleWindow() LocaleWindow::LocaleWindow()
: :
BWindow(BRect(0, 0, 0, 0), "Locale", B_TITLED_WINDOW, B_NOT_RESIZABLE BWindow(BRect(0, 0, 0, 0), "Locale", B_TITLED_WINDOW, B_NOT_RESIZABLE
@@ -84,7 +93,7 @@ LocaleWindow::LocaleWindow()
for (int i = 0; installedLanguages.FindString("langs", for (int i = 0; installedLanguages.FindString("langs",
i, &currentLanguageCode) == B_OK; i++) { i, &currentLanguageCode) == B_OK; i++) {
// Now get an human-readable, loacalized name for each language // Now get an human-readable, localized name for each language
// TODO: sort them using collators. // TODO: sort them using collators.
BLanguage* currentLanguage; BLanguage* currentLanguage;
be_locale_roster->GetLanguage(&currentLanguage, be_locale_roster->GetLanguage(&currentLanguage,
@@ -101,11 +110,17 @@ LocaleWindow::LocaleWindow()
// This is a language without country, add it at top-level // This is a language without country, add it at top-level
fLanguageListView->AddItem(si); fLanguageListView->AddItem(si);
si->SetExpanded(false); si->SetExpanded(false);
if (lastAddedLanguage != NULL) {
fLanguageListView->SortItemsUnder(lastAddedLanguage,
true, compare_typed_list_items);
}
lastAddedLanguage = si; lastAddedLanguage = si;
} }
delete currentLanguage; delete currentLanguage;
} }
fLanguageListView->SortItemsUnder(lastAddedLanguage, true,
compare_typed_list_items);
fLanguageListView->SortItems(compare_list_items); fLanguageListView->SortItems(compare_list_items);
// see previous comment on sort using collators // see previous comment on sort using collators
@@ -133,9 +148,8 @@ LocaleWindow::LocaleWindow()
BMessage msg; BMessage msg;
be_locale_roster->GetPreferredLanguages(&msg); be_locale_roster->GetPreferredLanguages(&msg);
BString langCode; BString langCode;
for (int index = 0; msg.FindString("language", index, &langCode) for (int index = 0;
== B_OK; msg.FindString("language", index, &langCode) == B_OK; index++) {
index++) {
for (int listPos = 0; LanguageListItem* lli for (int listPos = 0; LanguageListItem* lli
= static_cast<LanguageListItem*> = static_cast<LanguageListItem*>
(fLanguageListView->FullListItemAt(listPos)); (fLanguageListView->FullListItemAt(listPos));
@@ -144,8 +158,9 @@ LocaleWindow::LocaleWindow()
// We found the item we were looking for, now move it to // We found the item we were looking for, now move it to
// the other list along with all its children // the other list along with all its children
static_cast<LanguageListView*>(fPreferredListView) static_cast<LanguageListView*>(fPreferredListView)
-> MoveItemFrom(fLanguageListView, fLanguageListView ->MoveItemFrom(fLanguageListView,
-> FullListIndexOf(lli)); fLanguageListView->FullListIndexOf(lli),
fLanguageListView->CountItems());
} }
} }
} }
@@ -252,16 +267,15 @@ LocaleWindow::MessageReceived(BMessage* message)
while (index < fPreferredListView->FullListCountItems()) { while (index < fPreferredListView->FullListCountItems()) {
// only include subitems : we can guess the superitem // only include subitems : we can guess the superitem
// from them anyway // from them anyway
if (fPreferredListView->Superitem(fPreferredListView-> if (fPreferredListView->Superitem(
FullListItemAt(index)) fPreferredListView->FullListItemAt(index)) != NULL) {
!= NULL) { update.AddString("language", static_cast<LanguageListItem*>(
update.AddString("language", fPreferredListView->FullListItemAt(index))
static_cast<LanguageListItem*>
(fPreferredListView->FullListItemAt(index))
->LanguageCode()); ->LanguageCode());
} }
index++; index++;
} }
fLanguageListView->SortItems(compare_list_items);
be_app_messenger.SendMessage(&update); be_app_messenger.SendMessage(&update);
break; break;
} }
@@ -293,8 +307,7 @@ LocaleWindow::MessageReceived(BMessage* message)
= static_cast<LanguageListItem*> = static_cast<LanguageListItem*>
(fLanguageListView->RemoveItem(index)); (fLanguageListView->RemoveItem(index));
fPreferredListView->AddItem(listItem); fPreferredListView->AddItem(listItem);
fPreferredListView fPreferredListView->Invoke(fMsgPrefLanguagesChanged);
->Invoke(fMsgPrefLanguagesChanged);
} }
break; break;
} }
@@ -312,8 +325,7 @@ LocaleWindow::MessageReceived(BMessage* message)
fLanguageListView->AddItem(listItem); fLanguageListView->AddItem(listItem);
fLanguageListView->SortItems(compare_list_items); fLanguageListView->SortItems(compare_list_items);
// see previous comment on sort using collators // see previous comment on sort using collators
fPreferredListView fPreferredListView->Invoke(fMsgPrefLanguagesChanged);
->Invoke(fMsgPrefLanguagesChanged);
} }
break; break;
} }