Upstream version 7.35.144.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / gtk / gtk_tree.cc
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
16 namespace gtk_tree {
17
18 gint 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
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);
31   return row;
32 }
33
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);
40   return row;
41 }
42
43 void 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
59 bool 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
70 void 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
85 TableAdapter::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
92 void TableAdapter::SetModel(ui::TableModel* table_model) {
93   table_model_ = table_model;
94   table_model_->SetObserver(this);
95 }
96
97 bool 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
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;
116 }
117
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();
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
163 int 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
178 void 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
201 void 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                          base::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
246 void 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
274 void 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
282 void 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
315 gboolean 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
328 gboolean 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
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,
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
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]);
366 }
367
368 void TreeAdapter::Init() {
369   gtk_tree_store_clear(tree_store_);
370   Fill(NULL, tree_model_->GetRoot());
371 }
372
373
374 ui::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
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];
387   else
388     pixbuf = GtkThemeService::GetFolderIcon(true).ToGdkPixbuf();
389   gtk_tree_store_set(tree_store_, iter,
390                      COL_ICON, pixbuf,
391                      COL_TITLE, base::UTF16ToUTF8(node->GetTitle()).c_str(),
392                      COL_NODE_PTR, node,
393                      -1);
394 }
395
396 void 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
409 GtkTreePath* 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
423 bool 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
435 void 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
452 void 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
468 void 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