1 /*
2     This file is a part of the RepSnapper project.
3     Copyright (C) 2010 Michael Meeks
4     Copyright (C) 2013  martin.dieringer@gmx.de
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU Lesser General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU Lesser General Public License for more details.
15 
16     You should have received a copy of the GNU Lesser General Public License along
17     with this program; if not, write to the Free Software Foundation, Inc.,
18     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20 
21 #include <cstdlib>
22 #include <gtkmm.h>
23 #include "settings.h"
24 
25 #include <stdafx.h>
26 
27 #include "infill.h"
28 /*
29  * How settings are intended to work:
30  *
31  * Settings is a subclass of Glib::KeyFile.
32  *
33  * Glade Builder Widgets are named as <Group>.<Key>, so automatically
34  * converted to KeyFile settings.  This works for most settings, but
35  * there are exceptions...
36  *
37  * All default setting values have to be at least in the default .conf file
38  *
39  * A redraw is done on every change made by the GUI.
40  */
41 
42 #ifdef WIN32
43 #  define DEFAULT_COM_PORT "COM0"
44 #else
45 #  define DEFAULT_COM_PORT "/dev/ttyUSB0"
46 #endif
47 
48 const string serialspeeds[] = { "9600", "19200", "38400", "57600", "115200", "230400", "250000" };
49 
50 
51 // convert GUI name to group/key
splitpoint(const string & glade_name,string & group,string & key)52 bool splitpoint(const string &glade_name, string &group, string &key) {
53   int pos = glade_name.find(".");
54   if (pos==(int)std::string::npos) return false;
55   group = glade_name.substr(0,pos);
56   key = glade_name.substr(pos+1);
57   return true;
58 }
59 
60 
set_up_combobox(Gtk::ComboBox * combo,vector<string> values)61 void set_up_combobox(Gtk::ComboBox *combo, vector<string> values)
62 {
63   if (combo->get_model())
64     return;
65   Gtk::TreeModelColumn<Glib::ustring> column;
66   Gtk::TreeModelColumnRecord record;
67   record.add(column);
68   Glib::RefPtr<Gtk::ListStore> store = Gtk::ListStore::create(record);
69   combo->pack_start (column);
70   combo->set_model(store);
71   for (uint i=0; i<values.size(); i++) {
72     //cerr << " adding " << values[i] << endl;
73     store->append()->set_value(0, Glib::ustring(values[i].c_str()));
74   }
75 #if GTK_VERSION_GE(2, 24)
76   if (!combo->get_has_entry())
77 #endif
78     combo->set_active(0);
79   //cerr << "ok" << endl;
80 }
81 
combobox_get_active_value(Gtk::ComboBox * combo)82 string combobox_get_active_value(Gtk::ComboBox *combo){
83 #if GTK_VERSION_GE(2, 24)
84   if (combo->get_has_entry())
85     {
86       Gtk::Entry *entry = combo->get_entry();
87       if (entry)
88 	return string(entry->get_text());
89     } else
90 #endif
91     {
92       uint c = combo->get_active_row_number();
93       Glib::ustring rval;
94       combo->get_model()->children()[c].get_value(0,rval);
95       return string(rval);
96     }
97   cerr << "could not get combobox active value" << endl;
98   return "";
99 }
100 
combobox_set_to(Gtk::ComboBox * combo,string value)101 bool combobox_set_to(Gtk::ComboBox *combo, string value)
102 {
103   Glib::ustring val(value);
104   Glib::RefPtr<Gtk::TreeModel> model = combo->get_model();
105   uint nch = model->children().size();
106   Glib::ustring rval;
107   Glib::ustring gvalue(value.c_str());
108 #if GTK_VERSION_GE(2, 24)
109   if (combo->get_has_entry())
110     {
111       Gtk::Entry *entry = combo->get_entry();
112       if (entry) {
113 	entry->set_text(value);
114 	return true;
115       }
116     }
117   else
118 #endif
119     {
120       for (uint c=0; c < nch; c++) {
121 	Gtk::TreeRow row = model->children()[c];
122 	row.get_value(0,rval);
123 	if (rval== gvalue) {
124 	  combo->set_active(c);
125 	  return true;
126 	}
127       }
128     }
129   cerr << "value " << value << " not found in combobox" << endl;
130   return false;
131 }
132 
133 
134 
135 /////////////////////////////////////////////////////////////////
136 
137 
138 
Settings()139 Settings::Settings ()
140 {
141   set_defaults();
142   m_user_changed = false;
143   inhibit_callback = false;
144 }
145 
~Settings()146 Settings::~Settings()
147 {
148 }
149 
150 // merge into current settings
merge(const Glib::KeyFile & keyfile)151 void Settings::merge (const Glib::KeyFile &keyfile)
152 {
153   vector< Glib::ustring > groups = keyfile.get_groups();
154   for (uint g = 0; g < groups.size(); g++) {
155     vector< Glib::ustring > keys = keyfile.get_keys(groups[g]);
156     for (uint k = 0; k < keys.size(); k++) {
157       set_value(groups[g], keys[k], keyfile.get_value(groups[g], keys[k]));
158     }
159   }
160 }
161 // always merge when loading settings
load_from_file(string file)162 bool Settings::load_from_file (string file) {
163   Glib::KeyFile k;
164   if (!k.load_from_file(file)) return false;
165   merge(k);
166   return true;
167 }
load_from_data(string data)168 bool Settings::load_from_data (string data) {
169   Glib::KeyFile k;
170   if (!k.load_from_file(data)) return false;
171   merge(k);
172   return true;
173 }
174 
175 
176 
177 // make "ExtruderN" group, if i<0 (not given), use current selected Extruder number
numberedExtruder(const string & group,int num) const178 string Settings::numberedExtruder(const string &group, int num) const {
179   if (group == "Extruder") {
180     ostringstream oss; oss << "Extruder" << num;
181     //cerr << "call for " << oss.str() << endl;
182     return oss.str();
183   }
184   return group;
185 }
186 
get_colour(const string & group,const string & name) const187 Vector4f Settings::get_colour(const string &group, const string &name) const {
188   vector<double> s = get_double_list(group, name);
189   return Vector4f(s[0],s[1],s[2],s[3]);
190 }
191 
set_colour(const string & group,const string & name,const Vector4f & value)192 void Settings::set_colour  (const string &group, const string &name,
193 			    const Vector4f &value) {
194   Glib::KeyFile::set_double_list(group, name, value);
195 }
196 
197 
198 void
assign_from(Settings * pSettings)199 Settings::assign_from(Settings *pSettings)
200 {
201   this->load_from_data(pSettings->to_data());
202   m_user_changed = false;
203   m_signal_visual_settings_changed.emit();
204   m_signal_update_settings_gui.emit();
205 }
206 
set_defaults()207 void Settings::set_defaults ()
208 {
209 
210   filename = "";
211 
212   set_string("Global","SettingsName","Default Settings");
213   set_string("Global","SettingsImage","");
214 
215   set_string("Global","Version",VERSION);
216 
217   set_string("GCode","Start",
218 	     "; This code is sent to the printer at the beginning.\n"
219 	     "; Adjust it to your needs.\n"
220 	     "; \n"
221 	     "; GCode generated by RepSnapper:\n"
222 	     "; http://reprap.org/wiki/RepSnapper_Manual:Introduction\n"
223 	     "G21             ; metric coordinates\n"
224 	     "G90             ; absolute positioning\n"
225 	     "T0              ; select new extruder\n"
226 	     "G28             ; go home\n"
227 	     "G92 E0          ; set extruder home\n"
228 	     "G1 X5 Y5 F500 ; move away 5 mm from 0.0, to use the same reset for each layer\n\n");
229   set_string("GCode","Layer","");
230   set_string("GCode","End",
231 	     "; This code is sent to the printer after the print.\n"
232 	     "; Adjust it to your needs.\n"
233 	     "G1 X0 Y0 F2000.0 ; feed for start of next move\n"
234 	     "M104 S0.0        ; heater off\n");
235 
236 
237 
238   // Extruders.clear();
239   // Extruders.push_back(Extruder);
240 
241   // The vectors map each to 3 spin boxes, one per dimension
242   set_double("Hardware","Volume.X", 200);
243   set_double("Hardware","Volume.Y", 200);
244   set_double("Hardware","Volume.Z", 140);
245   set_double("Hardware","PrintMargin.X", 10);
246   set_double("Hardware","PrintMargin.Y", 10);
247   set_double("Hardware","PrintMargin.Z", 0);
248 
249   set_boolean("Misc","SpeedsAreMMperSec",true);
250 }
251 
252 
253 // make old single coordinate colours to lists
convert_old_colour(const string & group,const string & key)254 void Settings::convert_old_colour  (const string &group, const string &key) {
255   try {
256     cerr << "converting  "<< group << "." <<key <<endl;
257     const double c[5] = { get_double(group, key+"R"),
258 			  get_double(group, key+"G"),
259 			  get_double(group, key+"B"),
260 			  get_double(group, key+"A"),0};
261     set_double_list(group,key,c);
262     remove_key(group, key+"R");
263     remove_key(group, key+"G");
264     remove_key(group, key+"B");
265     remove_key(group, key+"A");
266   } catch (const Glib::KeyFileError &err) {
267   }
268 }
269 
load_settings(Glib::RefPtr<Gio::File> file)270 void Settings::load_settings (Glib::RefPtr<Gio::File> file)
271 {
272   inhibit_callback = true;
273   filename = file->get_path();
274 
275   // set_defaults();
276 
277   if (has_group("Extruder"))
278     remove_group("Extruder"); // avoid converting old if merging new file
279 
280   try {
281     if (!load_from_file (filename)) {
282       std::cout << _("Failed to load settings from file '") << filename << "\n";
283       return;
284     }
285   } catch (const Glib::KeyFileError &err) {
286     std::cout << _("Exception ") << err.what() << _(" loading settings from file '") << filename << "\n";
287     return;
288   }
289 
290   std::cerr << _("Parsing config from '") << filename << "\n";
291 
292   // convert old colour handling:
293   vector< Glib::ustring > groups = get_groups();
294   for (uint g = 0; g < groups.size(); g++) {
295     //cerr << "["<<groups[g] << "] " ;
296     vector< Glib::ustring > keys = get_keys(groups[g]);
297     for (uint k = 0; k < keys.size(); k++) {
298       int n = keys[k].length();
299       int c = keys[k].find("Colour");
300       if (c >= 0 && c < n-6 && keys[k].substr(c+6,1) == "R")
301 	convert_old_colour(groups[g],keys[k].substr(0,c+6));
302     }
303   }
304 
305   // convert old user buttons:
306   std::vector<std::string> CustomButtonLabels;
307   std::vector<std::string> CustomButtonGCodes;
308   if (has_group("UserButtons")) {
309     CustomButtonLabels = get_string_list("UserButtons","Labels");
310     CustomButtonGCodes = get_string_list("UserButtons","GCodes");
311   }
312   try {
313     vector< Glib::ustring > keys = get_keys("CustomButtons");
314     for (uint k = 0; k < keys.size(); k++) {
315       bool havekey = false;
316       for (uint o = 0; o < CustomButtonLabels.size(); o++) {
317 	if (CustomButtonLabels[o] == keys[k]) {
318 	  CustomButtonGCodes[o] = get_string("CustomButtons",keys[k]);
319 	  havekey = true;
320 	  break;
321 	}
322       }
323       if (!havekey) {
324 	CustomButtonLabels.push_back(keys[k]);
325 	CustomButtonGCodes.push_back(get_string("CustomButtons",keys[k]));
326       }
327     }
328     remove_group("CustomButtons");
329   } catch (Glib::KeyFileError &err) {}
330   if (!has_group("UserButtons")) {
331     set_string_list("UserButtons","Labels",CustomButtonLabels);
332     set_string_list("UserButtons","GCodes",CustomButtonGCodes);
333   }
334 
335   // convert old extruders, now we count "Extruder0", "Extruder1" ...
336   // instead of "Extruder", "Extruder2" ...
337   if (has_group("Extruder")) { // have old Extruders
338     uint ne = getNumExtruders() + 1; // +1 because "Extruder" is not counted
339     if (has_group("Extruder0")) ne--; // already have "new" Extruders
340     copyGroup("Extruder","Extruder0");
341     if (ne > 1) {
342       for (uint k = 2; k < ne+1; k++) // copy 2,3,4,... to 1,2,3,...
343 	copyGroup(numberedExtruder("Extruder",k),
344 		  numberedExtruder("Extruder",k-1));
345       remove_group(numberedExtruder("Extruder",ne));
346     }
347     remove_group("Extruder");
348   }
349   uint ne = getNumExtruders();
350   for (uint k = 0; k < ne; k++) {
351     if (!has_key(numberedExtruder("Extruder",k), "OffsetX"))
352       set_double(numberedExtruder("Extruder",k), "OffsetX", 0);
353     if (!has_key(numberedExtruder("Extruder",k), "OffsetY"))
354       set_double(numberedExtruder("Extruder",k), "OffsetY", 0);
355   }
356   SelectExtruder(0);
357 
358   if ( ( has_group("Misc") &&
359 	 !has_key("Misc","SpeedsAreMMperSec") ) ||
360        !get_boolean("Misc","SpeedsAreMMperSec") )
361     cout << "!!!" << endl <<  _("\tThe config file has old speed settings (mm/min).\n\t Adjust them to mm/sec\n\t or use RepSnapper from 2.0 to 2.2 to convert them automatically.") << "!!!" <<  endl;
362   set_boolean("Misc","SpeedsAreMMperSec",true);
363 
364   inhibit_callback = false;
365   m_user_changed = false;
366   m_signal_visual_settings_changed.emit();
367   m_signal_update_settings_gui.emit();
368 }
369 
370 
save_settings(Glib::RefPtr<Gio::File> file)371 void Settings::save_settings(Glib::RefPtr<Gio::File> file)
372 {
373   inhibit_callback = true;
374   set_string("Global","Version",VERSION);
375 
376   remove_group("Extruder"); // is only temporary
377 
378   Glib::ustring contents = to_data();
379   // cerr << contents << endl;
380   Glib::file_set_contents (file->get_path(), contents);
381 
382   SelectExtruder(selectedExtruder); // reload default extruder
383 
384   inhibit_callback = false;
385   // all changes safely saved
386   m_user_changed = false;
387 }
388 
set_to_gui(Builder & builder,const string & group,const string & key)389 void Settings::set_to_gui (Builder &builder,
390 			   const string &group, const string &key)
391 {
392   inhibit_callback = true;
393   Glib::ustring glade_name = group + "." + key;
394   // inhibit warning for settings not defined in glade UI:
395   if (!builder->get_object (glade_name)) {
396     //cerr << glade_name << _(" not defined in GUI!")<< endl;
397     return;
398   }
399 
400   Gtk::Widget *w = NULL;
401   builder->get_widget (glade_name, w);
402   if (!w) {
403     std::cerr << _("Missing user interface item ") << glade_name << "\n";
404     return;
405   }
406   Gtk::CheckButton *check = dynamic_cast<Gtk::CheckButton *>(w);
407   if (check) {
408     check->set_active (get_boolean(group,key));
409     return;
410   }
411   Gtk::SpinButton *spin = dynamic_cast<Gtk::SpinButton *>(w);
412   if (spin) {
413     spin->set_value (get_double(group,key));
414     return;
415   }
416   Gtk::Range *range = dynamic_cast<Gtk::Range *>(w);
417   if (range) {
418     range->set_value (get_double(group,key));
419     return;
420   }
421   Gtk::ComboBox *combo = dynamic_cast<Gtk::ComboBox *>(w);
422   if (combo) {
423     if (glade_name == "Hardware.SerialSpeed") // has real value
424       combobox_set_to(combo, get_string(group,key));
425     else // has index
426       combo->set_active(get_integer(group,key));
427     return;
428   }
429   Gtk::Entry *entry = dynamic_cast<Gtk::Entry *>(w);
430   if (entry) {
431     entry->set_text (get_string(group,key));
432     return;
433   }
434   Gtk::Expander *exp = dynamic_cast<Gtk::Expander *>(w);
435   if (exp) {
436     exp->set_expanded (get_boolean(group,key));
437     return;
438   }
439   Gtk::ColorButton *col = dynamic_cast<Gtk::ColorButton *>(w);
440   if(col) {
441     vector<double> c = get_double_list(group,key);
442     Gdk::Color co; co.set_rgb_p(c[0],c[1],c[2]);
443     col->set_use_alpha(true);
444     col->set_color(co);
445     col->set_alpha(c[3] * 65535.0);
446     return;
447   }
448   Gtk::TextView *tv = dynamic_cast<Gtk::TextView *>(w);
449   if (tv) {
450     tv->get_buffer()->set_text(get_string(group,key));
451     return;
452   }
453 
454   cerr << "set_to_gui of "<< glade_name << " not done!" << endl;
455 }
456 
457 
get_from_gui(Builder & builder,const string & glade_name)458 void Settings::get_from_gui (Builder &builder, const string &glade_name)
459 {
460   if (inhibit_callback) return;
461   if (!builder->get_object (glade_name)) {
462     cerr << "no such object " << glade_name << endl;
463     return;
464   }
465   Gtk::Widget *w = NULL;
466   builder->get_widget (glade_name, w);
467   string group, key;
468   if (!splitpoint(glade_name, group, key)) return;
469   while (w) { // for using break ...
470     //cerr << "get " << group  << "." << key << " from gui"<< endl;
471     m_user_changed = true; // is_changed;
472     Gtk::CheckButton *check = dynamic_cast<Gtk::CheckButton *>(w);
473     if (check) {
474       set_boolean(group, key, check->get_active());
475       break;
476     }
477     Gtk::SpinButton *spin = dynamic_cast<Gtk::SpinButton *>(w);
478     if (spin) {
479       set_double(group, key, spin->get_value());
480       break;
481     }
482     Gtk::Range *range = dynamic_cast<Gtk::Range *>(w);
483     if (range) {
484       set_double(group, key, range->get_value());
485       break;
486     }
487     Gtk::ComboBox *combo = dynamic_cast<Gtk::ComboBox *>(w);
488     if (combo) {
489       if (glade_name == "Hardware.SerialSpeed") // has real value
490 	set_string(group,key,combobox_get_active_value(combo));
491       else
492 	set_integer(group,key,combo->get_active_row_number ());
493       break;
494     }
495     Gtk::Entry *e = dynamic_cast<Gtk::Entry *>(w);
496     if (e) {
497       set_string(group,key,e->get_text());
498       break;
499     }
500     Gtk::Expander *exp = dynamic_cast<Gtk::Expander *>(w);
501     if (exp) {
502       set_boolean(group,key,exp->get_expanded());
503       break;
504     }
505     Gtk::ColorButton *cb = dynamic_cast<Gtk::ColorButton *>(w);
506     if (cb) {
507       get_colour_from_gui(builder, glade_name);
508       break;
509     }
510     Gtk::TextView *tv = dynamic_cast<Gtk::TextView *>(w);
511     if (tv) {
512       set_string(group,key,tv->get_buffer()->get_text());
513       break;
514     }
515     cerr << _("Did not get setting from  ") << glade_name << endl;
516     m_user_changed = false;
517     break;
518   }
519   if (m_user_changed) {
520     // update currently edited extruder
521     if (glade_name.substr(0,8) == "Extruder") {
522       copyGroup("Extruder",numberedExtruder("Extruder", selectedExtruder));
523       // if selected for support, disable support for other extruders
524       if (key == "UseForSupport" && get_boolean(group,key) ) {
525         for (uint i = 0; i < getNumExtruders(); i++) {
526           if (i != selectedExtruder) {
527             set_boolean(numberedExtruder("Extruder", i), key, false);
528           }
529         }
530       }
531     }
532     m_signal_visual_settings_changed.emit();
533   }
534 }
535 
536 
537 
get_colour_from_gui(Builder & builder,const string & glade_name)538 void Settings::get_colour_from_gui (Builder &builder, const string &glade_name)
539 {
540   string group,key;
541   if (!splitpoint(glade_name, group,key)) return;
542   Gdk::Color c;
543   Gtk::ColorButton *w = NULL;
544   builder->get_widget (glade_name, w);
545   if (!w) return;
546 
547   c = w->get_color();
548 
549   // FIXME: detect 'changed' etc.
550   vector<double> d(4);
551   d[0] = c.get_red_p();
552   d[1] = c.get_green_p();
553   d[2] = c.get_blue_p();
554   d[3] = (float) (w->get_alpha()) / 65535.0;
555 
556   set_double_list(group, key, d);
557 
558   m_signal_visual_settings_changed.emit();
559 }
560 
561 
562 // whole group or all groups
set_to_gui(Builder & builder,const string filter)563 void Settings::set_to_gui (Builder &builder, const string filter)
564 {
565   inhibit_callback = true;
566   vector< Glib::ustring > groups = get_groups();
567   for (uint g = 0; g < groups.size(); g++) {
568     vector< Glib::ustring > keys = get_keys(groups[g]);
569     for (uint k = 0; k < keys.size(); k++) {
570       set_to_gui(builder, groups[g], keys[k]);
571     }
572   }
573 
574   //set_filltypes_to_gui (builder);
575 
576   if (filter == "" || filter == "Misc") {
577     Gtk::Window *pWindow = NULL;
578     builder->get_widget("main_window", pWindow);
579     try {
580       int w = get_integer("Misc","WindowWidth");
581       int h = get_integer("Misc","WindowHeight");
582       if (pWindow && w > 0 && h > 0) pWindow->resize(w,h);
583       int x = get_integer("Misc","WindowPosX");
584       int y = get_integer("Misc","WindowPosY");
585       if (pWindow && x > 0 && y > 0) pWindow->move(x,y);
586     } catch (const Glib::KeyFileError &err) {
587       std::cout << _("Exception ") << err.what() << _(" loading setting\n");
588     }
589   }
590 
591   // Set serial speed. Find the row that holds this value
592   if (filter == "" || filter == "Hardware") {
593     Gtk::ComboBox *portspeed = NULL;
594     builder->get_widget ("Hardware.SerialSpeed", portspeed);
595     if (portspeed) {
596       std::ostringstream ostr;
597       ostr << get_integer("Hardware","SerialSpeed");
598       //cerr << "portspeed " << get_integer("Hardware","SerialSpeed") << endl;
599       combobox_set_to(portspeed, ostr.str());
600     }
601   }
602   inhibit_callback = false;
603 }
604 
605 
connect_to_ui(Builder & builder)606 void Settings::connect_to_ui (Builder &builder)
607 {
608   if (has_group("Ranges")) {
609     vector<string> ranges = get_keys("Ranges");
610     for (uint i = 0; i < ranges.size(); i++) {
611       // get min, max, increment, page-incr.
612       vector<double> vals = get_double_list("Ranges", ranges[i]);
613       Gtk::Widget *w = NULL;
614       try {
615 	builder->get_widget (ranges[i], w);
616 	if (!w) {
617 	  std::cerr << "Missing user interface item " << ranges[i] << "\n";
618 	  continue;
619 	}
620 	Gtk::SpinButton *spin = dynamic_cast<Gtk::SpinButton *>(w);
621 	if (spin) {
622 	  spin->set_range (vals[0],vals[1]);
623 	  spin->set_increments (vals[2],vals[3]);
624 	  continue;
625 	}
626 	Gtk::Range *range = dynamic_cast<Gtk::Range *>(w); // sliders etc.
627 	if (range) {
628 	  range->set_range (vals[0],vals[1]);
629 	  range->set_increments (vals[2],vals[3]);
630 	  continue;
631 	}
632       } catch (Glib::Exception &ex) {
633       }
634     }
635   }
636 
637   // add signal callbacks to GUI elements
638   vector< Glib::ustring > groups = get_groups();
639   for (uint g = 0; g < groups.size(); g++) {
640     if (groups[g] == "Ranges") continue; // done that above
641     vector< Glib::ustring > keys = get_keys(groups[g]);
642     for (uint k = 0; k < keys.size(); k++) {
643       string glade_name = groups[g] + "." + keys[k];
644       if (!builder->get_object (glade_name))
645 	continue;
646       Gtk::Widget *w = NULL;
647       try {
648 	builder->get_widget (glade_name, w);
649 	if (!w) {
650 	  std::cerr << "Missing user interface item " << glade_name << "\n";
651 	  continue;
652 	}
653 	Gtk::CheckButton *check = dynamic_cast<Gtk::CheckButton *>(w);
654 	if (check) {
655 	  check->signal_toggled().connect
656 	    (sigc::bind(sigc::bind<string>(sigc::mem_fun(*this, &Settings::get_from_gui), glade_name), builder));
657 	  continue;
658 	}
659 	Gtk::SpinButton *spin = dynamic_cast<Gtk::SpinButton *>(w);
660 	if (spin) {
661 	  spin->signal_value_changed().connect
662 	    (sigc::bind(sigc::bind<string>(sigc::mem_fun(*this, &Settings::get_from_gui), glade_name), builder)) ;
663 	  continue;
664 	}
665 	Gtk::Range *range = dynamic_cast<Gtk::Range *>(w);
666 	if (range) {
667 	  range->signal_value_changed().connect
668 	    (sigc::bind(sigc::bind<string>(sigc::mem_fun(*this, &Settings::get_from_gui), glade_name), builder));
669 	  continue;
670 	}
671 	Gtk::ComboBox *combo = dynamic_cast<Gtk::ComboBox *>(w);
672 	if (combo) {
673 	  if (glade_name == "Hardware.SerialSpeed") { // Serial port speed
674 	    vector<string> speeds(serialspeeds,
675 				  serialspeeds+sizeof(serialspeeds)/sizeof(string));
676 	    set_up_combobox(combo, speeds);
677 	  } else if (glade_name.find("Filltype")!=std::string::npos) { // Infill types
678 	    uint nfills = sizeof(InfillNames)/sizeof(string);
679 	    vector<string> infills(InfillNames,InfillNames+nfills);
680 	    set_up_combobox(combo,infills);
681 	  }
682 	  combo->signal_changed().connect
683 	    (sigc::bind(sigc::bind<string>(sigc::mem_fun(*this, &Settings::get_from_gui), glade_name), builder));
684 	  continue;
685 	}
686 	Gtk::Entry *e = dynamic_cast<Gtk::Entry *>(w);
687 	if (e) {
688 	  e->signal_changed().connect
689 	    (sigc::bind(sigc::bind<string>(sigc::mem_fun(*this, &Settings::get_from_gui), glade_name), builder));
690 	  continue;
691 	}
692 	Gtk::Expander *exp = dynamic_cast<Gtk::Expander *>(w);
693 	if (exp) {
694 	  exp->property_expanded().signal_changed().connect
695 	    (sigc::bind(sigc::bind<string>(sigc::mem_fun(*this, &Settings::get_from_gui), glade_name), builder));
696 	  continue;
697 	}
698 	Gtk::ColorButton *cb = dynamic_cast<Gtk::ColorButton *>(w);
699 	if (cb) {
700 	  cb->signal_color_set().connect
701 	    (sigc::bind(sigc::bind<string>(sigc::mem_fun(*this, &Settings::get_from_gui), glade_name), builder));
702 	  continue;
703 	}
704 	Gtk::TextView *tv = dynamic_cast<Gtk::TextView *>(w);
705 	if (tv) {
706 	  tv->get_buffer()->signal_changed().connect
707 	    (sigc::bind(sigc::bind<string>(sigc::mem_fun(*this, &Settings::get_from_gui), glade_name), builder));
708 	  continue;
709 	}
710       } catch (Glib::Exception &ex) {
711       }
712     }
713   }
714 
715 
716   /* Update UI with defaults */
717   m_signal_update_settings_gui.emit();
718 }
719 
720 
721 // extrusion ratio for round-edge lines
RoundedLinewidthCorrection(double extr_width,double layerheight)722 double Settings::RoundedLinewidthCorrection(double extr_width,
723 					    double layerheight)
724 {
725   double factor = 1 + (M_PI/4.-1) * layerheight/extr_width;
726   // assume 2 half circles at edges
727   //    /-------------------\     //
728   //   |                     |    //
729   //    \-------------------/     //
730   //cerr << "round factor " << factor << endl;
731   return factor;
732 }
733 
GetExtrudedMaterialWidth(double layerheight) const734 double Settings::GetExtrudedMaterialWidth(double layerheight) const
735 {
736   // ExtrudedMaterialWidthRatio is preset by user
737   return min(max(get_double("Extruder","MinimumLineWidth"),
738 		 get_double("Extruder","ExtrudedMaterialWidthRatio") * layerheight),
739 	     get_double("Extruder","MaximumLineWidth"));
740 }
741 
742 // TODO This depends whether lines are packed or not - ellipsis/rectangle
743 
744 // how much mm filament material per extruded line length mm -> E gcode
GetExtrusionPerMM(double layerheight) const745 double Settings::GetExtrusionPerMM(double layerheight) const
746 {
747   double f = get_double("Extruder","ExtrusionFactor"); // overall factor
748   if (get_boolean("Extruder","CalibrateInput")) {  // means we use input filament diameter
749     const double matWidth = GetExtrudedMaterialWidth(layerheight); // this is the goal
750     // otherwise we just work back from the extruded diameter for now.
751     const double filamentdiameter = get_double("Extruder","FilamentDiameter");
752     f *= (matWidth * matWidth) / (filamentdiameter * filamentdiameter);
753   } // else: we work in terms of output anyway;
754 
755   return f;
756 }
757 
758 // return infill distance in mm
GetInfillDistance(double layerthickness,float percent) const759 double Settings::GetInfillDistance(double layerthickness, float percent) const
760 {
761   double fullInfillDistance = GetExtrudedMaterialWidth(layerthickness);
762   if (percent == 0) return 10000000;
763   return fullInfillDistance * (100./percent);
764 }
765 
getNumExtruders() const766 uint Settings::getNumExtruders() const
767 {
768   vector< Glib::ustring > groups = get_groups();
769   uint num=0;
770   for (uint g = 0; g < groups.size(); g++)
771     if (groups[g].substr(0,8) == "Extruder"
772 	&& groups[g].length() > 8 ) // count only numbered
773       num++;
774   return num;
775 }
776 
get_extruder_letters() const777 std::vector<char> Settings::get_extruder_letters() const
778 {
779   uint num = getNumExtruders();
780   std::vector<char> letters(num);
781   for (uint i = 0; i < num; i++)
782     letters[i] = get_string(numberedExtruder("Extruder",i),"GCLetter")[0];
783   return letters;
784 }
785 
GetSupportExtruder() const786 uint Settings::GetSupportExtruder() const
787 {
788   uint num = getNumExtruders();
789   for (uint i = 0; i < num; i++)
790     if (get_boolean(numberedExtruder("Extruder",i),"UseForSupport"))
791       return i;
792   return 0;
793 }
794 
get_extruder_offset(uint num) const795 Vector3d Settings::get_extruder_offset(uint num) const
796 {
797   string ext = numberedExtruder("Extruder",num);
798   return Vector3d(get_double(ext, "OffsetX"),
799 		  get_double(ext, "OffsetY"), 0.);
800 }
801 
802 
copyGroup(const string & from,const string & to)803 void Settings::copyGroup(const string &from, const string &to)
804 {
805   vector<string> keys = get_keys(from);
806   for (uint i = 0; i < keys.size(); i++)
807     set_value(to, keys[i], get_value(from, keys[i]));
808 }
809 
810 // create new
CopyExtruder(uint num)811 void Settings::CopyExtruder(uint num)
812 {
813   uint total = getNumExtruders();
814   string from = numberedExtruder("Extruder",num);
815   string to   = numberedExtruder("Extruder",total);
816   copyGroup(from, to);
817 }
RemoveExtruder(uint num)818 void Settings::RemoveExtruder(uint num)
819 {
820   ostringstream oss; oss << "Extruder"<<num;
821   remove_group(oss.str());
822 }
SelectExtruder(uint num,Builder * builder)823 void Settings::SelectExtruder(uint num, Builder *builder)
824 {
825   if (num >= getNumExtruders()) return;
826   selectedExtruder = num;
827   copyGroup(numberedExtruder("Extruder",num),"Extruder");
828   // show Extruder settings on gui
829   if (builder) {
830     set_to_gui(*builder, "Extruder");
831   }
832 }
833 
getBasicTransformation(Matrix4d T) const834 Matrix4d Settings::getBasicTransformation(Matrix4d T) const
835 {
836   Vector3d t;
837   T.get_translation(t);
838   const Vector3d margin = getPrintMargin();
839   double rsize = get_double("Raft","Size") * (get_boolean("Raft","Enable")?1:0);
840   t+= Vector3d(margin.x() + rsize, margin.y() + rsize, 0);
841   T.set_translation(t);
842   return T;
843 }
844 
845 
getPrintVolume() const846 Vector3d Settings::getPrintVolume() const
847 {
848   return Vector3d (get_double("Hardware","Volume.X"),
849 		   get_double("Hardware","Volume.Y"),
850 		   get_double("Hardware","Volume.Z"));
851 
852 }
getPrintMargin() const853 Vector3d Settings::getPrintMargin() const
854 {
855   Vector3d margin(get_double("Hardware","PrintMargin.X"),
856 		  get_double("Hardware","PrintMargin.Y"),
857 		  get_double("Hardware","PrintMargin.Z"));
858   Vector3d maxoff = Vector3d::ZERO;
859   uint num = getNumExtruders();
860   for (uint i = 0; i < num ; i++) {
861     string ext = numberedExtruder("Extruder",i);
862     double offx = 0, offy = 0;
863     try {
864       offx = abs(get_double(ext, "OffsetX"));
865       offy = abs(get_double(ext, "OffsetY"));
866     } catch (const Glib::KeyFileError &err) {
867     }
868     if (offx > abs(maxoff.x())) maxoff.x() = offx;
869     if (offy > abs(maxoff.y())) maxoff.y() = offy;
870   }
871   if (get_boolean("Slicing","Skirt")) {
872     double distance = get_double("Slicing","SkirtDistance");
873     maxoff += Vector3d(distance, distance, 0);
874   }
875   return margin + maxoff;
876 }
877 
878 
879 // Locate it in relation to ourselves ...
get_image_path()880 std::string Settings::get_image_path()
881 {
882   std::string basename = Glib::path_get_dirname(filename);
883   return Glib::build_filename (basename, get_string("Global","Image"));
884 }
885 
886 
887 
set_user_button(const string & name,const string & gcode)888 bool Settings::set_user_button(const string &name, const string &gcode) {
889   try {
890     vector<string> buttonlabels = get_string_list("UserButtons","Labels");
891     vector<string> buttongcodes = get_string_list("UserButtons","GCodes");
892     for (uint i = 0; i < buttonlabels.size(); i++){
893       if (buttonlabels[i] == name) {
894 	// change button
895 	buttongcodes[i] = gcode;
896 	set_string_list("UserButtons","GCodes",buttongcodes);
897       } else {
898 	// add button
899 	buttonlabels.push_back(name);
900 	buttongcodes.push_back(gcode);
901 	set_string_list("UserButtons","Labels",buttonlabels);
902 	set_string_list("UserButtons","GCodes",buttongcodes);
903       }
904     }
905   } catch (const Glib::KeyFileError &err) {
906   }
907   return true;
908 }
909 
get_user_gcode(const string & name)910 string Settings::get_user_gcode(const string &name) {
911   try {
912     vector<string> buttonlabels = get_string_list("UserButtons","Labels");
913     vector<string> buttongcodes = get_string_list("UserButtons","GCodes");
914     for (uint i = 0; i < buttonlabels.size(); i++){
915       if (buttonlabels[i] == name)
916 	return buttongcodes[i];
917     }
918   } catch (const Glib::KeyFileError &err) {
919   }
920   return "";
921 }
922 
del_user_button(const string & name)923 bool Settings::del_user_button(const string &name) {
924   try {
925     vector<string> buttonlabels = get_string_list("UserButtons","Labels");
926     vector<string> buttongcodes = get_string_list("UserButtons","GCodes");
927     for (uint i = 0; i < buttonlabels.size(); i++){
928       if (buttonlabels[i] == name) {
929 	buttonlabels.erase(buttonlabels.begin()+i);
930 	buttongcodes.erase(buttongcodes.begin()+i);
931 	return true;
932       }
933     }
934   } catch (const Glib::KeyFileError &err) {
935   }
936   return false;
937 }
938 
939