1 // This file is part of GtkEveMon.
2 //
3 // GtkEveMon is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, either version 3 of the License, or
6 // (at your option) any later version.
7 //
8 // You should have received a copy of the GNU General Public License
9 // along with GtkEveMon. If not, see <http://www.gnu.org/licenses/>.
10
11 #include <iostream>
12
13 #include <gtkmm.h>
14
15 #include "util/exception.h"
16 #include "util/helpers.h"
17
18 #include "gtkcolumnsbase.h"
19
20 void
set_format(std::string const & format_str)21 GtkColumnsFormat::set_format (std::string const& format_str)
22 {
23 this->clear();
24 std::string fstr = format_str;
25
26 while (true)
27 {
28 if (fstr.empty())
29 break;
30
31 /* Find blank and separate parts. */
32 std::string fpart;
33 size_t off = fstr.find_first_of(' ');
34 if (off == std::string::npos)
35 {
36 fpart = fstr;
37 fstr.clear();
38 }
39 else
40 {
41 fpart = fstr.substr(0, off);
42 fstr = fstr.substr(off + 1);
43 }
44
45 /* Check if the column is visible. */
46 bool visible;
47 if (fpart[0] == '+')
48 visible = true;
49 else if (fpart[0] == '-')
50 visible = false;
51 else
52 {
53 std::cout << "GtkColumnsBase: Format parse error 1" << std::endl;
54 throw Exception("GtkColumnsBase: Format parse error 1");
55 }
56
57 fpart = fpart.substr(1);
58
59 int column;
60 try
61 {
62 column = Helpers::get_int_from_string(fpart);
63 }
64 catch (Exception& e)
65 {
66 std::cout << "GtkColumnsBase: Format parse error 2" << std::endl;
67 throw Exception("GtkColumnsBase: Format parse error 2");
68 }
69
70 this->push_back(std::make_pair(column, visible));
71 }
72
73 //if (!this->check_format())
74 // throw Exception("GtkColumnsBase: Invalid format string!");
75 }
76
77 /* ---------------------------------------------------------------- */
78
79 std::string
get_format(void) const80 GtkColumnsFormat::get_format (void) const
81 {
82 std::string ret;
83 ret.reserve(3 * this->size());
84 for (unsigned int i = 0; i < this->size(); ++i)
85 {
86 ret += (this->at(i).second ? "+" : "-");
87 ret += Helpers::get_string_from_int((int)this->at(i).first);
88 if (i < this->size() - 1)
89 ret += " ";
90 }
91
92 return ret;
93 }
94
95 /* ---------------------------------------------------------------- */
96 /* The following algo will check for dupes, valid values and holes
97 * in the column format vector. In other words: The code will return
98 * true for any valid permutation of {0, ..., this->size() - 1} for
99 * the column positions.
100 */
101
102 bool
check_format(void)103 GtkColumnsFormat::check_format (void)
104 {
105 std::vector<bool> col_exist;
106 col_exist.resize(this->size(), false);
107
108 for (unsigned int i = 0; i < this->size(); ++i)
109 {
110 int col_pos = this->at(i).first;
111 if ((unsigned int)col_pos >= this->size() || col_pos < 0)
112 return false;
113 col_exist[col_pos] = true;
114 }
115
116 for (unsigned int i = 0; i < this->size(); ++i)
117 if (col_exist[i] == false)
118 return false;
119
120 return true;
121 }
122
123 /* ================================================================ */
124
GtkColumnsBase(Gtk::TreeView * view)125 GtkColumnsBase::GtkColumnsBase (Gtk::TreeView* view)
126 {
127 this->tree_view = view;
128 this->edit_context = false;
129 }
130
131 /* ---------------------------------------------------------------- */
132
133 void
append_column(Gtk::TreeViewColumn * col,GtkColumnOptions o)134 GtkColumnsBase::append_column (Gtk::TreeViewColumn* col, GtkColumnOptions o)
135 {
136 this->columns.push_back(std::make_pair(col, o));
137 if (this->columns.size() > this->format.size())
138 this->format.push_back(std::make_pair((int)this->format.size(), true));
139 }
140
141 /* ---------------------------------------------------------------- */
142
143 void
setup_columns_editable(void)144 GtkColumnsBase::setup_columns_editable (void)
145 {
146 if (this->format.size() != this->columns.size())
147 {
148 std::cout << "GtkColumnsBase: Internal size mismatch!" << std::endl;
149 throw Exception("GtkColumnsBase: Internal size mismatch!");
150 }
151 /* If we previously was in normal context, store format positions. */
152 if (!this->edit_context)
153 this->update_format_positions();
154
155 this->tree_view->remove_all_columns();
156
157 for (unsigned int i = 0; i < this->format.size(); ++i)
158 {
159 GtkColumnsPair& colpair = this->columns[this->format[i].first];
160
161 /* Only removable columns get new widgets. */
162 if (colpair.second.removable)
163 {
164 /* Setup checkbutton widget. */
165 Gtk::CheckButton* cb = Gtk::manage(new Gtk::CheckButton
166 (colpair.first->get_title(), false));
167 cb->set_active(this->format[i].second);
168 colpair.first->set_widget(*cb);
169 colpair.first->set_clickable(false);
170 this->tree_view->append_column(*colpair.first);
171 cb->show_all();
172 }
173 else if (colpair.second.icon)
174 {
175 Gtk::Widget* w = Gtk::manage(new Gtk::Image(colpair.second.icon));
176 colpair.first->set_widget(*w);
177 this->tree_view->append_column(*colpair.first);
178 w->show_all();
179 }
180 else
181 {
182 Gtk::Label* lb = Gtk::manage
183 (new Gtk::Label(colpair.first->get_title()));
184 colpair.first->set_widget(*lb);
185 this->tree_view->append_column(*colpair.first);
186 lb->show_all();
187 }
188
189 colpair.first->set_reorderable(false);
190 }
191
192 this->edit_context = true;
193 }
194
195 /* ---------------------------------------------------------------- */
196
197 void
setup_columns_normal(void)198 GtkColumnsBase::setup_columns_normal (void)
199 {
200 if (this->format.size() != this->columns.size())
201 {
202 std::cout << "GtkColumnsBase: Internal size mismatch!" << std::endl;
203 throw Exception("GtkColumnsBase: Internal size mismatch!");
204 }
205
206 /* If we previously was in edit context, store format visibility. */
207 if (this->edit_context)
208 this->update_format_visibility();
209
210 this->tree_view->remove_all_columns();
211
212 for (unsigned int i = 0; i < this->format.size(); ++i)
213 {
214 GtkColumnsPair& colpair = this->columns[this->format[i].first];
215
216 if (!colpair.second.icon)
217 {
218 /* Setup label widget. */
219 Gtk::Label* lb = Gtk::manage(new Gtk::Label(colpair.first->get_title()));
220 colpair.first->set_widget(*lb);
221 lb->show_all();
222 }
223 else
224 {
225 /* Setup image widget. */
226 Gtk::Widget* w = Gtk::manage(new Gtk::Image(colpair.second.icon));
227 colpair.first->set_widget(*w);
228 w->show_all();
229 }
230
231 colpair.first->set_clickable(colpair.second.clickable);
232 colpair.first->set_reorderable(colpair.second.reorderable);
233 }
234
235 this->show_format_columns();
236 this->edit_context = false;
237 }
238
239 /* ---------------------------------------------------------------- */
240
241 void
toggle_edit_context(void)242 GtkColumnsBase::toggle_edit_context (void)
243 {
244 if (this->edit_context == true)
245 this->setup_columns_normal();
246 else
247 this->setup_columns_editable();
248 }
249
250 /* ---------------------------------------------------------------- */
251
252 std::string
get_format(void)253 GtkColumnsBase::get_format (void)
254 {
255 this->update_format_positions();
256 if (this->edit_context)
257 this->update_format_visibility();
258 return this->format.get_format();
259 }
260
261 /* ---------------------------------------------------------------- */
262
263 void
set_format(std::string const & format_str)264 GtkColumnsBase::set_format (std::string const& format_str)
265 {
266 this->format.set_format(format_str);
267 this->check_format();
268 }
269
270 /* ---------------------------------------------------------------- */
271
272 void
show_format_columns(void)273 GtkColumnsBase::show_format_columns (void)
274 {
275 for (unsigned int i = 0; i < this->format.size(); ++i)
276 /* If the colum is visible. */
277 if (this->format[i].second)
278 {
279 /* If it is editable append it editable. */
280 this->tree_view->append_column
281 (*this->columns[this->format[i].first].first);
282 }
283 }
284
285 /* ---------------------------------------------------------------- */
286
287 void
check_format(void)288 GtkColumnsBase::check_format (void)
289 {
290 while (this->columns.size() > this->format.size())
291 this->format.push_back(std::make_pair((int)this->format.size(), true));
292
293 while (this->columns.size() < this->format.size())
294 this->format.pop_back();
295
296 if (!this->format.check_format())
297 {
298 std::cout << "GtkColumnsBase: Invalid format!" << std::endl;
299 throw Exception("GtkColumnsBase: Invalid format!");
300 }
301 }
302
303 /* ---------------------------------------------------------------- */
304
305 void
update_format_positions(void)306 GtkColumnsBase::update_format_positions (void)
307 {
308 Glib::ListHandle<Gtk::TreeViewColumn*> clist = this->tree_view->get_columns();
309 if (clist.size() == 0)
310 return;
311
312 /* Determine current column order. */
313 std::vector<int> view_order;
314 Glib::ListHandle<Gtk::TreeViewColumn*>::iterator iter = clist.begin();
315 while (iter != clist.end())
316 {
317 for (unsigned int i = 0; i < this->columns.size(); ++i)
318 if (this->columns[i].first == *iter)
319 {
320 view_order.push_back(i);
321 break;
322 }
323 iter++;
324 }
325
326 if (clist.size() != view_order.size())
327 {
328 std::cout << "GtkColumnsBase: Columns size mismatch. "
329 "Cannot determine order" << std::endl;
330 return;
331 }
332
333 /* Update the format with the current order. */
334 unsigned int current_col = 0;
335 for (unsigned int i = 0; i < this->format.size(); ++i)
336 for (unsigned int j = 0; j < view_order.size(); ++j)
337 if (this->format[i].first == view_order[j])
338 {
339 this->format[i].first = view_order[current_col];
340 current_col += 1;
341 if (current_col == view_order.size())
342 return;
343 else
344 break;
345 }
346 }
347
348 /* ---------------------------------------------------------------- */
349
350 void
update_format_visibility(void)351 GtkColumnsBase::update_format_visibility (void)
352 {
353 Glib::ListHandle<Gtk::TreeViewColumn*> clist = this->tree_view->get_columns();
354 if (clist.size() != this->format.size())
355 {
356 std::cout << "ListViewColumns: Invalid column state" << std::endl;
357 return;
358 }
359
360 for (unsigned int i = 0; i < this->format.size(); ++i)
361 if (this->columns[this->format[i].first].second.removable)
362 this->format[i].second = ((Gtk::CheckButton*)this->columns
363 [this->format[i].first].first->get_widget())->get_active();
364 }
365