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
14namespace gtk_tree {
15
16gint 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
25gint 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
32gint 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
41void 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
57bool 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
68void 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
83TableAdapter::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
90void TableAdapter::SetModel(ui::TableModel* table_model) {
91  table_model_ = table_model;
92  table_model_->SetObserver(this);
93}
94
95bool 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
110static 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
116void 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
161int 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
176void 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
199void 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
244void 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
272void 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
280void 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
313gboolean 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
326gboolean 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
344TreeAdapter::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
360TreeAdapter::~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
366void TreeAdapter::Init() {
367  gtk_tree_store_clear(tree_store_);
368  Fill(NULL, tree_model_->GetRoot());
369}
370
371
372ui::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
380void 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
394void 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
407GtkTreePath* 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
421bool 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
433void 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
450void 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
466void 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