1 /*
2  * Copyright (C) 2008 Emweb bv, Herent, Belgium.
3  *
4  * See the LICENSE file for terms of use.
5  */
6 
7 #include "Wt/WApplication.h"
8 #include "Wt/WBootstrap5Theme.h"
9 #include "Wt/WInPlaceEdit.h"
10 #include "Wt/WCssDecorationStyle.h"
11 #include "Wt/WContainerWidget.h"
12 #include "Wt/WPushButton.h"
13 #include "Wt/WText.h"
14 #include "Wt/WTheme.h"
15 #include "Wt/WLineEdit.h"
16 
17 #include <memory>
18 
19 namespace Wt {
20 
WInPlaceEdit()21 WInPlaceEdit::WInPlaceEdit()
22 {
23   create();
24 }
25 
WInPlaceEdit(const WString & text)26 WInPlaceEdit::WInPlaceEdit(const WString& text)
27 {
28   create();
29   setText(text);
30 }
31 
WInPlaceEdit(bool buttons,const WString & text)32 WInPlaceEdit::WInPlaceEdit(bool buttons, const WString& text)
33 {
34   create();
35   setText(text);
36   setButtonsEnabled(buttons);
37 }
38 
create()39 void WInPlaceEdit::create()
40 {
41   setImplementation(std::unique_ptr<WWidget>(impl_ = new WContainerWidget()));
42   setInline(true);
43 
44   impl_->addWidget(std::unique_ptr<WWidget>
45 		   (text_ = new WText(WString::Empty, TextFormat::Plain)));
46   text_->decorationStyle().setCursor(Cursor::Arrow);
47 
48   impl_->addWidget(std::unique_ptr<WWidget>(editing_ = new WContainerWidget()));
49   editing_->setInline(true);
50   editing_->hide();
51 
52   editing_->addWidget(std::unique_ptr<WWidget>(edit_ = new WLineEdit()));
53   edit_->setTextSize(20);
54   save_ = nullptr;
55   cancel_ = nullptr;
56 
57   /*
58    * This is stateless implementation heaven
59    */
60   text_->clicked().connect(text_, &WWidget::hide);
61   text_->clicked().connect(editing_, &WWidget::show);
62   text_->clicked().connect(edit_, &WWidget::setFocus);
63 
64   edit_->enterPressed().connect(edit_, &WFormWidget::disable);
65   edit_->enterPressed().connect(this, &WInPlaceEdit::save);
66   edit_->enterPressed().preventPropagation();
67 
68   edit_->escapePressed().connect(editing_, &WWidget::hide);
69   edit_->escapePressed().connect(text_, &WWidget::show);
70   edit_->escapePressed().connect(this, &WInPlaceEdit::cancel);
71   edit_->escapePressed().preventPropagation();
72 
73   auto app = WApplication::instance();
74   auto bs5Theme = std::dynamic_pointer_cast<WBootstrap5Theme>(app->theme());
75 
76   if (!bs5Theme) {
77     editing_->addWidget
78       (std::unique_ptr<WWidget>(buttons_ = new WContainerWidget()));
79     buttons_->setInline(true);
80 
81     app->theme()->apply(this, buttons_, InPlaceEditingButtonsContainer);
82   }
83 
84   setButtonsEnabled();
85 }
86 
text()87 const WString& WInPlaceEdit::text() const
88 {
89   return edit_->text();
90 }
91 
setText(const WString & text)92 void WInPlaceEdit::setText(const WString& text)
93 {
94   empty_ = text.empty();
95 
96   if (!empty_)
97     text_->setText(text);
98   else
99     text_->setText(placeholderText());
100 
101   edit_->setText(text);
102 }
103 
setPlaceholderText(const WString & text)104 void WInPlaceEdit::setPlaceholderText(const WString& text)
105 {
106   placeholderText_ = text;
107 
108   edit_->setPlaceholderText(text);
109   if (empty_)
110     text_->setText(text);
111 }
112 
placeholderText()113 const WString& WInPlaceEdit::placeholderText() const
114 {
115   return placeholderText_;
116 }
117 
save()118 void WInPlaceEdit::save()
119 {
120   editing_->hide();
121   text_->show();
122   edit_->enable();
123   if (save_)
124     save_->enable();
125   if (cancel_)
126     cancel_->enable();
127 
128   bool changed
129     = empty_ ? !edit_->text().empty() : edit_->text() != text_->text();
130 
131   if (changed) {
132     setText(edit_->text());
133     valueChanged().emit(edit_->text());
134   }
135 }
136 
cancel()137 void WInPlaceEdit::cancel()
138 {
139   edit_->setText(empty_ ? WString::Empty : text_->text());
140 }
141 
setButtonsEnabled(bool enabled)142 void WInPlaceEdit::setButtonsEnabled(bool enabled)
143 {
144   if (enabled && !save_) {
145     c2_.disconnect();
146 
147     auto app = WApplication::instance();
148     auto bs5Theme = std::dynamic_pointer_cast<WBootstrap5Theme>(app->theme());
149 
150     if (!bs5Theme) {
151       buttons_->addWidget
152         (std::unique_ptr<WWidget>
153          (save_ = new WPushButton(tr("Wt.WInPlaceEdit.Save"))));
154       buttons_->addWidget
155         (std::unique_ptr<WWidget>
156          (cancel_ = new WPushButton(tr("Wt.WInPlaceEdit.Cancel"))));
157     } else {
158       editing_->addWidget (std::unique_ptr<WWidget>
159        (save_ = new WPushButton(tr("Wt.WInPlaceEdit.Save"))));
160       editing_->addWidget (std::unique_ptr<WWidget>
161        (cancel_ = new WPushButton(tr("Wt.WInPlaceEdit.Cancel"))));
162     }
163 
164     app->theme()->apply(this, save_, InPlaceEditingButton);
165     app->theme()->apply(this, cancel_, InPlaceEditingButton);
166 
167     save_->clicked().connect(edit_, &WFormWidget::disable);
168     save_->clicked().connect(save_, &WFormWidget::disable);
169     save_->clicked().connect(cancel_, &WFormWidget::disable);
170     save_->clicked().connect(this, &WInPlaceEdit::save);
171 
172     cancel_->clicked().connect(editing_, &WWidget::hide);
173     cancel_->clicked().connect(text_, &WWidget::show);
174     cancel_->clicked().connect(this, &WInPlaceEdit::cancel);
175   } else if (!enabled && save_) {
176     save_->parent()->removeWidget(save_);
177     cancel_->parent()->removeWidget(cancel_);
178     save_ = nullptr;
179     cancel_ = nullptr;
180 
181     c2_ = edit_->blurred().connect(this, &WInPlaceEdit::save);
182   }
183 }
184 
render(WFlags<RenderFlag> flags)185 void WInPlaceEdit::render(WFlags<RenderFlag> flags)
186 {
187   if (save_ && flags.test(RenderFlag::Full))
188     wApp->theme()->apply(this, editing_, InPlaceEditing);
189 
190   WCompositeWidget::render(flags);
191 }
192 }
193