Home | History | Annotate | Download | only in gtk
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/ui/gtk/gtk_tree.h"
      6 
      7 #include "base/logging.h"
      8 #include "base/utf_string_conversions.h"
      9 #include "chrome/browser/ui/gtk/gtk_theme_service.h"
     10 #include "third_party/skia/include/core/SkBitmap.h"
     11 #include "ui/base/models/table_model.h"
     12 #include "ui/gfx/gtk_util.h"
     13 
     14 namespace gtk_tree {
     15 
     16 gint GetRowNumForPath(GtkTreePath* path) {
     17   gint* indices = gtk_tree_path_get_indices(path);
     18   if (!indices) {
     19     NOTREACHED();
     20     return -1;
     21   }
     22   return indices[0];
     23 }
     24 
     25 gint GetRowNumForIter(GtkTreeModel* model, GtkTreeIter* iter) {
     26   GtkTreePath* path = gtk_tree_model_get_path(model, iter);
     27   int row = GetRowNumForPath(path);
     28   gtk_tree_path_free(path);
     29   return row;
     30 }
     31 
     32 gint GetTreeSortChildRowNumForPath(GtkTreeModel* sort_model,
     33                                    GtkTreePath* sort_path) {
     34   GtkTreePath *child_path = gtk_tree_model_sort_convert_path_to_child_path(
     35       GTK_TREE_MODEL_SORT(sort_model), sort_path);
     36   int row = GetRowNumForPath(child_path);
     37   gtk_tree_path_free(child_path);
     38   return row;
     39 }
     40 
     41 void SelectAndFocusRowNum(int row, GtkTreeView* tree_view) {
     42   GtkTreeModel* model = gtk_tree_view_get_model(tree_view);
     43   if (!model) {
     44     NOTREACHED();
     45     return;
     46   }
     47   GtkTreeIter iter;
     48   if (!gtk_tree_model_iter_nth_child(model, &iter, NULL, row)) {
     49     NOTREACHED();
     50     return;
     51   }
     52   GtkTreePath* path = gtk_tree_model_get_path(model, &iter);
     53   gtk_tree_view_set_cursor(tree_view, path, NULL, FALSE);
     54   gtk_tree_path_free(path);
     55 }
     56 
     57 bool RemoveRecursively(GtkTreeStore* tree_store, GtkTreeIter* iter) {
     58   GtkTreeIter child;
     59   if (gtk_tree_model_iter_children(GTK_TREE_MODEL(tree_store), &child, iter)) {
     60     while (true) {
     61       if (!RemoveRecursively(tree_store, &child))
     62         break;
     63     }
     64   }
     65   return gtk_tree_store_remove(tree_store, iter);
     66 }
     67 
     68 void GetSelectedIndices(GtkTreeSelection* selection, std::set<int>* out) {
     69   GList* list = gtk_tree_selection_get_selected_rows(
     70       selection, NULL);
     71   GList* node;
     72   for (node = list; node != NULL; node = node->next) {
     73     out->insert(
     74         gtk_tree::GetRowNumForPath(static_cast<GtkTreePath*>(node->data)));
     75   }
     76   g_list_foreach(list, (GFunc)gtk_tree_path_free, NULL);
     77   g_list_free(list);
     78 }
     79 
     80 ////////////////////////////////////////////////////////////////////////////////
     81 //  TableAdapter
     82 
     83 TableAdapter::TableAdapter(Delegate* delegate, GtkListStore* list_store,
     84                            ui::TableModel* table_model)
     85     : delegate_(delegate), list_store_(list_store), table_model_(table_model) {
     86   if (table_model)
     87     table_model->SetObserver(this);
     88 }
     89 
     90 void TableAdapter::SetModel(ui::TableModel* table_model) {
     91   table_model_ = table_model;
     92   table_model_->SetObserver(this);
     93 }
     94 
     95 bool TableAdapter::IsGroupRow(GtkTreeIter* iter) const {
     96   if (!table_model_->HasGroups())
     97     return false;
     98   gboolean is_header = false;
     99   gboolean is_separator = false;
    100   gtk_tree_model_get(GTK_TREE_MODEL(list_store_),
    101                      iter,
    102                      COL_IS_HEADER,
    103                      &is_header,
    104                      COL_IS_SEPARATOR,
    105                      &is_separator,
    106                      -1);
    107   return is_header || is_separator;
    108 }
    109 
    110 static int OffsetForGroupIndex(size_t group_index) {
    111   // Every group consists of a header and a separator row, and there is a blank
    112   // row between groups.
    113   return 3 * group_index + 2;
    114 }
    115 
    116 void TableAdapter::MapListStoreIndicesToModelRows(
    117     const std::set<int>& list_store_indices,
    118     RemoveRowsTableModel::Rows* model_rows) {
    119   if (!table_model_->HasGroups()) {
    120     for (std::set<int>::const_iterator it = list_store_indices.begin();
    121          it != list_store_indices.end();
    122          ++it) {
    123       model_rows->insert(*it);
    124     }
    125     return;
    126   }
    127 
    128   const ui::TableModel::Groups& groups = table_model_->GetGroups();
    129   ui::TableModel::Groups::const_iterator group_it = groups.begin();
    130   for (std::set<int>::const_iterator list_store_it = list_store_indices.begin();
    131        list_store_it != list_store_indices.end();
    132        ++list_store_it) {
    133     int list_store_index = *list_store_it;
    134     GtkTreeIter iter;
    135     bool rv = gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(list_store_),
    136                                        &iter,
    137                                        NULL,
    138                                        list_store_index);
    139     if (!rv) {
    140       NOTREACHED();
    141       return;
    142     }
    143     int group = -1;
    144     gtk_tree_model_get(GTK_TREE_MODEL(list_store_),
    145                        &iter,
    146                        COL_GROUP_ID,
    147                        &group,
    148                        -1);
    149     while (group_it->id != group) {
    150       ++group_it;
    151       if (group_it == groups.end()) {
    152         NOTREACHED();
    153         return;
    154       }
    155     }
    156     int offset = OffsetForGroupIndex(group_it - groups.begin());
    157     model_rows->insert(list_store_index - offset);
    158   }
    159 }
    160 
    161 int TableAdapter::GetListStoreIndexForModelRow(int model_row) const {
    162   if (!table_model_->HasGroups())
    163     return model_row;
    164   int group = table_model_->GetGroupID(model_row);
    165   const ui::TableModel::Groups& groups = table_model_->GetGroups();
    166   for (ui::TableModel::Groups::const_iterator it = groups.begin();
    167        it != groups.end(); ++it) {
    168     if (it->id == group) {
    169       return model_row + OffsetForGroupIndex(it - groups.begin());
    170     }
    171   }
    172   NOTREACHED();
    173   return -1;
    174 }
    175 
    176 void TableAdapter::AddNodeToList(int row) {
    177   GtkTreeIter iter;
    178   int list_store_index = GetListStoreIndexForModelRow(row);
    179   if (list_store_index == 0) {
    180     gtk_list_store_prepend(list_store_, &iter);
    181   } else {
    182     GtkTreeIter sibling;
    183     gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(list_store_), &sibling, NULL,
    184                                   list_store_index - 1);
    185     gtk_list_store_insert_after(list_store_, &iter, &sibling);
    186   }
    187 
    188   if (table_model_->HasGroups()) {
    189     gtk_list_store_set(list_store_,
    190                        &iter,
    191                        COL_WEIGHT, PANGO_WEIGHT_NORMAL,
    192                        COL_WEIGHT_SET, TRUE,
    193                        COL_GROUP_ID, table_model_->GetGroupID(row),
    194                        -1);
    195   }
    196   delegate_->SetColumnValues(row, &iter);
    197 }
    198 
    199 void TableAdapter::OnModelChanged() {
    200   delegate_->OnAnyModelUpdateStart();
    201   gtk_list_store_clear(list_store_);
    202   delegate_->OnModelChanged();
    203 
    204   if (table_model_->HasGroups()) {
    205     const ui::TableModel::Groups& groups = table_model_->GetGroups();
    206     for (ui::TableModel::Groups::const_iterator it = groups.begin();
    207          it != groups.end(); ++it) {
    208       GtkTreeIter iter;
    209       if (it != groups.begin()) {
    210         // Blank row between groups.
    211         gtk_list_store_append(list_store_, &iter);
    212         gtk_list_store_set(list_store_, &iter, COL_IS_HEADER, TRUE, -1);
    213       }
    214       // Group title.
    215       gtk_list_store_append(list_store_, &iter);
    216       gtk_list_store_set(list_store_,
    217                          &iter,
    218                          COL_WEIGHT,
    219                          PANGO_WEIGHT_BOLD,
    220                          COL_WEIGHT_SET,
    221                          TRUE,
    222                          COL_TITLE,
    223                          UTF16ToUTF8(it->title).c_str(),
    224                          COL_IS_HEADER,
    225                          TRUE,
    226                          -1);
    227       // Group separator.
    228       gtk_list_store_append(list_store_, &iter);
    229       gtk_list_store_set(list_store_,
    230                          &iter,
    231                          COL_IS_HEADER,
    232                          TRUE,
    233                          COL_IS_SEPARATOR,
    234                          TRUE,
    235                          -1);
    236     }
    237   }
    238 
    239   for (int i = 0; i < table_model_->RowCount(); ++i)
    240     AddNodeToList(i);
    241   delegate_->OnAnyModelUpdate();
    242 }
    243 
    244 void TableAdapter::OnItemsChanged(int start, int length) {
    245   if (length == 0)
    246     return;
    247   delegate_->OnAnyModelUpdateStart();
    248   int list_store_index = GetListStoreIndexForModelRow(start);
    249   GtkTreeIter iter;
    250   bool rv = gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(list_store_),
    251                                           &iter,
    252                                           NULL,
    253                                           list_store_index);
    254   for (int i = 0; i < length; ++i) {
    255     if (!rv) {
    256       NOTREACHED();
    257       return;
    258     }
    259     while (IsGroupRow(&iter)) {
    260       rv = gtk_tree_model_iter_next(GTK_TREE_MODEL(list_store_), &iter);
    261       if (!rv) {
    262         NOTREACHED();
    263         return;
    264       }
    265     }
    266     delegate_->SetColumnValues(start + i, &iter);
    267     rv = gtk_tree_model_iter_next(GTK_TREE_MODEL(list_store_), &iter);
    268   }
    269   delegate_->OnAnyModelUpdate();
    270 }
    271 
    272 void TableAdapter::OnItemsAdded(int start, int length) {
    273   delegate_->OnAnyModelUpdateStart();
    274   for (int i = 0; i < length; ++i) {
    275     AddNodeToList(start + i);
    276   }
    277   delegate_->OnAnyModelUpdate();
    278 }
    279 
    280 void TableAdapter::OnItemsRemoved(int start, int length) {
    281   if (length == 0)
    282     return;
    283   delegate_->OnAnyModelUpdateStart();
    284   // When this method is called, the model has already removed the items, so
    285   // accessing items in the model from |start| on may not be possible anymore.
    286   // Therefore we use the item right before that, if it exists.
    287   int list_store_index = 0;
    288   if (start > 0)
    289     list_store_index = GetListStoreIndexForModelRow(start - 1) + 1;
    290   GtkTreeIter iter;
    291   bool rv = gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(list_store_),
    292                                           &iter,
    293                                           NULL,
    294                                           list_store_index);
    295   if (!rv) {
    296     NOTREACHED();
    297     return;
    298   }
    299   for (int i = 0; i < length; ++i) {
    300     while (IsGroupRow(&iter)) {
    301       rv = gtk_tree_model_iter_next(GTK_TREE_MODEL(list_store_), &iter);
    302       if (!rv) {
    303         NOTREACHED();
    304         return;
    305       }
    306     }
    307     gtk_list_store_remove(list_store_, &iter);
    308   }
    309   delegate_->OnAnyModelUpdate();
    310 }
    311 
    312 // static
    313 gboolean TableAdapter::OnCheckRowIsSeparator(GtkTreeModel* model,
    314                                              GtkTreeIter* iter,
    315                                              gpointer user_data) {
    316   gboolean is_separator;
    317   gtk_tree_model_get(model,
    318                      iter,
    319                      COL_IS_SEPARATOR,
    320                      &is_separator,
    321                      -1);
    322   return is_separator;
    323 }
    324 
    325 // static
    326 gboolean TableAdapter::OnSelectionFilter(GtkTreeSelection* selection,
    327                                          GtkTreeModel* model,
    328                                          GtkTreePath* path,
    329                                          gboolean path_currently_selected,
    330                                          gpointer user_data) {
    331   GtkTreeIter iter;
    332   if (!gtk_tree_model_get_iter(model, &iter, path)) {
    333     NOTREACHED();
    334     return TRUE;
    335   }
    336   gboolean is_header;
    337   gtk_tree_model_get(model, &iter, COL_IS_HEADER, &is_header, -1);
    338   return !is_header;
    339 }
    340 
    341 ////////////////////////////////////////////////////////////////////////////////
    342 //  TreeAdapter
    343 
    344 TreeAdapter::TreeAdapter(Delegate* delegate, ui::TreeModel* tree_model)
    345     : delegate_(delegate),
    346       tree_model_(tree_model) {
    347   tree_store_ = gtk_tree_store_new(COL_COUNT,
    348                                    GDK_TYPE_PIXBUF,
    349                                    G_TYPE_STRING,
    350                                    G_TYPE_POINTER);
    351   tree_model->AddObserver(this);
    352 
    353   std::vector<SkBitmap> icons;
    354   tree_model->GetIcons(&icons);
    355   for (size_t i = 0; i < icons.size(); ++i) {
    356     pixbufs_.push_back(gfx::GdkPixbufFromSkBitmap(&icons[i]));
    357   }
    358 }
    359 
    360 TreeAdapter::~TreeAdapter() {
    361   g_object_unref(tree_store_);
    362   for (size_t i = 0; i < pixbufs_.size(); ++i)
    363     g_object_unref(pixbufs_[i]);
    364 }
    365 
    366 void TreeAdapter::Init() {
    367   gtk_tree_store_clear(tree_store_);
    368   Fill(NULL, tree_model_->GetRoot());
    369 }
    370 
    371 
    372 ui::TreeModelNode* TreeAdapter::GetNode(GtkTreeIter* iter) {
    373   ui::TreeModelNode* node;
    374   gtk_tree_model_get(GTK_TREE_MODEL(tree_store_), iter,
    375                      COL_NODE_PTR, &node,
    376                      -1);
    377   return node;
    378 }
    379 
    380 void TreeAdapter::FillRow(GtkTreeIter* iter, ui::TreeModelNode* node) {
    381   GdkPixbuf* pixbuf = NULL;
    382   int icon_index = tree_model_->GetIconIndex(node);
    383   if (icon_index >= 0 && icon_index < static_cast<int>(pixbufs_.size()))
    384     pixbuf = pixbufs_[icon_index];
    385   else
    386     pixbuf = GtkThemeService::GetFolderIcon(true);
    387   gtk_tree_store_set(tree_store_, iter,
    388                      COL_ICON, pixbuf,
    389                      COL_TITLE, UTF16ToUTF8(node->GetTitle()).c_str(),
    390                      COL_NODE_PTR, node,
    391                      -1);
    392 }
    393 
    394 void TreeAdapter::Fill(GtkTreeIter* parent_iter,
    395                        ui::TreeModelNode* parent_node) {
    396   if (parent_iter)
    397     FillRow(parent_iter, parent_node);
    398   GtkTreeIter iter;
    399   int child_count = tree_model_->GetChildCount(parent_node);
    400   for (int i = 0; i < child_count; ++i) {
    401     ui::TreeModelNode* node = tree_model_->GetChild(parent_node, i);
    402     gtk_tree_store_append(tree_store_, &iter, parent_iter);
    403     Fill(&iter, node);
    404   }
    405 }
    406 
    407 GtkTreePath* TreeAdapter::GetTreePath(ui::TreeModelNode* node) {
    408   GtkTreePath* path = gtk_tree_path_new();
    409   ui::TreeModelNode* parent = node;
    410   while (parent) {
    411     parent = tree_model_->GetParent(parent);
    412     if (parent) {
    413       int idx = tree_model_->GetIndexOf(parent, node);
    414       gtk_tree_path_prepend_index(path, idx);
    415       node = parent;
    416     }
    417   }
    418   return path;
    419 }
    420 
    421 bool TreeAdapter::GetTreeIter(ui::TreeModelNode* node, GtkTreeIter* iter) {
    422   GtkTreePath* path = GetTreePath(node);
    423   bool rv = false;
    424   // Check the path ourselves since gtk_tree_model_get_iter prints a warning if
    425   // given an empty path.  The path will be empty when it points to the root
    426   // node and we are using SetRootShown(false).
    427   if (gtk_tree_path_get_depth(path) > 0)
    428     rv = gtk_tree_model_get_iter(GTK_TREE_MODEL(tree_store_), iter, path);
    429   gtk_tree_path_free(path);
    430   return rv;
    431 }
    432 
    433 void TreeAdapter::TreeNodesAdded(ui::TreeModel* model,
    434                                  ui::TreeModelNode* parent,
    435                                  int start,
    436                                  int count) {
    437   delegate_->OnAnyModelUpdateStart();
    438   GtkTreeIter parent_iter;
    439   GtkTreeIter* parent_iter_ptr = NULL;
    440   GtkTreeIter iter;
    441   if (GetTreeIter(parent, &parent_iter))
    442     parent_iter_ptr = &parent_iter;
    443   for (int i = 0; i < count; ++i) {
    444     gtk_tree_store_insert(tree_store_, &iter, parent_iter_ptr, start + i);
    445     Fill(&iter, tree_model_->GetChild(parent, start + i));
    446   }
    447   delegate_->OnAnyModelUpdate();
    448 }
    449 
    450 void TreeAdapter::TreeNodesRemoved(ui::TreeModel* model,
    451                                    ui::TreeModelNode* parent,
    452                                    int start,
    453                                    int count) {
    454   delegate_->OnAnyModelUpdateStart();
    455   GtkTreeIter iter;
    456   GtkTreePath* path = GetTreePath(parent);
    457   gtk_tree_path_append_index(path, start);
    458   gtk_tree_model_get_iter(GTK_TREE_MODEL(tree_store_), &iter, path);
    459   gtk_tree_path_free(path);
    460   for (int i = 0; i < count; ++i) {
    461     RemoveRecursively(tree_store_, &iter);
    462   }
    463   delegate_->OnAnyModelUpdate();
    464 }
    465 
    466 void TreeAdapter::TreeNodeChanged(ui::TreeModel* model,
    467                                   ui::TreeModelNode* node) {
    468   delegate_->OnAnyModelUpdateStart();
    469   GtkTreeIter iter;
    470   if (GetTreeIter(node, &iter))
    471     FillRow(&iter, node);
    472   delegate_->OnAnyModelUpdate();
    473 }
    474 
    475 }  // namespace gtk_tree
    476