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.
5 #include "chrome/browser/ui/gtk/gtk_tree.h"
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"
18 gint
GetRowNumForPath(GtkTreePath
* path
) {
19 gint
* indices
= gtk_tree_path_get_indices(path
);
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
);
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
);
43 void SelectAndFocusRowNum(int row
, GtkTreeView
* tree_view
) {
44 GtkTreeModel
* model
= gtk_tree_view_get_model(tree_view
);
50 if (!gtk_tree_model_iter_nth_child(model
, &iter
, NULL
, row
)) {
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
);
59 bool RemoveRecursively(GtkTreeStore
* tree_store
, GtkTreeIter
* iter
) {
61 if (gtk_tree_model_iter_children(GTK_TREE_MODEL(tree_store
), &child
, iter
)) {
63 if (!RemoveRecursively(tree_store
, &child
))
67 return gtk_tree_store_remove(tree_store
, iter
);
70 void GetSelectedIndices(GtkTreeSelection
* selection
, std::set
<int>* out
) {
71 GList
* list
= gtk_tree_selection_get_selected_rows(
74 for (node
= list
; node
!= NULL
; node
= node
->next
) {
76 gtk_tree::GetRowNumForPath(static_cast<GtkTreePath
*>(node
->data
)));
78 g_list_foreach(list
, (GFunc
)gtk_tree_path_free
, NULL
);
82 ////////////////////////////////////////////////////////////////////////////////
85 TableAdapter::TableAdapter(Delegate
* delegate
, GtkListStore
* list_store
,
86 ui::TableModel
* table_model
)
87 : delegate_(delegate
), list_store_(list_store
), table_model_(table_model
) {
89 table_model
->SetObserver(this);
92 void TableAdapter::SetModel(ui::TableModel
* table_model
) {
93 table_model_
= table_model
;
94 table_model_
->SetObserver(this);
97 bool TableAdapter::IsGroupRow(GtkTreeIter
* iter
) const {
98 if (!table_model_
->HasGroups())
100 gboolean is_header
= false;
101 gboolean is_separator
= false;
102 gtk_tree_model_get(GTK_TREE_MODEL(list_store_
),
109 return is_header
|| is_separator
;
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;
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();
125 model_rows
->insert(*it
);
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();
135 int list_store_index
= *list_store_it
;
137 bool rv
= gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(list_store_
),
146 gtk_tree_model_get(GTK_TREE_MODEL(list_store_
),
151 while (group_it
->id
!= group
) {
153 if (group_it
== groups
.end()) {
158 int offset
= OffsetForGroupIndex(group_it
- groups
.begin());
159 model_rows
->insert(list_store_index
- offset
);
163 int TableAdapter::GetListStoreIndexForModelRow(int model_row
) const {
164 if (!table_model_
->HasGroups())
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());
178 void TableAdapter::AddNodeToList(int row
) {
180 int list_store_index
= GetListStoreIndexForModelRow(row
);
181 if (list_store_index
== 0) {
182 gtk_list_store_prepend(list_store_
, &iter
);
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
);
190 if (table_model_
->HasGroups()) {
191 gtk_list_store_set(list_store_
,
193 COL_WEIGHT
, PANGO_WEIGHT_NORMAL
,
194 COL_WEIGHT_SET
, TRUE
,
195 COL_GROUP_ID
, table_model_
->GetGroupID(row
),
198 delegate_
->SetColumnValues(row
, &iter
);
201 void TableAdapter::OnModelChanged() {
202 delegate_
->OnAnyModelUpdateStart();
203 gtk_list_store_clear(list_store_
);
204 delegate_
->OnModelChanged();
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
) {
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);
217 gtk_list_store_append(list_store_
, &iter
);
218 gtk_list_store_set(list_store_
,
225 base::UTF16ToUTF8(it
->title
).c_str(),
230 gtk_list_store_append(list_store_
, &iter
);
231 gtk_list_store_set(list_store_
,
241 for (int i
= 0; i
< table_model_
->RowCount(); ++i
)
243 delegate_
->OnAnyModelUpdate();
246 void TableAdapter::OnItemsChanged(int start
, int length
) {
249 delegate_
->OnAnyModelUpdateStart();
250 int list_store_index
= GetListStoreIndexForModelRow(start
);
252 bool rv
= gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(list_store_
),
256 for (int i
= 0; i
< length
; ++i
) {
261 while (IsGroupRow(&iter
)) {
262 rv
= gtk_tree_model_iter_next(GTK_TREE_MODEL(list_store_
), &iter
);
268 delegate_
->SetColumnValues(start
+ i
, &iter
);
269 rv
= gtk_tree_model_iter_next(GTK_TREE_MODEL(list_store_
), &iter
);
271 delegate_
->OnAnyModelUpdate();
274 void TableAdapter::OnItemsAdded(int start
, int length
) {
275 delegate_
->OnAnyModelUpdateStart();
276 for (int i
= 0; i
< length
; ++i
) {
277 AddNodeToList(start
+ i
);
279 delegate_
->OnAnyModelUpdate();
282 void TableAdapter::OnItemsRemoved(int start
, int length
) {
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;
291 list_store_index
= GetListStoreIndexForModelRow(start
- 1) + 1;
293 bool rv
= gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(list_store_
),
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
);
309 gtk_list_store_remove(list_store_
, &iter
);
311 delegate_
->OnAnyModelUpdate();
315 gboolean
TableAdapter::OnCheckRowIsSeparator(GtkTreeModel
* model
,
317 gpointer user_data
) {
318 gboolean is_separator
;
319 gtk_tree_model_get(model
,
328 gboolean
TableAdapter::OnSelectionFilter(GtkTreeSelection
* selection
,
331 gboolean path_currently_selected
,
332 gpointer user_data
) {
334 if (!gtk_tree_model_get_iter(model
, &iter
, path
)) {
339 gtk_tree_model_get(model
, &iter
, COL_IS_HEADER
, &is_header
, -1);
343 ////////////////////////////////////////////////////////////////////////////////
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
,
353 tree_model
->AddObserver(this);
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()));
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
]);
368 void TreeAdapter::Init() {
369 gtk_tree_store_clear(tree_store_
);
370 Fill(NULL
, tree_model_
->GetRoot());
374 ui::TreeModelNode
* TreeAdapter::GetNode(GtkTreeIter
* iter
) {
375 ui::TreeModelNode
* node
;
376 gtk_tree_model_get(GTK_TREE_MODEL(tree_store_
), iter
,
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
];
388 pixbuf
= GtkThemeService::GetFolderIcon(true).ToGdkPixbuf();
389 gtk_tree_store_set(tree_store_
, iter
,
391 COL_TITLE
, base::UTF16ToUTF8(node
->GetTitle()).c_str(),
396 void TreeAdapter::Fill(GtkTreeIter
* parent_iter
,
397 ui::TreeModelNode
* parent_node
) {
399 FillRow(parent_iter
, parent_node
);
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
);
409 GtkTreePath
* TreeAdapter::GetTreePath(ui::TreeModelNode
* node
) {
410 GtkTreePath
* path
= gtk_tree_path_new();
411 ui::TreeModelNode
* parent
= node
;
413 parent
= tree_model_
->GetParent(parent
);
415 int idx
= tree_model_
->GetIndexOf(parent
, node
);
416 gtk_tree_path_prepend_index(path
, idx
);
423 bool TreeAdapter::GetTreeIter(ui::TreeModelNode
* node
, GtkTreeIter
* iter
) {
424 GtkTreePath
* path
= GetTreePath(node
);
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
);
435 void TreeAdapter::TreeNodesAdded(ui::TreeModel
* model
,
436 ui::TreeModelNode
* parent
,
439 delegate_
->OnAnyModelUpdateStart();
440 GtkTreeIter parent_iter
;
441 GtkTreeIter
* parent_iter_ptr
= NULL
;
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
));
449 delegate_
->OnAnyModelUpdate();
452 void TreeAdapter::TreeNodesRemoved(ui::TreeModel
* model
,
453 ui::TreeModelNode
* parent
,
456 delegate_
->OnAnyModelUpdateStart();
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
);
465 delegate_
->OnAnyModelUpdate();
468 void TreeAdapter::TreeNodeChanged(ui::TreeModel
* model
,
469 ui::TreeModelNode
* node
) {
470 delegate_
->OnAnyModelUpdateStart();
472 if (GetTreeIter(node
, &iter
))
473 FillRow(&iter
, node
);
474 delegate_
->OnAnyModelUpdate();
477 } // namespace gtk_tree