summaryrefslogtreecommitdiffstats
path: root/chrome/browser/gtk/bookmark_tree_model.cc
blob: 309a9a119360e58f86ae4585aeccb1e75d31776d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/gtk/bookmark_tree_model.h"

#include <gtk/gtk.h>

#include "base/string_util.h"
#include "chrome/browser/bookmarks/bookmark_model.h"

namespace {

// Helper function for BuildTreeStoreFrom() which recursively inserts data from
// a BookmarkNode tree into a GtkTreeStore.
void RecursiveInsert(BookmarkNode* node, int selected_id,
                     GtkTreeStore* store, GtkTreeIter* selected_iter,
                     GtkTreeIter* parent) {
  GtkTreeIter iter;

  for (int i = 0; i < node->GetChildCount(); ++i) {
    BookmarkNode* child = node->GetChild(i);
    if (child->is_folder()) {
      gtk_tree_store_append(store, &iter, parent);
      gtk_tree_store_set(store, &iter,
                         0, WideToUTF8(child->GetTitle()).c_str(),
                         1, child->id(),
                         -1);
      if (selected_id && child->id() == selected_id) {
        // Save the iterator. Since we're using a GtkTreeStore, we're
        // guaranteed that the iterator will remain valid as long as the above
        // appended item exists.
        *selected_iter = iter;
      }

      RecursiveInsert(child, selected_id, store, selected_iter, &iter);
    }
  }
}

// Helper function for CommitTreeStoreDifferencesBetween() which recursively
// merges changes back from a GtkTreeStore into a tree of BookmarkNodes. This
// function only works on non-root nodes; our caller handles that special case.
void RecursiveResolve(BookmarkModel* bb_model, BookmarkNode* bb_node,
                      GtkTreeModel* tree_model, GtkTreeIter* parent_iter,
                      GtkTreePath* selected_path,
                      BookmarkNode** selected_node) {
  GtkTreePath* current_path = gtk_tree_model_get_path(tree_model, parent_iter);
  if (gtk_tree_path_compare(current_path, selected_path) == 0)
    *selected_node = bb_node;
  gtk_tree_path_free(current_path);

  GtkTreeIter child_iter;
  if (gtk_tree_model_iter_children(tree_model, &child_iter, parent_iter)) {
    do {
      int id = bookmark_utils::GetIdFromTreeIter(tree_model, &child_iter);
      std::wstring title =
          bookmark_utils::GetTitleFromTreeIter(tree_model, &child_iter);
      BookmarkNode* child_bb_node = NULL;
      if (id == 0) {
        child_bb_node = bb_model->AddGroup(bb_node, bb_node->GetChildCount(),
                                           title);
      } else {
        // Existing node, reset the title (BBModel ignores changes if the title
        // is the same).
        for (int j = 0; j < bb_node->GetChildCount(); ++j) {
          BookmarkNode* node = bb_node->GetChild(j);
          if (node->is_folder() && node->id() == id) {
            child_bb_node = node;
            break;
          }
        }
        DCHECK(child_bb_node);
        bb_model->SetTitle(child_bb_node, title);
      }
      RecursiveResolve(bb_model, child_bb_node,
                       tree_model, &child_iter,
                       selected_path, selected_node);
    } while (gtk_tree_model_iter_next(tree_model, &child_iter));
  }
}

}  // namespace

namespace bookmark_utils {

void BuildTreeStoreFrom(BookmarkModel* model, int selected_id,
                        GtkTreeStore** store, GtkTreeIter* selected_iter) {
  *store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_INT);
  RecursiveInsert(model->root_node(), selected_id, *store, selected_iter, NULL);
}

BookmarkNode* CommitTreeStoreDifferencesBetween(
    BookmarkModel* bb_model, GtkTreeStore* tree_store, GtkTreeIter* selected) {
  BookmarkNode* node_to_return = NULL;
  GtkTreeModel* tree_model = GTK_TREE_MODEL(tree_store);

  GtkTreePath* selected_path = gtk_tree_model_get_path(tree_model, selected);

  GtkTreeIter tree_root;
  if (!gtk_tree_model_get_iter_first(tree_model, &tree_root))
    NOTREACHED() << "Impossible missing bookmarks case";

  // The top level of this tree is weird and needs to be special cased. The
  // BookmarksNode tree is rooted on a root node while the GtkTreeStore has a
  // set of top level nodes that are the root BookmarksNode's children. These
  // items in the top level are not editable and therefore don't need the extra
  // complexity of trying to modify their title.
  BookmarkNode* root_node = bb_model->root_node();
  do {
    DCHECK(GetIdFromTreeIter(tree_model, &tree_root) != 0)
        << "It should be impossible to add another toplevel node";

    int id = GetIdFromTreeIter(tree_model, &tree_root);
    BookmarkNode* child_node = NULL;
    for (int j = 0; j < root_node->GetChildCount(); ++j) {
      BookmarkNode* node = root_node->GetChild(j);
      if (node->is_folder() && node->id() == id) {
        child_node = node;
        break;
      }
    }
    DCHECK(child_node);

    GtkTreeIter child_iter = tree_root;
    RecursiveResolve(bb_model, child_node, tree_model, &child_iter,
                     selected_path, &node_to_return);
  } while (gtk_tree_model_iter_next(tree_model, &tree_root));

  gtk_tree_path_free(selected_path);
  return node_to_return;
}

int GetIdFromTreeIter(GtkTreeModel* model, GtkTreeIter* iter) {
  GValue value = { 0, };
  int ret_val = -1;
  gtk_tree_model_get_value(model, iter, 1, &value);
  if (G_VALUE_HOLDS_INT(&value))
    ret_val = g_value_get_int(&value);
  else
    NOTREACHED() << "Impossible type mismatch";

  return ret_val;
}

std::wstring GetTitleFromTreeIter(GtkTreeModel* model, GtkTreeIter* iter) {
  GValue value = { 0, };
  std::wstring ret_val;
  gtk_tree_model_get_value(model, iter, 0, &value);
  if (G_VALUE_HOLDS_STRING(&value)) {
    const gchar* utf8str = g_value_get_string(&value);
    ret_val = UTF8ToWide(utf8str);
    g_value_unset(&value);
  } else {
    NOTREACHED() << "Impossible type mismatch";
  }

  return ret_val;
}

}  // namespace bookmark_utils