• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
GetRowNumForPath(GtkTreePath * path)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 
GetRowNumForIter(GtkTreeModel * model,GtkTreeIter * iter)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 
GetTreeSortChildRowNumForPath(GtkTreeModel * sort_model,GtkTreePath * sort_path)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 
SelectAndFocusRowNum(int row,GtkTreeView * tree_view)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 
RemoveRecursively(GtkTreeStore * tree_store,GtkTreeIter * iter)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 
GetSelectedIndices(GtkTreeSelection * selection,std::set<int> * out)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 
TableAdapter(Delegate * delegate,GtkListStore * list_store,ui::TableModel * table_model)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 
SetModel(ui::TableModel * table_model)90 void TableAdapter::SetModel(ui::TableModel* table_model) {
91   table_model_ = table_model;
92   table_model_->SetObserver(this);
93 }
94 
IsGroupRow(GtkTreeIter * iter) const95 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 
OffsetForGroupIndex(size_t group_index)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 
MapListStoreIndicesToModelRows(const std::set<int> & list_store_indices,RemoveRowsTableModel::Rows * model_rows)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 
GetListStoreIndexForModelRow(int model_row) const161 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 
AddNodeToList(int row)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 
OnModelChanged()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 
OnItemsChanged(int start,int length)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 
OnItemsAdded(int start,int length)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 
OnItemsRemoved(int start,int length)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
OnCheckRowIsSeparator(GtkTreeModel * model,GtkTreeIter * iter,gpointer user_data)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
OnSelectionFilter(GtkTreeSelection * selection,GtkTreeModel * model,GtkTreePath * path,gboolean path_currently_selected,gpointer user_data)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 
TreeAdapter(Delegate * delegate,ui::TreeModel * tree_model)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 
~TreeAdapter()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 
Init()366 void TreeAdapter::Init() {
367   gtk_tree_store_clear(tree_store_);
368   Fill(NULL, tree_model_->GetRoot());
369 }
370 
371 
GetNode(GtkTreeIter * iter)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 
FillRow(GtkTreeIter * iter,ui::TreeModelNode * node)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 
Fill(GtkTreeIter * parent_iter,ui::TreeModelNode * parent_node)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 
GetTreePath(ui::TreeModelNode * node)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 
GetTreeIter(ui::TreeModelNode * node,GtkTreeIter * iter)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 
TreeNodesAdded(ui::TreeModel * model,ui::TreeModelNode * parent,int start,int count)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 
TreeNodesRemoved(ui::TreeModel * model,ui::TreeModelNode * parent,int start,int count)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 
TreeNodeChanged(ui::TreeModel * model,ui::TreeModelNode * node)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