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 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 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 90 void TableAdapter::SetModel(ui::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 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 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 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 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 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 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, 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 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 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 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 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 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 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 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 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 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