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