1 // TimerPreferencesPanel.cc --- Preferences widgets for a timer
2 //
3 // Copyright (C) 2002 - 2013 Raymond Penners <raymond@dotsphinx.com>
4 // All rights reserved.
5 //
6 // This program is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation, either version 3 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 General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 //
19 
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #include "preinclude.h"
25 
26 #ifdef HAVE_UNISTD_H
27 #include <unistd.h>
28 #endif
29 
30 #include <gtkmm/spinbutton.h>
31 #include <gtkmm/checkbutton.h>
32 
33 #include "nls.h"
34 #include "debug.hh"
35 
36 #include "CoreFactory.hh"
37 #include "ICore.hh"
38 #include "IConfigurator.hh"
39 #include "IBreak.hh"
40 
41 #include "TimeEntry.hh"
42 #include "TimerPreferencesPanel.hh"
43 #include "GtkUtil.hh"
44 #include "Hig.hh"
45 
46 #include "GUIConfig.hh"
47 #include "CoreConfig.hh"
48 #include "DataConnector.hh"
49 
50 using namespace std;
51 
TimerPreferencesPanel(BreakId t,Glib::RefPtr<Gtk::SizeGroup> hsize_group,Glib::RefPtr<Gtk::SizeGroup> vsize_group)52 TimerPreferencesPanel::TimerPreferencesPanel
53 (BreakId t,
54  Glib::RefPtr<Gtk::SizeGroup> hsize_group,
55  Glib::RefPtr<Gtk::SizeGroup> vsize_group)
56   : Gtk::VBox(false, 6)
57 #ifdef HAVE_GTK3
58   ,max_prelude_adjustment(Gtk::Adjustment::create(0, 1, 100))
59 # ifdef HAVE_EXERCISES
60   ,exercises_adjustment(Gtk::Adjustment::create(0, 0, 10))
61 # endif
62 #else
63   ,max_prelude_adjustment(0, 1, 100)
64 # ifdef HAVE_EXERCISES
65   ,exercises_adjustment(0, 0, 10)
66 # endif
67 #endif
68 {
69   connector = new DataConnector();
70   break_id = t;
71 
72   Gtk::HBox *box = Gtk::manage(new Gtk::HBox(false, 6));
73 
74   // Enabled/Disabled checkbox
75   Gtk::Label *enabled_lab = Gtk::manage(GtkUtil::create_label(_("Enable timer"), true));
76   enabled_cb = Gtk::manage(new Gtk::CheckButton());
77   enabled_cb->add(*enabled_lab);
78   enabled_cb->signal_toggled().connect(sigc::mem_fun(*this, &TimerPreferencesPanel::on_enabled_toggled));
79 
80 
81   HigCategoriesPanel *categories = Gtk::manage(new HigCategoriesPanel());
82 
83   Gtk::Widget *prelude_frame = Gtk::manage(create_prelude_panel());
84   Gtk::Widget *timers_frame = Gtk::manage(create_timers_panel
85                                      (hsize_group, vsize_group));
86   Gtk::Widget *opts_frame = Gtk::manage(create_options_panel());
87 
88   categories->add(*timers_frame);
89   categories->add(*opts_frame);
90 
91   enable_buttons();
92 
93   // Overall box
94   box->pack_start(*categories, false, false, 0);
95   box->pack_start(*prelude_frame, false, false, 0);
96 
97   pack_start(*enabled_cb, false, false, 0);
98   pack_start(*box, false, false, 0);
99 
100   connector->connect(CoreConfig::CFG_KEY_BREAK_ENABLED % break_id, dc::wrap(enabled_cb));
101 
102   set_border_width(12);
103 }
104 
105 
~TimerPreferencesPanel()106 TimerPreferencesPanel::~TimerPreferencesPanel()
107 {
108   delete connector;
109 }
110 
111 
112 Gtk::Widget *
create_prelude_panel()113 TimerPreferencesPanel::create_prelude_panel()
114 {
115   // Prelude frame
116   HigCategoryPanel *hig = Gtk::manage(new HigCategoryPanel(_("Break prompting")));
117 
118   prelude_cb = Gtk::manage(new Gtk::CheckButton(_("Prompt before breaking")));
119   hig->add_widget(*prelude_cb);
120 
121   Gtk::HBox *max_box = Gtk::manage(new Gtk::HBox());
122   has_max_prelude_cb = Gtk::manage(new Gtk::CheckButton(_("Maximum number of prompts:")));
123   max_prelude_spin = Gtk::manage(new Gtk::SpinButton(max_prelude_adjustment));
124   max_box->pack_start(*has_max_prelude_cb, false, false, 0);
125   max_box->pack_start(*max_prelude_spin, false, false, 0);
126   hig->add_widget(*max_box);
127 
128   connector->connect(CoreConfig::CFG_KEY_BREAK_MAX_PRELUDES % break_id,
129                      dc::wrap(prelude_cb),
130                      sigc::mem_fun(*this, &TimerPreferencesPanel::on_preludes_changed));
131 
132   connector->connect(CoreConfig::CFG_KEY_BREAK_MAX_PRELUDES % break_id,
133                      dc::wrap(has_max_prelude_cb),
134                      sigc::mem_fun(*this, &TimerPreferencesPanel::on_preludes_changed),
135                      dc::NO_CONFIG);
136 
137   connector->connect(CoreConfig::CFG_KEY_BREAK_MAX_PRELUDES % break_id,
138                      dc::wrap(max_prelude_spin),
139                      sigc::mem_fun(*this, &TimerPreferencesPanel::on_preludes_changed),
140                      dc::NO_CONFIG);
141   return hig;
142 }
143 
144 Gtk::Widget *
create_options_panel()145 TimerPreferencesPanel::create_options_panel()
146 {
147   HigCategoryPanel *hig = Gtk::manage(new HigCategoryPanel(_("Options")));
148 
149   // Ignorable
150   ignorable_cb = Gtk::manage(new Gtk::CheckButton
151                              (_("Show 'Postpone' button")));
152   hig->add_widget(*ignorable_cb);
153 
154   // Skippable
155   skippable_cb = Gtk::manage(new Gtk::CheckButton
156                              (_("Show 'Skip' button")));
157   hig->add_widget(*skippable_cb);
158 
159   // Sensitive for activity
160   activity_sensitive_cb = Gtk::manage(new Gtk::CheckButton
161                                       (_("Suspend timer when inactive")));
162 #if REMOVED_IN_FAVOR_OF_READING_MODE__REMOVE_LATER
163   hig->add(*activity_sensitive_cb);
164 #endif
165 
166   // Break specific options
167 #ifdef HAVE_EXERCISES
168   exercises_spin = NULL;
169 #endif
170 
171 #ifdef HAVE_MICRO_BREAK_ACTIVITY
172   monitor_cb = NULL;
173   auto_natural_cb = NULL;
174   allow_shutdown_cb = NULL;
175 
176   if (break_id == BREAK_ID_DAILY_LIMIT)
177     {
178       monitor_cb
179         = Gtk::manage(new Gtk::CheckButton(_("Regard micro-breaks as activity")));
180       hig->add_widget(*monitor_cb);
181     }
182 #endif
183 
184 #ifdef HAVE_EXERCISES
185   if (break_id == BREAK_ID_REST_BREAK)
186     {
187       exercises_spin = Gtk::manage(new Gtk::SpinButton(exercises_adjustment));
188       hig->add_label(_("Number of exercises:"), *exercises_spin);
189     }
190 #endif
191   if (break_id == BREAK_ID_REST_BREAK)
192     {
193       auto_natural_cb = Gtk::manage(new Gtk::CheckButton(_("Start restbreak when screen is locked")));
194       hig->add_widget(*auto_natural_cb);
195 
196       connector->connect(GUIConfig::CFG_KEY_BREAK_AUTO_NATURAL % break_id,
197                          dc::wrap(auto_natural_cb));
198 
199       allow_shutdown_cb = Gtk::manage(new Gtk::CheckButton(_("Enable shutting down the computer from the rest screen")));
200       hig->add_widget(*allow_shutdown_cb);
201 
202       connector->connect(GUIConfig::CFG_KEY_BREAK_ENABLE_SHUTDOWN % break_id,
203                          dc::wrap(allow_shutdown_cb));
204     }
205 
206   connector->connect(CoreConfig::CFG_KEY_TIMER_ACTIVITY_SENSITIVE % break_id,
207                      dc::wrap(activity_sensitive_cb));
208 
209   connector->connect(GUIConfig::CFG_KEY_BREAK_IGNORABLE % break_id,
210                      dc::wrap(ignorable_cb));
211 
212   connector->connect(GUIConfig::CFG_KEY_BREAK_SKIPPABLE % break_id,
213                      dc::wrap(skippable_cb));
214 
215 
216 #ifdef HAVE_EXERCISES
217   if (break_id == BREAK_ID_REST_BREAK)
218     {
219       connector->connect(GUIConfig::CFG_KEY_BREAK_EXERCISES % break_id,
220                          dc::wrap(exercises_spin));
221     }
222 #endif
223 
224   connector->connect(CoreConfig::CFG_KEY_TIMER_MONITOR % break_id,
225                      dc::wrap(monitor_cb),
226                      sigc::mem_fun(*this, &TimerPreferencesPanel::on_monitor_changed));
227 
228   return hig;
229 }
230 
231 Gtk::Widget *
create_timers_panel(Glib::RefPtr<Gtk::SizeGroup> hsize_group,Glib::RefPtr<Gtk::SizeGroup> vsize_group)232 TimerPreferencesPanel::create_timers_panel
233 (Glib::RefPtr<Gtk::SizeGroup> hsize_group,
234  Glib::RefPtr<Gtk::SizeGroup> vsize_group)
235 {
236   HigCategoryPanel *hig = Gtk::manage(new HigCategoryPanel(_("Timers")));
237 
238   // Limit time
239   limit_tim = Gtk::manage(new TimeEntry());
240   Gtk::Label *limit_lab = hig->add_label(break_id == BREAK_ID_DAILY_LIMIT
241            ? _("Time before end:")
242            : _("Time between breaks:"), *limit_tim);
243   hsize_group->add_widget(*limit_lab);
244 
245 
246   // Auto-reset time
247   if (break_id != BREAK_ID_DAILY_LIMIT)
248     {
249       const char *auto_reset_txt = _("Break duration:");
250 
251       auto_reset_tim = Gtk::manage(new TimeEntry());
252 
253       Gtk::Label *auto_reset_lab = Gtk::manage(new Gtk::Label(auto_reset_txt));
254       hsize_group->add_widget(*auto_reset_lab);
255       hig->add_label(*auto_reset_lab, *auto_reset_tim);
256     }
257   else
258     {
259       auto_reset_tim = NULL;
260     }
261 
262   // Snooze time
263   snooze_tim = Gtk::manage(new TimeEntry());
264   Gtk::Label *snooze_lab = hig->add_label(_("Postpone time:"), *snooze_tim);
265   hsize_group->add_widget(*snooze_lab);
266 
267   vsize_group->add_widget(*hig);
268 
269   connector->connect(CoreConfig::CFG_KEY_TIMER_LIMIT % break_id, dc::wrap(limit_tim));
270   connector->connect(CoreConfig::CFG_KEY_TIMER_AUTO_RESET % break_id, dc::wrap(auto_reset_tim));
271   connector->connect(CoreConfig::CFG_KEY_TIMER_SNOOZE % break_id, dc::wrap(snooze_tim));
272 
273   return hig;
274 }
275 
276 
277 void
set_prelude_sensitivity()278 TimerPreferencesPanel::set_prelude_sensitivity()
279 {
280   bool on = enabled_cb->get_active();
281   bool has_preludes = prelude_cb->get_active();
282   bool has_max = has_max_prelude_cb->get_active();
283   has_max_prelude_cb->set_sensitive(has_preludes && on);
284   max_prelude_spin->set_sensitive(has_preludes && has_max && on);
285 }
286 
287 bool
on_preludes_changed(const std::string & key,bool write)288 TimerPreferencesPanel::on_preludes_changed(const std::string &key, bool write)
289 {
290   static bool inside = false;
291 
292   if (inside)
293     return true;
294 
295   inside = true;
296 
297   IConfigurator *config = CoreFactory::get_configurator();
298   if (write)
299     {
300       int mp;
301       if (prelude_cb->get_active())
302         {
303           if (has_max_prelude_cb->get_active())
304             {
305 #ifdef HAVE_GTK3
306               mp = (int) max_prelude_adjustment->get_value();
307 #else
308               mp = (int) max_prelude_adjustment.get_value();
309 #endif
310             }
311           else
312             {
313               mp = -1;
314             }
315         }
316       else
317         {
318           mp = 0;
319         }
320       config->set_value(key, mp);
321       set_prelude_sensitivity();
322     }
323   else
324     {
325       int value;
326       bool ok = config->get_value(key, value);
327       if (ok)
328         {
329           if (value == -1)
330             {
331               prelude_cb->set_active(true);
332               has_max_prelude_cb->set_active(false);
333             }
334           else if (value == 0)
335             {
336               prelude_cb->set_active(false);
337               has_max_prelude_cb->set_active(false);
338             }
339           else
340             {
341               prelude_cb->set_active(true);
342               has_max_prelude_cb->set_active(true);
343 #ifdef HAVE_GTK3
344               max_prelude_adjustment->set_value(value);
345 #else
346               max_prelude_adjustment.set_value(value);
347 #endif
348             }
349 
350           set_prelude_sensitivity();
351         }
352     }
353 
354   inside = false;
355 
356   return true;
357 }
358 
359 
360 #ifdef HAVE_MICRO_BREAK_ACTIVITY
361 bool
on_monitor_changed(const string & key,bool write)362 TimerPreferencesPanel::on_monitor_changed(const string &key, bool write)
363 {
364   IConfigurator *config = CoreFactory::get_configurator();
365 
366   if (write)
367     {
368       string val;
369 
370       if (monitor_cb->get_active())
371         {
372           ICore *core = CoreFactory::get_core();
373           IBreak *mp_break = core->get_break(BREAK_ID_MICRO_BREAK);
374           val = mp_break->get_name();
375         }
376 
377       config->set_value(key, val);
378     }
379   else
380     {
381       string monitor_name;
382       bool ok = config->get_value(key, monitor_name);
383       if (ok && monitor_name != "")
384         {
385           bool s = monitor_cb->is_sensitive();
386           monitor_cb->set_active(monitor_name != "");
387           monitor_cb->set_sensitive(s);
388         }
389     }
390 
391   return true;
392 }
393 #endif
394 
395 
396 void
on_enabled_toggled()397 TimerPreferencesPanel::on_enabled_toggled()
398 {
399   enable_buttons();
400   set_prelude_sensitivity();
401 }
402 
403 
404 //! Enable widgets
405 void
enable_buttons()406 TimerPreferencesPanel::enable_buttons()
407 {
408   bool on = enabled_cb->get_active();
409 
410   ignorable_cb->set_sensitive(on);
411   skippable_cb->set_sensitive(on);
412   activity_sensitive_cb->set_sensitive(on);
413 
414 #ifdef HAVE_MICRO_BREAK_ACTIVITY
415   if (monitor_cb != NULL)
416     {
417       monitor_cb->set_sensitive(on);
418     }
419 #endif
420 
421   prelude_cb->set_sensitive(on);
422   has_max_prelude_cb->set_sensitive(on);
423   limit_tim->set_sensitive(on);
424 
425   if (auto_reset_tim != NULL)
426     {
427       auto_reset_tim->set_sensitive(on);
428     }
429 
430   snooze_tim->set_sensitive(on);
431 
432 #ifdef HAVE_EXERCISES
433   if (exercises_spin != NULL)
434     {
435       exercises_spin->set_sensitive(on);
436     }
437 #endif
438 
439   if (auto_natural_cb != NULL)
440     {
441       auto_natural_cb->set_sensitive(on);
442     }
443 
444   if (allow_shutdown_cb != NULL)
445     {
446       allow_shutdown_cb->set_sensitive(on);
447     }
448 
449   // max_prelude_spin->set_sensitive(on);
450 }
451