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