OLD | NEW |
| (Empty) |
1 // Copyright (c) 2010 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/gtk/gtk_tree.h" | |
6 | |
7 #include "app/table_model.h" | |
8 #include "base/logging.h" | |
9 #include "base/utf_string_conversions.h" | |
10 #include "chrome/browser/gtk/gtk_theme_provider.h" | |
11 #include "gfx/gtk_util.h" | |
12 #include "third_party/skia/include/core/SkBitmap.h" | |
13 | |
14 namespace gtk_tree { | |
15 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
83 TableAdapter::TableAdapter(Delegate* delegate, GtkListStore* list_store, | |
84 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 | |
90 void TableAdapter::SetModel(TableModel* table_model) { | |
91 table_model_ = table_model; | |
92 table_model_->SetObserver(this); | |
93 } | |
94 | |
95 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 | |
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 | |
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 TableModel::Groups& groups = table_model_->GetGroups(); | |
129 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 | |
161 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 TableModel::Groups& groups = table_model_->GetGroups(); | |
166 for (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 | |
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 | |
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 TableModel::Groups& groups = table_model_->GetGroups(); | |
206 for (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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
344 TreeAdapter::TreeAdapter(Delegate* delegate, 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 | |
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 | |
366 void TreeAdapter::Init() { | |
367 gtk_tree_store_clear(tree_store_); | |
368 Fill(NULL, tree_model_->GetRoot()); | |
369 } | |
370 | |
371 | |
372 TreeModelNode* TreeAdapter::GetNode(GtkTreeIter* iter) { | |
373 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 | |
380 void TreeAdapter::FillRow(GtkTreeIter* iter, 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 = GtkThemeProvider::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 | |
394 void TreeAdapter::Fill(GtkTreeIter* parent_iter, TreeModelNode* parent_node) { | |
395 if (parent_iter) | |
396 FillRow(parent_iter, parent_node); | |
397 GtkTreeIter iter; | |
398 int child_count = tree_model_->GetChildCount(parent_node); | |
399 for (int i = 0; i < child_count; ++i) { | |
400 TreeModelNode* node = tree_model_->GetChild(parent_node, i); | |
401 gtk_tree_store_append(tree_store_, &iter, parent_iter); | |
402 Fill(&iter, node); | |
403 } | |
404 } | |
405 | |
406 GtkTreePath* TreeAdapter::GetTreePath(TreeModelNode* node) { | |
407 GtkTreePath* path = gtk_tree_path_new(); | |
408 TreeModelNode* parent = node; | |
409 while (parent) { | |
410 parent = tree_model_->GetParent(parent); | |
411 if (parent) { | |
412 int idx = tree_model_->IndexOfChild(parent, node); | |
413 gtk_tree_path_prepend_index(path, idx); | |
414 node = parent; | |
415 } | |
416 } | |
417 return path; | |
418 } | |
419 | |
420 bool TreeAdapter::GetTreeIter(TreeModelNode* node, GtkTreeIter* iter) { | |
421 GtkTreePath* path = GetTreePath(node); | |
422 bool rv = false; | |
423 // Check the path ourselves since gtk_tree_model_get_iter prints a warning if | |
424 // given an empty path. The path will be empty when it points to the root | |
425 // node and we are using SetRootShown(false). | |
426 if (gtk_tree_path_get_depth(path) > 0) | |
427 rv = gtk_tree_model_get_iter(GTK_TREE_MODEL(tree_store_), iter, path); | |
428 gtk_tree_path_free(path); | |
429 return rv; | |
430 } | |
431 | |
432 void TreeAdapter::TreeNodesAdded(TreeModel* model, | |
433 TreeModelNode* parent, | |
434 int start, | |
435 int count) { | |
436 delegate_->OnAnyModelUpdateStart(); | |
437 GtkTreeIter parent_iter; | |
438 GtkTreeIter* parent_iter_ptr = NULL; | |
439 GtkTreeIter iter; | |
440 if (GetTreeIter(parent, &parent_iter)) | |
441 parent_iter_ptr = &parent_iter; | |
442 for (int i = 0; i < count; ++i) { | |
443 gtk_tree_store_insert(tree_store_, &iter, parent_iter_ptr, start + i); | |
444 Fill(&iter, tree_model_->GetChild(parent, start + i)); | |
445 } | |
446 delegate_->OnAnyModelUpdate(); | |
447 } | |
448 | |
449 void TreeAdapter::TreeNodesRemoved(TreeModel* model, | |
450 TreeModelNode* parent, | |
451 int start, | |
452 int count) { | |
453 delegate_->OnAnyModelUpdateStart(); | |
454 GtkTreeIter iter; | |
455 GtkTreePath* path = GetTreePath(parent); | |
456 gtk_tree_path_append_index(path, start); | |
457 gtk_tree_model_get_iter(GTK_TREE_MODEL(tree_store_), &iter, path); | |
458 gtk_tree_path_free(path); | |
459 for (int i = 0; i < count; ++i) { | |
460 RemoveRecursively(tree_store_, &iter); | |
461 } | |
462 delegate_->OnAnyModelUpdate(); | |
463 } | |
464 | |
465 void TreeAdapter::TreeNodeChanged(TreeModel* model, TreeModelNode* node) { | |
466 delegate_->OnAnyModelUpdateStart(); | |
467 GtkTreeIter iter; | |
468 if (GetTreeIter(node, &iter)) | |
469 FillRow(&iter, node); | |
470 delegate_->OnAnyModelUpdate(); | |
471 } | |
472 | |
473 } // namespace gtk_tree | |
OLD | NEW |