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
16namespace gtk_tree {
17
18gint 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
27gint 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
34gint 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
43void 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
59bool 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
70void 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
85TableAdapter::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
92void TableAdapter::SetModel(ui::TableModel* table_model) {
93  table_model_ = table_model;
94  table_model_->SetObserver(this);
95}
96
97bool 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
112static 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
118void 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
163int 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
178void 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
201void 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
246void 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
274void 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
282void 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
315gboolean 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
328gboolean 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
346TreeAdapter::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
362TreeAdapter::~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
368void TreeAdapter::Init() {
369  gtk_tree_store_clear(tree_store_);
370  Fill(NULL, tree_model_->GetRoot());
371}
372
373
374ui::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
382void 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
396void 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
409GtkTreePath* 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
423bool 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
435void 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
452void 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
468void 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