| Index: chrome/browser/ui/views/tabs/base_tab_strip.cc
|
| ===================================================================
|
| --- chrome/browser/ui/views/tabs/base_tab_strip.cc (revision 76049)
|
| +++ chrome/browser/ui/views/tabs/base_tab_strip.cc (working copy)
|
| @@ -4,10 +4,16 @@
|
|
|
| #include "chrome/browser/ui/views/tabs/base_tab_strip.h"
|
|
|
| +#include <vector>
|
| +
|
| +#include "base/command_line.h"
|
| +#include "base/hash_tables.h"
|
| #include "base/logging.h"
|
| +#include "base/string_split.h"
|
| #include "chrome/browser/ui/view_ids.h"
|
| #include "chrome/browser/ui/views/tabs/dragged_tab_controller.h"
|
| #include "chrome/browser/ui/views/tabs/tab_strip_controller.h"
|
| +#include "chrome/common/chrome_switches.h"
|
| #include "views/widget/root_view.h"
|
| #include "views/window/window.h"
|
|
|
| @@ -145,6 +151,7 @@
|
|
|
| TabData d = { tab, gfx::Rect() };
|
| tab_data_.insert(tab_data_.begin() + ModelIndexToTabIndex(model_index), d);
|
| + UpdateCommonTitlePrefix();
|
|
|
| AddChildView(tab);
|
|
|
| @@ -175,6 +182,7 @@
|
| BaseTab* tab = GetBaseTabAtModelIndex(model_index);
|
| bool mini_state_changed = tab->data().mini != data.mini;
|
| tab->SetData(data);
|
| + UpdateCommonTitlePrefix();
|
| tab->SchedulePaint();
|
|
|
| if (mini_state_changed) {
|
| @@ -404,8 +412,105 @@
|
| tab_data_.erase(tab_data_.begin() + tab_data_index);
|
|
|
| delete tab;
|
| + UpdateCommonTitlePrefix();
|
| }
|
|
|
| +void BaseTabStrip::UpdateCommonTitlePrefix() {
|
| + // Hidden behind a command line flag until we want to enable it always.
|
| + if (!CommandLine::ForCurrentProcess()->HasSwitch(
|
| + switches::kElideTabTitlePrefix)) {
|
| + return;
|
| + }
|
| +
|
| + // First, we need to identify if there are identical titles,
|
| + // because we don't want to remove prefixes for those at all.
|
| + // We do it as a separate pass so that we don't need to remove
|
| + // previously parsed titles when we find a duplicate title later on.
|
| + // This set will contain the indexes of the tab that have a duplicate.
|
| + base::hash_set<int> no_prefix_tab;
|
| + // This map is used to remember the title and the index of a tab. If
|
| + // multiple tabs have the same title the index is set to -1 and the indices
|
| + // of all tabs sharing the same title are placed in no_prefix_tab.
|
| + base::hash_map<string16, int> existing_title;
|
| + for (int tab_index = 0; tab_index < tab_count(); ++tab_index) {
|
| + DCHECK(tab_data_[tab_index].tab != NULL);
|
| + if (tab_data_[tab_index].tab->data().mini ||
|
| + tab_data_[tab_index].tab->data().title.empty()) {
|
| + continue;
|
| + }
|
| + // We use pairs to test existence and insert in one shot.
|
| + std::pair<base::hash_map<string16, int>::iterator, bool> insert_result =
|
| + existing_title.insert(
|
| + std::make_pair(tab_data_[tab_index].tab->data().title, tab_index));
|
| + if (!insert_result.second) {
|
| + // We found a duplicate title, insert_result.second is false when we
|
| + // insert a duplicate in the set. insert_result.first is a map iterator
|
| + // and thus insert_result.first->first is the title key of the map.
|
| + DCHECK(tab_data_[tab_index].tab->data().title ==
|
| + insert_result.first->first);
|
| + no_prefix_tab.insert(tab_index);
|
| + // insert_result.first->second is the value of the tab index.
|
| + if (insert_result.first->second != -1) {
|
| + no_prefix_tab.insert(insert_result.first->second);
|
| + insert_result.first->second = -1;
|
| + }
|
| + }
|
| + }
|
| +
|
| + // This next loop accumulates all the potential prefixes,
|
| + // and remember on which tabs we saw them.
|
| + base::hash_map<string16, std::vector<int> > prefixes;
|
| + for (int tab_index = 0; tab_index < tab_count(); ++tab_index) {
|
| + const TabRendererData& tab_data = tab_data_[tab_index].tab->data();
|
| + // Mini, title-less, and duplicate title tabs
|
| + // are not to be included in this process.
|
| + if (tab_data.mini || tab_data.title.empty() ||
|
| + no_prefix_tab.find(tab_index) != no_prefix_tab.end()) {
|
| + continue;
|
| + }
|
| +
|
| + // We only create prefixes at word boundaries.
|
| + std::vector<string16> words;
|
| + base::SplitStringAlongWhitespace(tab_data.title, &words);
|
| + if (words.size() > 1) {
|
| + size_t end_of_word = 0;
|
| + // We stop this for loop at words.size() - 1 because we don't need to
|
| + // insert the concatenation of all the words in the prefixes map because
|
| + // we ignore the complete title matches anyway.
|
| + for (size_t word_index = 0; word_index < words.size() - 1; ++word_index) {
|
| + if (!words[word_index].empty()) {
|
| + end_of_word = tab_data.title.find(words[word_index], end_of_word) +
|
| + words[word_index].size();
|
| + prefixes[tab_data.title.substr(0, end_of_word)].push_back(tab_index);
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + // Now we parse the map to find common prefixes and set the largest per tab.
|
| + std::vector<size_t> prefix_lengths(tab_count(), 0);
|
| + for (base::hash_map<string16, std::vector<int> >::iterator iter =
|
| + prefixes.begin(); iter != prefixes.end(); ++iter) {
|
| + if (iter->second.size() > 1) { // Need more than one with same prefix.
|
| + size_t prefix_length = iter->first.size();
|
| + for (size_t index = 0; index < iter->second.size(); ++index){
|
| + if (prefix_lengths[iter->second[index]] < prefix_length) {
|
| + prefix_lengths[iter->second[index]] = prefix_length;
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + // And finally, reset the tab data for the tabs that changed.
|
| + for (int tab_index = 0; tab_index < tab_count(); ++tab_index) {
|
| + TabRendererData data = tab_data_[tab_index].tab->data();
|
| + if (data.common_prefix_length != prefix_lengths[tab_index]) {
|
| + data.common_prefix_length = prefix_lengths[tab_index];
|
| + tab_data_[tab_index].tab->SetData(data);
|
| + }
|
| + }
|
| +}
|
| +
|
| int BaseTabStrip::TabIndexOfTab(BaseTab* tab) const {
|
| for (int i = 0; i < tab_count(); ++i) {
|
| if (base_tab_at_tab_index(i) == tab)
|
|
|