1 | // ==================================================================== // |
---|
2 | // // |
---|
3 | // File : aw_advice.cpp // |
---|
4 | // Purpose : // |
---|
5 | // // |
---|
6 | // // |
---|
7 | // Coded by Ralf Westram (coder@reallysoft.de) in May 2002 // |
---|
8 | // Copyright Department of Microbiology (Technical University Munich) // |
---|
9 | // // |
---|
10 | // Visit our web site at: http://www.arb-home.de/ // |
---|
11 | // // |
---|
12 | // // |
---|
13 | // ==================================================================== // |
---|
14 | |
---|
15 | #include "aw_advice.hxx" |
---|
16 | #include "aw_window.hxx" |
---|
17 | #include "aw_awar.hxx" |
---|
18 | #include "aw_msg.hxx" |
---|
19 | #include "aw_root.hxx" |
---|
20 | |
---|
21 | #include <arbdb.h> |
---|
22 | |
---|
23 | using namespace std; |
---|
24 | |
---|
25 | #define AWAR_ADVICE_TMP "/tmp/advices/" |
---|
26 | |
---|
27 | #define AWAR_ADVICE_TEXT AWAR_ADVICE_TMP "text" |
---|
28 | #define AWAR_ADVICE_UNDERSTOOD AWAR_ADVICE_TMP "understood" |
---|
29 | |
---|
30 | #define AWAR_ADVICE_DISABLED "/advices/disabled" |
---|
31 | #define AWAR_ADVICE_SHOWN "/tmp/advices/shown" |
---|
32 | |
---|
33 | // ------------------------- |
---|
34 | // internal data : |
---|
35 | |
---|
36 | static bool initialized = false; |
---|
37 | static AW_root *advice_root = NULp; |
---|
38 | |
---|
39 | // -------------------------------------------------------- |
---|
40 | |
---|
41 | void init_Advisor(AW_root *awr) { |
---|
42 | if (!initialized) { |
---|
43 | advice_root = awr; |
---|
44 | |
---|
45 | advice_root->awar_string(AWAR_ADVICE_TEXT, "<no advice>"); |
---|
46 | advice_root->awar_int(AWAR_ADVICE_UNDERSTOOD, 0); |
---|
47 | |
---|
48 | initialized = true; |
---|
49 | } |
---|
50 | } |
---|
51 | |
---|
52 | // ---------------------------- |
---|
53 | // disabled advices : |
---|
54 | |
---|
55 | inline AW_awar *get_disabled_advices() { return advice_root->awar_string(AWAR_ADVICE_DISABLED, ""); } |
---|
56 | inline AW_awar *get_shown_advices() { return advice_root->awar_string(AWAR_ADVICE_SHOWN, ""); } |
---|
57 | |
---|
58 | inline int advice_id_offset(const char* id, const char *idlist) { |
---|
59 | const char *found = strstr(idlist, GBS_global_string(";%s;", id)); |
---|
60 | return found ? found-idlist : -1; |
---|
61 | } |
---|
62 | |
---|
63 | inline bool advice_id_is_set(const char* id, const char *idlist) { return advice_id_offset(id, idlist) >= 0; } |
---|
64 | |
---|
65 | inline void set_advice_id(const char* id, AW_awar *var) { |
---|
66 | const char *idlist = var->read_char_pntr(); |
---|
67 | if (!advice_id_is_set(id, idlist)) { |
---|
68 | if (idlist[0]) var->write_string(GBS_global_string("%s%s;", idlist, id)); |
---|
69 | else var->write_string(GBS_global_string(";%s;", id)); |
---|
70 | } |
---|
71 | } |
---|
72 | inline void remove_advice_id(const char* id, AW_awar *var) { |
---|
73 | const char *idlist = var->read_char_pntr(); |
---|
74 | if (advice_id_is_set(id, idlist)) { |
---|
75 | int offset = advice_id_offset(id, idlist); |
---|
76 | if (offset >= 0) { |
---|
77 | char *newList = NULp; |
---|
78 | if (offset == 0) { |
---|
79 | newList = strdup(idlist+strlen(id)+1); |
---|
80 | } |
---|
81 | else { |
---|
82 | newList = strdup(idlist); |
---|
83 | char *idPos = newList+offset; |
---|
84 | strcpy(idPos, idPos+strlen(id)+1); |
---|
85 | } |
---|
86 | var->write_string(newList); |
---|
87 | free(newList); |
---|
88 | } |
---|
89 | } |
---|
90 | } |
---|
91 | inline void toggle_advice_id(const char* id, AW_awar *var) { |
---|
92 | if (advice_id_is_set(id, var->read_char_pntr())) { |
---|
93 | remove_advice_id(id, var); |
---|
94 | } |
---|
95 | else { |
---|
96 | set_advice_id(id, var); |
---|
97 | } |
---|
98 | } |
---|
99 | |
---|
100 | inline bool advice_disabled(const char* id) { return advice_id_is_set(id, get_disabled_advices()->read_char_pntr()); } |
---|
101 | inline void disable_advice(const char* id) { set_advice_id(id, get_disabled_advices()); } |
---|
102 | |
---|
103 | inline bool advice_currently_shown(const char *id) { return advice_id_is_set(id, get_shown_advices()->read_char_pntr()); } |
---|
104 | inline void toggle_advice_shown(const char *id) { toggle_advice_id(id, get_shown_advices()); } |
---|
105 | |
---|
106 | // ------------------------------------------- |
---|
107 | |
---|
108 | static void advice_close_cb(AW_window *aww, const char *id, AW_Advice_Type type) { |
---|
109 | int understood = advice_root->awar(AWAR_ADVICE_UNDERSTOOD)->read_int(); |
---|
110 | |
---|
111 | // switch to 'not understood'. Has to be checked by user for each advice. |
---|
112 | advice_root->awar(AWAR_ADVICE_UNDERSTOOD)->write_int(0); |
---|
113 | aww->hide(); |
---|
114 | |
---|
115 | toggle_advice_shown(id); |
---|
116 | |
---|
117 | if (understood) { |
---|
118 | disable_advice(id); |
---|
119 | if (type & AW_ADVICE_TOGGLE) { |
---|
120 | static bool in_advice = false; |
---|
121 | if (!in_advice) { |
---|
122 | in_advice = true; |
---|
123 | AW_advice("You have disabled an advice.\n" |
---|
124 | "In order to disable it PERMANENTLY, save properties.", AW_ADVICE_TOGGLE, "Advice disabled", "advice.hlp"); |
---|
125 | in_advice = false; |
---|
126 | } |
---|
127 | } |
---|
128 | } |
---|
129 | } |
---|
130 | static void advice_hide_and_close_cb(AW_window *aww, const char *id, AW_Advice_Type type) { |
---|
131 | advice_root->awar(AWAR_ADVICE_UNDERSTOOD)->write_int(1); |
---|
132 | advice_close_cb(aww, id, type); |
---|
133 | } |
---|
134 | |
---|
135 | // ------------------------------------------- |
---|
136 | |
---|
137 | void AW_reactivate_all_advices(AW_window*) { |
---|
138 | AW_awar *awar_disabled = get_disabled_advices(); |
---|
139 | |
---|
140 | char *disabled = awar_disabled->read_string(); |
---|
141 | char *nosemi = GBS_string_eval(disabled, ";="); |
---|
142 | int entries = strlen(disabled)-strlen(nosemi); |
---|
143 | const char *msg = "No advices were disabled yet."; |
---|
144 | |
---|
145 | if (entries>0) { |
---|
146 | aw_assert(entries>1); |
---|
147 | entries--; |
---|
148 | msg = GBS_global_string("Reactivated %i advices (for this session)\n" |
---|
149 | "To reactivate them for future sessions, save properties.", |
---|
150 | entries); |
---|
151 | } |
---|
152 | aw_message(msg); |
---|
153 | |
---|
154 | free(nosemi); |
---|
155 | free(disabled); |
---|
156 | |
---|
157 | awar_disabled->write_string(""); |
---|
158 | } |
---|
159 | |
---|
160 | void AW_advice(const char *message, AW_Advice_Type type, const char *title, const char *corresponding_help) { |
---|
161 | aw_assert(initialized); |
---|
162 | size_t message_len = strlen(message); aw_assert(message_len>0); |
---|
163 | long crc32 = GB_checksum(message, message_len, true, " .,-!"); // checksum is used to test if advice was shown |
---|
164 | char *advice_id = GBS_global_string_copy("%lx", crc32); // do not delete (bound to cbs) |
---|
165 | |
---|
166 | bool show_advice = !advice_disabled(advice_id) && !advice_currently_shown(advice_id); |
---|
167 | |
---|
168 | if (show_advice) { |
---|
169 | AW_awar *understood = advice_root->awar(AWAR_ADVICE_UNDERSTOOD); |
---|
170 | understood->write_int(0); |
---|
171 | |
---|
172 | if (corresponding_help) type = AW_Advice_Type(type|AW_ADVICE_HELP); |
---|
173 | #if defined(ASSERTION_USED) |
---|
174 | else aw_assert((type & AW_ADVICE_HELP) == 0); |
---|
175 | #endif // ASSERTION_USED |
---|
176 | |
---|
177 | AW_window_simple *aws = new AW_window_simple; // do not delete (ARB will crash) -- maybe reuse window for all advices? |
---|
178 | |
---|
179 | if (!title) title = "Please read carefully"; |
---|
180 | aws->init(advice_root, "advice", GBS_global_string("ARB: %s", title)); |
---|
181 | aws->load_xfig("window/advice.fig"); |
---|
182 | |
---|
183 | bool has_help = type & AW_ADVICE_HELP; |
---|
184 | bool help_pops_up = false; |
---|
185 | |
---|
186 | if (has_help) { |
---|
187 | aws->callback(makeHelpCallback(corresponding_help)); |
---|
188 | aws->at("help"); |
---|
189 | aws->create_button(NULp, "HELP", "H"); |
---|
190 | |
---|
191 | if (type & AW_ADVICE_HELP_POPUP) help_pops_up = true; |
---|
192 | } |
---|
193 | |
---|
194 | aws->at("advice"); |
---|
195 | aws->create_text_field(AWAR_ADVICE_TEXT); |
---|
196 | |
---|
197 | advice_root->awar(AWAR_ADVICE_TEXT) |
---|
198 | ->write_string(has_help && !help_pops_up |
---|
199 | ? GBS_global_string("%s\n\nPlease refer to 'HELP' for more info.", message) |
---|
200 | : message); |
---|
201 | |
---|
202 | if (help_pops_up) AW_help_popup(NULp, corresponding_help); |
---|
203 | |
---|
204 | if (type & AW_ADVICE_TOGGLE) { |
---|
205 | aws->label("Do not advice me again"); |
---|
206 | aws->at("understood"); |
---|
207 | aws->create_toggle(AWAR_ADVICE_UNDERSTOOD); |
---|
208 | } |
---|
209 | |
---|
210 | aws->at("ok"); |
---|
211 | if (type & AW_ADVICE_TOGGLE) { |
---|
212 | aws->callback(makeWindowCallback(advice_close_cb, advice_id, type)); |
---|
213 | aws->create_button(NULp, "OK", "O"); |
---|
214 | } |
---|
215 | else { |
---|
216 | aws->callback(makeWindowCallback(advice_hide_and_close_cb, advice_id, type)); |
---|
217 | aws->create_autosize_button(NULp, "I understand", "O", 2); |
---|
218 | } |
---|
219 | |
---|
220 | aws->window_fit(); |
---|
221 | aws->allow_delete_window(false); // disable closing the window via 'X'-button |
---|
222 | aws->show(); |
---|
223 | toggle_advice_shown(advice_id); |
---|
224 | } |
---|
225 | } |
---|
226 | |
---|
227 | // -------------------------------------------------------------------------------- |
---|
228 | |
---|
229 | #ifdef UNIT_TESTS |
---|
230 | #include <test_unit.h> |
---|
231 | |
---|
232 | void TEST_advice_id_awar_handling() { |
---|
233 | GB_shell shell; |
---|
234 | AW_root root("min_ascii.arb", UNITTEST_MOCK); |
---|
235 | init_Advisor(&root); |
---|
236 | |
---|
237 | const char *one = "one"; |
---|
238 | const char *two = "second"; |
---|
239 | |
---|
240 | TEST_REJECT(advice_disabled(one)); |
---|
241 | TEST_REJECT(advice_disabled(two)); |
---|
242 | |
---|
243 | disable_advice(one); |
---|
244 | TEST_EXPECT(advice_disabled(one)); |
---|
245 | TEST_REJECT(advice_disabled(two)); |
---|
246 | |
---|
247 | disable_advice(two); |
---|
248 | TEST_EXPECT(advice_disabled(one)); |
---|
249 | TEST_EXPECT(advice_disabled(two)); |
---|
250 | |
---|
251 | |
---|
252 | TEST_REJECT(advice_currently_shown(one)); |
---|
253 | TEST_REJECT(advice_currently_shown(two)); |
---|
254 | |
---|
255 | toggle_advice_shown(two); |
---|
256 | TEST_REJECT(advice_currently_shown(one)); |
---|
257 | TEST_EXPECT(advice_currently_shown(two)); |
---|
258 | |
---|
259 | toggle_advice_shown(one); |
---|
260 | TEST_EXPECT(advice_currently_shown(one)); |
---|
261 | TEST_EXPECT(advice_currently_shown(two)); |
---|
262 | |
---|
263 | toggle_advice_shown(two); |
---|
264 | TEST_EXPECT(advice_currently_shown(one)); |
---|
265 | TEST_REJECT(advice_currently_shown(two)); |
---|
266 | |
---|
267 | toggle_advice_shown(one); |
---|
268 | TEST_REJECT(advice_currently_shown(one)); |
---|
269 | TEST_REJECT(advice_currently_shown(two)); |
---|
270 | } |
---|
271 | |
---|
272 | void TEST_another_AW_root() { |
---|
273 | GB_shell shell; |
---|
274 | AW_root("min_ascii.arb", UNITTEST_MOCK); |
---|
275 | } |
---|
276 | TEST_PUBLISH(TEST_another_AW_root); |
---|
277 | |
---|
278 | #endif // UNIT_TESTS |
---|