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/helpers.h"
16 #include "bits/config.h"
17 #include "imagestore.h"
18 #include "gtkhelpers.h"
19 #include "gtkdefines.h"
20 #include "gtkitembrowser.h"
21 
ItemBrowserBase(void)22 ItemBrowserBase::ItemBrowserBase (void)
23   : store(Gtk::TreeStore::create(cols)),
24     view(store)
25 {
26   Gtk::TreeViewColumn* col_name = Gtk::manage(new Gtk::TreeViewColumn);
27   col_name->set_title("Name");
28   col_name->pack_start(this->cols.icon, false);
29  #ifdef GLIBMM_PROPERTIES_ENABLED
30   Gtk::CellRendererText* name_renderer = Gtk::manage(new Gtk::CellRendererText);
31   col_name->pack_start(*name_renderer, true);
32   col_name->add_attribute(name_renderer->property_markup(),
33       this->cols.name);
34   #else
35   /* FIXME: Activate markup here. */
36   col_name->pack_start(this->cols.name);
37   #endif
38   this->view.append_column(*col_name);
39   this->view.set_headers_visible(false);
40   this->view.get_selection()->set_mode(Gtk::SELECTION_SINGLE);
41 
42   this->view.get_selection()->signal_changed().connect
43       (sigc::mem_fun(*this, &ItemBrowserBase::on_selection_changed));
44   this->view.signal_row_activated().connect(sigc::mem_fun
45       (*this, &ItemBrowserBase::on_row_activated));
46   this->view.signal_button_press_myevent().connect(sigc::mem_fun
47       (*this, &ItemBrowserBase::on_view_button_pressed));
48   this->view.signal_query_tooltip().connect(sigc::mem_fun
49       (*this, &ItemBrowserBase::on_query_element_tooltip));
50   this->view.set_has_tooltip(true);
51 }
52 
53 /* ---------------------------------------------------------------- */
54 
55 void
on_selection_changed(void)56 ItemBrowserBase::on_selection_changed (void)
57 {
58   if (this->view.get_selection()->get_selected_rows().empty())
59     return;
60 
61   Gtk::TreeModel::iterator iter = this->view.get_selection()->get_selected();
62 
63   ApiElement const* elem = (*iter)[this->cols.data];
64   if (elem == 0)
65     return;
66 
67   this->sig_element_selected.emit(elem);
68 }
69 
70 /* ---------------------------------------------------------------- */
71 
72 void
on_row_activated(Gtk::TreeModel::Path const & path,Gtk::TreeViewColumn *)73 ItemBrowserBase::on_row_activated (Gtk::TreeModel::Path const& path,
74     Gtk::TreeViewColumn* /*col*/)
75 {
76   Gtk::TreeModel::iterator iter = this->store->get_iter(path);
77   ApiElement const* elem = (*iter)[this->cols.data];
78 
79   if (elem != 0)
80   {
81     this->sig_element_activated.emit(elem);
82   }
83   else
84   {
85     if (this->view.row_expanded(path))
86       this->view.collapse_row(path);
87     else
88       this->view.expand_row(path, true);
89   }
90 }
91 
92 /* ---------------------------------------------------------------- */
93 
94 void
on_view_button_pressed(GdkEventButton * event)95 ItemBrowserBase::on_view_button_pressed (GdkEventButton* event)
96 {
97   if (event->type != GDK_BUTTON_PRESS || event->button != 3)
98     return;
99 
100   Glib::RefPtr<Gtk::TreeView::Selection> selection
101       = this->view.get_selection();
102 
103   if (selection->count_selected_rows() != 1)
104     return;
105 
106   Gtk::TreeModel::iterator iter = selection->get_selected();
107   ApiElement const* elem = (*iter)[this->cols.data];
108   if (elem == 0)
109     return;
110 
111   /* Skills and certs have context menus. */
112   switch (elem->get_type())
113   {
114     case API_ELEM_SKILL:
115     {
116       ApiSkill const* skill = (ApiSkill const*)elem;
117 
118       GtkSkillContextMenu* menu = Gtk::manage(new GtkSkillContextMenu);
119       menu->set_skill(skill, this->charsheet->get_level_for_skill(skill->id));
120       menu->popup(event->button, event->time);
121       menu->signal_planning_requested().connect(sigc::mem_fun
122           (*this, &ItemBrowserBase::on_planning_requested));
123       break;
124     }
125 
126     case API_ELEM_CERT:
127     {
128       ApiCert const* cert = (ApiCert const*)elem;
129       GtkCertContextMenu* menu = Gtk::manage(new GtkCertContextMenu);
130       menu->set_cert(cert);
131       menu->popup(event->button, event->time);
132       menu->signal_planning_requested().connect(sigc::mem_fun
133           (*this, &ItemBrowserBase::on_planning_requested));
134       break;
135     }
136 
137     default:
138       break;
139   }
140 
141   return;
142 }
143 
144 /* ---------------------------------------------------------------- */
145 
146 bool
on_query_element_tooltip(int x,int y,bool,Glib::RefPtr<Gtk::Tooltip> const & tooltip)147 ItemBrowserBase::on_query_element_tooltip (int x, int y,
148     bool /* key */, Glib::RefPtr<Gtk::Tooltip> const& tooltip)
149 {
150   return GtkHelpers::create_tooltip_from_view(x, y, tooltip,
151       this->view, this->store, this->cols.data);
152 }
153 
154 /* ================================================================ */
155 
156 enum ComboBoxSkillFilter
157 {
158   CB_FILTER_SKILL_ALL,
159   CB_FILTER_SKILL_UNKNOWN,
160   CB_FILTER_SKILL_PARTIAL,
161   CB_FILTER_SKILL_ENABLED,
162   CB_FILTER_SKILL_KNOWN,
163   CB_FILTER_SKILL_KNOWN_BUT_V
164 };
165 
166 enum ComboBoxSkillFilterAttribute
167 {
168   CB_FILTER_ATTRIBUTE_ANY,
169   CB_FILTER_ATTRIBUTE_INTELLIGENCE,
170   CB_FILTER_ATTRIBUTE_MEMORY,
171   CB_FILTER_ATTRIBUTE_CHARISMA,
172   CB_FILTER_ATTRIBUTE_PERCEPTION,
173   CB_FILTER_ATTRIBUTE_WILLPOWER
174 };
175 
GtkSkillBrowser(void)176 GtkSkillBrowser::GtkSkillBrowser (void)
177   : Gtk::Box(Gtk::ORIENTATION_VERTICAL, 5)
178 {
179   this->store->set_sort_column(this->cols.name, Gtk::SORT_ASCENDING);
180 
181   this->filter_cb.append("Show all skills");
182   this->filter_cb.append("Only show unknown skills");
183   this->filter_cb.append("Only show partial skills");
184   this->filter_cb.append("Only show enabled skills");
185   this->filter_cb.append("Only show known skills");
186   this->filter_cb.append("Only show known skills not at V");
187   this->filter_cb.set_active(0);
188 
189   this->primary_cb.append("Any");
190   this->primary_cb.append("Intelligence");
191   this->primary_cb.append("Memory");
192   this->primary_cb.append("Charisma");
193   this->primary_cb.append("Perception");
194   this->primary_cb.append("Willpower");
195   this->primary_cb.set_active(0);
196 
197   this->secondary_cb.append("Any");
198   this->secondary_cb.append("Intelligence");
199   this->secondary_cb.append("Memory");
200   this->secondary_cb.append("Charisma");
201   this->secondary_cb.append("Perception");
202   this->secondary_cb.append("Willpower");
203   this->secondary_cb.set_active(0);
204 
205   Gtk::ScrolledWindow* scwin = MK_SCWIN;
206   scwin->set_shadow_type(Gtk::SHADOW_ETCHED_IN);
207   scwin->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_ALWAYS);
208   scwin->add(this->view);
209 
210   Gtk::Button* clear_filter_but = MK_BUT0;
211   clear_filter_but->set_image_from_icon_name("edit-clear", Gtk::ICON_SIZE_MENU);
212   clear_filter_but->set_relief(Gtk::RELIEF_NONE);
213   clear_filter_but->set_tooltip_text("Clear filter");
214 
215   Gtk::Box* filter_box = MK_HBOX(5);
216   filter_box->pack_start(*MK_LABEL("Filter:"), false, false, 0);
217   filter_box->pack_start(this->filter_entry, true, true, 0);
218   filter_box->pack_start(*clear_filter_but, false, false, 0);
219 
220   Gtk::Box* attributes_box = MK_HBOX(5);
221   Gtk::Box* attributes_label_box = MK_HBOX(5);
222   attributes_label_box->pack_start(*MK_LABEL("Primary Attribute"), true, true, 0);
223   attributes_label_box->pack_start(*MK_LABEL("Secondary Attribute"), true, true, 0);
224   attributes_box->pack_start(this->primary_cb, true, true, 0);
225   attributes_box->pack_start(this->secondary_cb, true, true, 0);
226 
227   this->pack_start(*filter_box, false, false, 0);
228   this->pack_start(this->filter_cb, false, false, 0);
229   this->pack_start(*attributes_label_box, false, false, 0);
230   this->pack_start(*attributes_box, false, false, 0);
231   this->pack_start(*scwin, true, true, 0);
232 
233   this->filter_entry.signal_activate().connect(sigc::mem_fun
234       (*this, &GtkSkillBrowser::fill_store));
235   this->filter_cb.signal_changed().connect(sigc::mem_fun
236       (*this, &GtkSkillBrowser::fill_store));
237   this->primary_cb.signal_changed().connect(sigc::mem_fun
238       (*this, &GtkSkillBrowser::fill_store));
239   this->secondary_cb.signal_changed().connect(sigc::mem_fun
240       (*this, &GtkSkillBrowser::fill_store));
241   clear_filter_but->signal_clicked().connect(sigc::mem_fun
242       (*this, &GtkSkillBrowser::clear_filter));
243 }
244 
245 /* ---------------------------------------------------------------- */
246 
247 void
fill_store(void)248 GtkSkillBrowser::fill_store (void)
249 {
250   this->store->clear();
251   Glib::ustring filter = this->filter_entry.get_text();
252 
253   ApiSkillTreePtr tree = ApiSkillTree::request();
254   ApiSkillMap& skills = tree->skills;
255   ApiSkillGroupMap& groups = tree->groups;
256 
257   typedef Gtk::TreeModel::iterator GtkTreeModelIter;
258   typedef std::map<int, std::pair<GtkTreeModelIter, int> > SkillGroupsMap;
259   SkillGroupsMap skill_group_iters;
260 
261   /* Append all skill groups to the store. */
262   for (ApiSkillGroupMap::iterator iter = groups.begin();
263       iter != groups.end(); iter++)
264   {
265     Gtk::TreeModel::iterator siter = this->store->append();
266     (*siter)[this->cols.name] = iter->second.name;
267     (*siter)[this->cols.icon] = ImageStore::skillicons[0];
268     (*siter)[this->cols.data] = 0;
269     skill_group_iters.insert(std::make_pair
270         (iter->first, std::make_pair(siter, 0)));
271   }
272 
273   /* Prepare some short hands .*/
274   int active_row_num = this->filter_cb.get_active_row_number();
275   int primary_active_row_num = this->primary_cb.get_active_row_number();
276   int secondary_active_row_num = this->secondary_cb.get_active_row_number();
277   bool only_unknown = (active_row_num == CB_FILTER_SKILL_UNKNOWN);
278   bool only_partial = (active_row_num == CB_FILTER_SKILL_PARTIAL);
279   bool only_enabled = (active_row_num == CB_FILTER_SKILL_ENABLED);
280   bool only_known = (active_row_num == CB_FILTER_SKILL_KNOWN) || (active_row_num == CB_FILTER_SKILL_KNOWN_BUT_V);
281   bool only_known_but_v = (active_row_num == CB_FILTER_SKILL_KNOWN_BUT_V);
282   ApiAttrib primary = primary_active_row_num == CB_FILTER_ATTRIBUTE_ANY ? API_ATTRIB_UNKNOWN : (ApiAttrib)(primary_active_row_num-CB_FILTER_ATTRIBUTE_INTELLIGENCE);
283   ApiAttrib secondary = secondary_active_row_num == CB_FILTER_ATTRIBUTE_ANY ? API_ATTRIB_UNKNOWN : (ApiAttrib)(secondary_active_row_num-CB_FILTER_ATTRIBUTE_INTELLIGENCE);
284   std::string unpublished_cfg("planner.show_unpublished_skills");
285   bool only_published = !Config::conf.get_value(unpublished_cfg)->get_bool();
286 
287   /* Append all skills to the skill groups. */
288   for (ApiSkillMap::iterator iter = skills.begin();
289       iter != skills.end(); iter++)
290   {
291     ApiSkill& skill = iter->second;
292 
293     /* Filter non-public skills if so requested */
294     if (only_published && !skill.published)
295       continue;
296 
297     /* Apply string filter. */
298     if (Glib::ustring(skill.name).casefold()
299         .find(filter.casefold()) == Glib::ustring::npos)
300       continue;
301 
302     SkillGroupsMap::iterator giter = skill_group_iters.find(skill.group);
303     if (giter == skill_group_iters.end())
304     {
305       std::cout << "Error appending skill, unknown group!" << std::endl;
306       continue;
307     }
308 
309     ApiCharSheetSkill* cskill = this->charsheet->get_skill_for_id(skill.id);
310     Glib::RefPtr<Gdk::Pixbuf> skill_icon;
311 
312     if (cskill == 0)
313     {
314       if (primary!=API_ATTRIB_UNKNOWN)
315         if (skill.primary != primary)
316           continue;
317 
318       if (secondary!=API_ATTRIB_UNKNOWN)
319         if (skill.secondary != secondary)
320           continue;
321 
322       /* The skill is unknown. */
323       if (only_known || only_partial)
324         continue;
325 
326       if (this->have_prerequisites_for_skill(&skill))
327       {
328         /* The skill is unknown but prequisites are there. */
329         skill_icon = ImageStore::skillstatus[1];
330       }
331       else
332       {
333         /* The skill is unknown and no prequisites. */
334         if (only_enabled)
335           continue;
336         skill_icon = ImageStore::skillstatus[0];
337       }
338 
339     }
340     else
341     {
342       if (primary!=API_ATTRIB_UNKNOWN)
343         if (skill.primary != primary)
344           continue;
345 
346       if (secondary!=API_ATTRIB_UNKNOWN)
347         if (skill.secondary != secondary)
348           continue;
349 
350       /* The skill is known. */
351       if (only_unknown || only_enabled)
352         continue;
353 
354       /* Check if the skill is partially trained. */
355       if (only_partial && cskill->points == cskill->points_start)
356         continue;
357 
358       /* The skill is known and already trained to level v */
359       if (only_known_but_v && cskill->level == 5)
360         continue;
361 
362       switch (cskill->level)
363       {
364         case 0: skill_icon = ImageStore::skillstatus[2]; break;
365         case 1: skill_icon = ImageStore::skillstatus[3]; break;
366         case 2: skill_icon = ImageStore::skillstatus[4]; break;
367         case 3: skill_icon = ImageStore::skillstatus[5]; break;
368         case 4: skill_icon = ImageStore::skillstatus[6]; break;
369         case 5: skill_icon = ImageStore::skillstatus[7]; break;
370         default: skill_icon = ImageStore::skillstatus[0]; break;
371       }
372     }
373 
374     /* Finally append the skill. */
375     Gtk::TreeModel::iterator siter = this->store->append
376         (giter->second.first->children());
377     char const *primary_name = ApiSkillTree::get_attrib_short_name(skill.primary);
378     char const *secondary_name = ApiSkillTree::get_attrib_short_name(skill.secondary);
379     (*siter)[this->cols.name] = skill.name + " ("
380         + Helpers::get_string_from_int(skill.rank)
381         + ") <span size=\"small\" foreground=\"grey\">" + primary_name + "/"
382         + secondary_name + "</span>";
383     (*siter)[this->cols.data] = &skill;
384 
385     (*siter)[this->cols.icon] = skill_icon;
386 
387     giter->second.second += 1;
388   }
389 
390   /* Remove empty groups (due to filtering). */
391   for (SkillGroupsMap::iterator iter = skill_group_iters.begin();
392       iter != skill_group_iters.end(); iter++)
393   {
394     if (iter->second.second == 0)
395       this->store->erase(iter->second.first);
396   }
397 
398   if (!filter.empty() || primary != API_ATTRIB_UNKNOWN
399       || secondary != API_ATTRIB_UNKNOWN || active_row_num != 0)
400     this->view.expand_all();
401 }
402 
403 /* ---------------------------------------------------------------- */
404 
405 void
clear_filter(void)406 GtkSkillBrowser::clear_filter (void)
407 {
408   this->filter_entry.set_text("");
409   this->fill_store();
410 }
411 
412 /* ---------------------------------------------------------------- */
413 
414 bool
have_prerequisites_for_skill(ApiSkill const * skill)415 GtkSkillBrowser::have_prerequisites_for_skill (ApiSkill const* skill)
416 {
417   ApiSkillTreePtr tree = ApiSkillTree::request();
418   for (unsigned int i = 0; i < skill->deps.size(); ++i)
419   {
420     int depskill_id = skill->deps[i].first;
421     int depskill_level = skill->deps[i].second;
422 
423     int charlevel = this->charsheet->get_level_for_skill(depskill_id);
424     if (charlevel < depskill_level)
425       return false;
426   }
427 
428   return true;
429 }
430 
431 /* ================================================================ */
432 
433 enum ComboBoxCertFilter
434 {
435   CB_FILTER_CERT_ALL,
436   CB_FILTER_CERT_CLAIMED,
437   CB_FILTER_CERT_CLAIMABLE,
438   CB_FILTER_CERT_PARTIAL,
439   CB_FILTER_CERT_NOPRE
440 };
441 
GtkCertBrowser(void)442 GtkCertBrowser::GtkCertBrowser (void)
443   : Gtk::Box(Gtk::ORIENTATION_VERTICAL, 5)
444 {
445   this->filter_cb.append("Show all certificates");
446   this->filter_cb.append("Only show claimed certs");
447   this->filter_cb.append("Only show claimable certs");
448   this->filter_cb.append("Only show partial certs");
449   this->filter_cb.append("Only show unknown certs");
450   this->filter_cb.set_active(0);
451 
452   Gtk::ScrolledWindow* scwin = MK_SCWIN;
453   scwin->set_shadow_type(Gtk::SHADOW_ETCHED_IN);
454   scwin->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_ALWAYS);
455   scwin->add(this->view);
456 
457   Gtk::Button* clear_filter_but = MK_BUT0;
458   clear_filter_but->set_image_from_icon_name("edit-clear", Gtk::ICON_SIZE_MENU);
459   clear_filter_but->set_relief(Gtk::RELIEF_NONE);
460   clear_filter_but->set_tooltip_text("Clear filter");
461 
462   Gtk::Box* filter_box = MK_HBOX(5);
463   filter_box->pack_start(*MK_LABEL("Filter:"), false, false, 0);
464   filter_box->pack_start(this->filter_entry, true, true, 0);
465   filter_box->pack_start(*clear_filter_but, false, false, 0);
466 
467   this->pack_start(*filter_box, false, false, 0);
468   this->pack_start(this->filter_cb, false, false, 0);
469   this->pack_start(*scwin, true, true, 0);
470 
471   this->filter_entry.signal_activate().connect(sigc::mem_fun
472       (*this, &GtkCertBrowser::fill_store));
473   this->filter_cb.signal_changed().connect(sigc::mem_fun
474       (*this, &GtkCertBrowser::fill_store));
475   clear_filter_but->signal_clicked().connect(sigc::mem_fun
476       (*this, &GtkCertBrowser::clear_filter));
477 }
478 
479 /* ---------------------------------------------------------------- */
480 
481 void
fill_store(void)482 GtkCertBrowser::fill_store (void)
483 {
484   this->store->clear();
485   Glib::ustring filter = this->filter_entry.get_text();
486 
487   ApiCertTreePtr tree = ApiCertTree::request();
488   ApiCertMap& certs = tree->certificates;
489 
490   /* Prepare some short hands .*/
491   int active_row_num = this->filter_cb.get_active_row_number();
492   bool only_claimed = (active_row_num == CB_FILTER_CERT_CLAIMED);
493   bool only_claimable = (active_row_num == CB_FILTER_CERT_CLAIMABLE);
494   bool only_partial = (active_row_num == CB_FILTER_CERT_PARTIAL);
495   bool only_unknown = (active_row_num == CB_FILTER_CERT_NOPRE);
496 
497   typedef std::pair<int, ApiCert const*> CertShowInfo;
498   typedef std::map<Glib::ustring, CertShowInfo> CertClassMap;
499   typedef std::map<int, CertClassMap> CertGradeMap;
500   typedef std::map<Glib::ustring, CertGradeMap> CertCatMap;
501   CertCatMap show_mapping;
502 
503   /* Iterate over all certificates, check character status and add to maps. */
504   for (ApiCertMap::iterator iter = certs.begin(); iter != certs.end(); iter++)
505   {
506     ApiCert const* cert = &iter->second;
507     ApiCertClass const* cclass = cert->class_details;
508     ApiCertCategory const* cat = cclass->cat_details;
509 
510     /* Check if cert matches the filter. */
511     if (!filter.empty() && Glib::ustring(cclass->name)
512         .casefold().find(filter.casefold()) == Glib::ustring::npos)
513       continue;
514 
515     CertShowInfo csi;
516     csi.second = cert;
517     int cgrade = this->charsheet->get_grade_for_class(cclass->id);
518     if (cgrade < cert->grade)
519     {
520       if (only_claimed)
521         continue;
522 
523       switch (this->check_prerequisites_for_cert(cert))
524       {
525         default:
526         case CERT_PRE_HAVE_NONE:
527           if (only_claimable || only_partial)
528             continue;
529           csi.first = 3;
530           break;
531 
532         case CERT_PRE_HAVE_SOME:
533           if (only_claimable || only_unknown)
534             continue;
535           csi.first = 2;
536           break;
537 
538         case CERT_PRE_HAVE_ALL:
539           if (only_partial || only_unknown)
540             continue;
541           csi.first = 1;
542           break;
543       }
544     }
545     else
546     {
547       if (only_claimable || only_partial || only_unknown)
548         continue;
549 
550       csi.first = 0;
551     }
552 
553     /* Certificate matched filters. Add to sets. */
554     show_mapping[cat->name][cert->grade][cclass->name] = csi;
555   }
556 
557   /* Fill the certificate store. */
558   for (CertCatMap::iterator i = show_mapping.begin();
559       i != show_mapping.end(); i++)
560   {
561     Gtk::TreeModel::iterator siter = this->store->append();
562     (*siter)[this->cols.name] = i->first;
563     (*siter)[this->cols.icon] = ImageStore::certificate_small;
564     (*siter)[this->cols.data] = 0;
565 
566     for (CertGradeMap::iterator j = i->second.begin();
567         j != i->second.end(); j++)
568     {
569       Gtk::TreeModel::iterator siter2 = this->store->append(siter->children());
570       int grade_idx = ApiCertTree::get_grade_index(j->first);
571       (*siter2)[this->cols.name] = ApiCertTree::get_name_for_grade(j->first);
572       (*siter2)[this->cols.icon] = ImageStore::certgrades[grade_idx];
573       (*siter2)[this->cols.data] = 0;
574 
575       for (CertClassMap::iterator k = j->second.begin();
576           k != j->second.end(); k++)
577       {
578         Gtk::TreeModel::iterator row = this->store->append(siter2->children());
579         (*row)[this->cols.name] = k->second.second->class_details->name;
580         (*row)[this->cols.icon] = ImageStore::certstatus[k->second.first];
581         (*row)[this->cols.data] = k->second.second;
582       }
583     }
584   }
585 
586   if (!filter.empty() || active_row_num != 0)
587     this->view.expand_all();
588 }
589 
590 /* ---------------------------------------------------------------- */
591 
592 void
clear_filter(void)593 GtkCertBrowser::clear_filter (void)
594 {
595   this->filter_entry.set_text("");
596   this->fill_store();
597 }
598 
599 /* ---------------------------------------------------------------- */
600 
601 GtkCertBrowser::CertPrerequisite
check_prerequisites_for_cert(ApiCert const * cert)602 GtkCertBrowser::check_prerequisites_for_cert (ApiCert const* cert)
603 {
604   unsigned int deps_amount = 0;
605   unsigned int have_amount = 0;
606 
607   for (unsigned int i = 0; i < cert->skilldeps.size(); ++i)
608   {
609     int skill_id = cert->skilldeps[i].first;
610     int skill_level = cert->skilldeps[i].second;
611 
612     if (this->charsheet->get_level_for_skill(skill_id) >= skill_level)
613       have_amount += 1;
614     deps_amount += 1;
615   }
616 
617   ApiCertTreePtr tree = ApiCertTree::request();
618   for (unsigned int i = 0; i < cert->certdeps.size(); ++i)
619   {
620     int cert_id = cert->certdeps[i].first;
621     ApiCert const* rcert = tree->get_certificate_for_id(cert_id);
622     int rcert_class_id = rcert->class_details->id;
623 
624     if (this->charsheet->get_grade_for_class(rcert_class_id) >= rcert->grade)
625       have_amount += 1;
626     deps_amount += 1;
627   }
628 
629   if (have_amount == deps_amount)
630     return CERT_PRE_HAVE_ALL;
631   else if (have_amount == 0)
632     return CERT_PRE_HAVE_NONE;
633   else
634     return CERT_PRE_HAVE_SOME;
635 }
636