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