1 | // =============================================================== // |
---|
2 | // // |
---|
3 | // File : SEC_graphic.cxx // |
---|
4 | // Purpose : GUI for structure window // |
---|
5 | // // |
---|
6 | // Institute of Microbiology (Technical University Munich) // |
---|
7 | // http://www.arb-home.de/ // |
---|
8 | // // |
---|
9 | // =============================================================== // |
---|
10 | |
---|
11 | #include "SEC_graphic.hxx" |
---|
12 | #include "SEC_root.hxx" |
---|
13 | #include "SEC_iter.hxx" |
---|
14 | #include "SEC_toggle.hxx" |
---|
15 | |
---|
16 | #include <ed4_extern.hxx> |
---|
17 | |
---|
18 | #include <aw_awars.hxx> |
---|
19 | #include <aw_global.hxx> |
---|
20 | #include <aw_preset.hxx> |
---|
21 | #include <aw_msg.hxx> |
---|
22 | #include <aw_root.hxx> |
---|
23 | #include <aw_question.hxx> |
---|
24 | #include <arbdbt.h> |
---|
25 | #include <ad_cb.h> |
---|
26 | |
---|
27 | #include <vector> |
---|
28 | |
---|
29 | using namespace std; |
---|
30 | |
---|
31 | AW_gc_manager *SEC_graphic::init_devices(AW_window *aww, AW_device *device, AWT_canvas *scr) { |
---|
32 | AW_gc_manager *gc_manager = |
---|
33 | AW_manage_GC(aww, |
---|
34 | scr->get_gc_base_name(), |
---|
35 | device, |
---|
36 | SEC_GC_MAX, |
---|
37 | AW_GCM_DATA_AREA, |
---|
38 | makeGcChangedCallback(AWT_GC_changed_cb, scr), |
---|
39 | "#A1A1A1", |
---|
40 | "LOOP$#247900", |
---|
41 | "HELIX$#085DAB", |
---|
42 | "NONPAIRING HELIX$#D52B69", |
---|
43 | "DEFAULT$#000000", |
---|
44 | "BONDS$#000000", |
---|
45 | "ECOLI POSITION$#FFE223", |
---|
46 | "HELIX NUMBERS$#D4D4D4", |
---|
47 | |
---|
48 | // Color Ranges to paint SAIs |
---|
49 | "+-RANGE 0$#FFFFFF", "+-RANGE 1$#E0E0E0", "-RANGE 2$#C0C0C0", |
---|
50 | "+-RANGE 3$#A0A0A0", "+-RANGE 4$#909090", "-RANGE 5$#808080", |
---|
51 | "+-RANGE 6$#808080", "+-RANGE 7$#505050", "-RANGE 8$#404040", |
---|
52 | "+-RANGE 9$#303030", "+-CURSOR$#BF1515", "-MISMATCHES$#FF9AFF", |
---|
53 | |
---|
54 | // colors used to Paint search patterns |
---|
55 | // (do not change the names of these gcs) |
---|
56 | "+-User1$#B8E2F8", "+-User2$#B8E2F8", "-Probe$#B8E2F8", |
---|
57 | "+-Primer(l)$#A9FE54", "+-Primer(r)$#A9FE54", "-Primer(g)$#A9FE54", |
---|
58 | "+-Sig(l)$#DBB0FF", "+-Sig(r)$#DBB0FF", "-Sig(g)$#DBB0FF", |
---|
59 | |
---|
60 | // colors used to paint the skeleton of the structure |
---|
61 | "+-SKELETON HELIX${HELIX}", "+-SKELETON LOOP${LOOP}", "-SKELETON NONHELIX${NONPAIRING HELIX}", |
---|
62 | NULp); |
---|
63 | |
---|
64 | return gc_manager; |
---|
65 | } |
---|
66 | |
---|
67 | static GB_ERROR change_constraints(SEC_base *elem) { |
---|
68 | GB_ERROR error = NULp; |
---|
69 | GB_CSTR constraint_type = NULp; |
---|
70 | GB_CSTR element_type = NULp; |
---|
71 | |
---|
72 | switch (elem->getType()) { |
---|
73 | case SEC_HELIX: |
---|
74 | constraint_type = "length"; |
---|
75 | element_type = "helix"; |
---|
76 | break; |
---|
77 | case SEC_LOOP: |
---|
78 | constraint_type = "radius"; |
---|
79 | element_type = "loop"; |
---|
80 | break; |
---|
81 | default: |
---|
82 | sec_assert(0); |
---|
83 | error = "Illegal element type"; |
---|
84 | break; |
---|
85 | } |
---|
86 | |
---|
87 | if (!error) { |
---|
88 | char *question = GBS_global_string_copy("%s-constraints for %s", constraint_type, element_type); |
---|
89 | char *answer = aw_input(question, GBS_global_string("%.2f-%.2f", elem->minSize(), elem->maxSize())); |
---|
90 | |
---|
91 | while (answer) { |
---|
92 | char *end; |
---|
93 | double low = strtod(answer, &end); |
---|
94 | |
---|
95 | if (end[0]!='-') { |
---|
96 | error = "Wrong format! Wanted format is 'lower-upper'"; |
---|
97 | } |
---|
98 | else { |
---|
99 | double high = strtod(end+1, NULp); |
---|
100 | |
---|
101 | if (low<0 || high<0 || (low && high && low>high)) { |
---|
102 | error = "Illegal values"; |
---|
103 | } |
---|
104 | else { |
---|
105 | #if defined(DEBUG) |
---|
106 | sec_assert(!low || !high || low<=high); |
---|
107 | #endif // DEBUG |
---|
108 | elem->setConstraints(low, high); |
---|
109 | break; |
---|
110 | } |
---|
111 | } |
---|
112 | |
---|
113 | sec_assert(error); |
---|
114 | aw_message(error); |
---|
115 | |
---|
116 | char *retry = aw_input(question, answer); |
---|
117 | free(answer); |
---|
118 | |
---|
119 | answer = retry; |
---|
120 | } |
---|
121 | |
---|
122 | free(answer); |
---|
123 | free(question); |
---|
124 | } |
---|
125 | return error; |
---|
126 | } |
---|
127 | |
---|
128 | |
---|
129 | GB_ERROR SEC_graphic::handleKey(AW_event_type event, AW_key_mod key_modifier, AW_key_code key_code, char key_char) { |
---|
130 | GB_ERROR error = NULp; |
---|
131 | |
---|
132 | if (event == AW_Keyboard_Press) { |
---|
133 | int curpos = sec_root->get_cursor(); |
---|
134 | int maxIndex = sec_root->max_index(); |
---|
135 | bool setCurpos = false; |
---|
136 | bool handled = false; |
---|
137 | |
---|
138 | if (key_modifier == AW_KEYMODE_NONE) { |
---|
139 | bool wrapped = false; // avoid deadlock |
---|
140 | |
---|
141 | switch (key_code) { |
---|
142 | case AW_KEY_LEFT: { |
---|
143 | while (1) { |
---|
144 | curpos--; |
---|
145 | if (curpos<0) { |
---|
146 | curpos = maxIndex; |
---|
147 | if (wrapped) break; |
---|
148 | wrapped = true; |
---|
149 | } |
---|
150 | if (sec_root->shallDisplayPosition(curpos)) { |
---|
151 | setCurpos = true; |
---|
152 | break; |
---|
153 | } |
---|
154 | } |
---|
155 | break; |
---|
156 | } |
---|
157 | case AW_KEY_RIGHT: { |
---|
158 | while (1) { |
---|
159 | curpos++; |
---|
160 | if (curpos > maxIndex) { |
---|
161 | curpos = 0; |
---|
162 | if (wrapped) break; |
---|
163 | wrapped = true; |
---|
164 | } |
---|
165 | if (sec_root->shallDisplayPosition(curpos)) { |
---|
166 | setCurpos = true; |
---|
167 | break; |
---|
168 | } |
---|
169 | } |
---|
170 | break; |
---|
171 | } |
---|
172 | case AW_KEY_ASCII: { |
---|
173 | const char *toggle_awar = NULp; |
---|
174 | int val_max = 1; |
---|
175 | |
---|
176 | switch (key_char) { |
---|
177 | case 'b': toggle_awar = AWAR_SECEDIT_SHOW_BONDS; val_max = 2; break; |
---|
178 | case 'B': toggle_awar = AWAR_SECEDIT_HIDE_BASES; break; |
---|
179 | case 'k': toggle_awar = AWAR_SECEDIT_SHOW_STR_SKELETON; break; |
---|
180 | |
---|
181 | case 'c': toggle_awar = AWAR_SECEDIT_SHOW_CURPOS; val_max = 3; break; |
---|
182 | case 'h': toggle_awar = AWAR_SECEDIT_SHOW_HELIX_NRS; break; |
---|
183 | case 'e': toggle_awar = AWAR_SECEDIT_SHOW_ECOLI_POS; break; |
---|
184 | |
---|
185 | case 's': toggle_awar = AWAR_SECEDIT_DISPLAY_SAI; break; |
---|
186 | case 'r': toggle_awar = AWAR_SECEDIT_DISPLAY_SEARCH; break; |
---|
187 | |
---|
188 | case 'E': toggle_awar = AWAR_SECEDIT_DISPPOS_ECOLI; break; |
---|
189 | case 'H': toggle_awar = AWAR_SECEDIT_DISPPOS_BINDING; break; |
---|
190 | |
---|
191 | #if defined(DEBUG) |
---|
192 | case 'd': toggle_awar = AWAR_SECEDIT_SHOW_DEBUG; break; |
---|
193 | #endif // DEBUG |
---|
194 | |
---|
195 | case 't': |
---|
196 | error = sec_root->get_db()->structure()->next(); |
---|
197 | handled = true; |
---|
198 | break; |
---|
199 | } |
---|
200 | |
---|
201 | if (toggle_awar) { |
---|
202 | AW_awar *awar = aw_root->awar(toggle_awar); |
---|
203 | int val = awar->read_int()+1; |
---|
204 | |
---|
205 | if (val>val_max) val = 0; |
---|
206 | awar->write_int(val); |
---|
207 | |
---|
208 | handled = true; |
---|
209 | } |
---|
210 | |
---|
211 | break; |
---|
212 | } |
---|
213 | default: |
---|
214 | break; |
---|
215 | } |
---|
216 | } |
---|
217 | |
---|
218 | if (setCurpos) { |
---|
219 | aw_root->awar_int(AWAR_SET_CURSOR_POSITION)->write_int(curpos); |
---|
220 | handled = true; |
---|
221 | } |
---|
222 | |
---|
223 | if (!handled) { // pass unhandled key events to EDIT4 |
---|
224 | AW_event faked_event; |
---|
225 | |
---|
226 | memset((char*)&faked_event, 0, sizeof(faked_event)); |
---|
227 | |
---|
228 | faked_event.type = event; |
---|
229 | faked_event.keymodifier = key_modifier; |
---|
230 | faked_event.keycode = key_code; |
---|
231 | faked_event.character = key_char; |
---|
232 | |
---|
233 | sec_root->host().forward_event(&faked_event); |
---|
234 | } |
---|
235 | } |
---|
236 | |
---|
237 | return error; |
---|
238 | } |
---|
239 | |
---|
240 | GB_ERROR SEC_graphic::handleMouse(AW_device *device, AW_event_type event, int button, AWT_COMMAND_MODE cmd, const Position& world, SEC_base * const elem, int abspos) { |
---|
241 | GB_ERROR error = NULp; |
---|
242 | |
---|
243 | sec_assert(elem); // always contains the element targetted by the initial button-down |
---|
244 | |
---|
245 | // ------------------------------------------ |
---|
246 | // handle element dependent actions |
---|
247 | |
---|
248 | static Position start; // click position on mouse down |
---|
249 | |
---|
250 | Position fixpoint = elem->get_fixpoint(); // of strand or loop |
---|
251 | |
---|
252 | SEC_loop *loop = NULp; |
---|
253 | SEC_helix *helix = NULp; |
---|
254 | |
---|
255 | if (elem->getType() == SEC_HELIX) { |
---|
256 | helix = static_cast<SEC_helix*>(elem); |
---|
257 | } |
---|
258 | else { |
---|
259 | sec_assert(elem->getType() == SEC_LOOP); |
---|
260 | loop = static_cast<SEC_loop*>(elem); |
---|
261 | } |
---|
262 | |
---|
263 | if (event == AW_Mouse_Press) start = world; // store start position |
---|
264 | |
---|
265 | switch (cmd) { |
---|
266 | case AWT_MODE_STRETCH: { // change constraints with mouse |
---|
267 | static double start_size; // helix/loop size at start click |
---|
268 | |
---|
269 | switch (event) { |
---|
270 | case AW_Mouse_Press: |
---|
271 | if (button == AW_BUTTON_LEFT) { |
---|
272 | start_size = elem->drawnSize(); |
---|
273 | sec_root->set_show_constraints(elem->getType()); |
---|
274 | exports.request_refresh(); |
---|
275 | } |
---|
276 | else { // right button -> reset constraints |
---|
277 | elem->setConstraints(0, 0); |
---|
278 | elem->sizeChanged(); |
---|
279 | exports.request_save(); |
---|
280 | } |
---|
281 | break; |
---|
282 | |
---|
283 | case AW_Mouse_Drag: |
---|
284 | if (button == AW_BUTTON_LEFT) { |
---|
285 | double dfix1 = Distance(fixpoint, start); |
---|
286 | double dfix2 = Distance(fixpoint, world); |
---|
287 | |
---|
288 | if (dfix1>0 && dfix2>0) { |
---|
289 | double factor = dfix2/dfix1; |
---|
290 | double new_size = start_size*factor; |
---|
291 | |
---|
292 | elem->setDrawnSize(new_size); |
---|
293 | elem->sizeChanged(); |
---|
294 | |
---|
295 | exports.request_refresh(); |
---|
296 | } |
---|
297 | } |
---|
298 | break; |
---|
299 | |
---|
300 | case AW_Mouse_Release: |
---|
301 | sec_root->set_show_constraints(SEC_ANY_TYPE); |
---|
302 | exports.request_save(); |
---|
303 | break; |
---|
304 | |
---|
305 | default: sec_assert(0); break; |
---|
306 | } |
---|
307 | break; |
---|
308 | } |
---|
309 | case AWT_MODE_EDIT: // edit constraints |
---|
310 | if (button==AW_BUTTON_LEFT && event==AW_Mouse_Press) { |
---|
311 | error = change_constraints(elem); |
---|
312 | if (!error) { |
---|
313 | elem->sizeChanged(); |
---|
314 | exports.request_save(); |
---|
315 | } |
---|
316 | } |
---|
317 | break; |
---|
318 | |
---|
319 | case AWT_MODE_ROTATE: { // rotate branches/loops |
---|
320 | if (event == AW_Mouse_Release) { |
---|
321 | exports.request_save(); |
---|
322 | } |
---|
323 | else { |
---|
324 | static Angle startClick; // angle between fixpoint (of loop or helix) and first-click |
---|
325 | static bool rotateSubStructure; // whether to rotate the substructure below |
---|
326 | static vector<Angle> old; // old angles |
---|
327 | |
---|
328 | if (loop && loop->is_root_loop()) fixpoint = loop->get_center(); |
---|
329 | |
---|
330 | Angle fix2world(fixpoint, world); |
---|
331 | |
---|
332 | if (event == AW_Mouse_Press) { |
---|
333 | startClick = fix2world; |
---|
334 | old.clear(); |
---|
335 | rotateSubStructure = (button == AW_BUTTON_LEFT); |
---|
336 | |
---|
337 | if (loop) { |
---|
338 | old.push_back(loop->get_abs_angle()); |
---|
339 | if (!rotateSubStructure) { |
---|
340 | for (SEC_strand_iterator strand(loop); strand; ++strand) { |
---|
341 | if (strand->isRootsideFixpoint()) { |
---|
342 | old.push_back(strand->get_helix()->get_abs_angle()); |
---|
343 | } |
---|
344 | } |
---|
345 | } |
---|
346 | } |
---|
347 | else { |
---|
348 | old.push_back(helix->get_abs_angle()); |
---|
349 | old.push_back(helix->outsideLoop()->get_abs_angle()); |
---|
350 | } |
---|
351 | } |
---|
352 | else { |
---|
353 | sec_assert(event == AW_Mouse_Drag); |
---|
354 | Angle diff = fix2world-startClick; |
---|
355 | |
---|
356 | if (loop) { |
---|
357 | loop->set_abs_angle(old[0]+diff); |
---|
358 | if (!rotateSubStructure) { |
---|
359 | int idx = 1; |
---|
360 | for (SEC_strand_iterator strand(loop); strand; ++strand) { |
---|
361 | if (strand->isRootsideFixpoint()) { |
---|
362 | strand->get_helix()->set_abs_angle(old[idx++]); |
---|
363 | } |
---|
364 | } |
---|
365 | } |
---|
366 | } |
---|
367 | else { |
---|
368 | helix->set_abs_angle(old[0]+diff); |
---|
369 | if (!rotateSubStructure) helix->outsideLoop()->set_abs_angle(old[1]); |
---|
370 | } |
---|
371 | |
---|
372 | exports.request_refresh(); |
---|
373 | elem->orientationChanged(); |
---|
374 | } |
---|
375 | } |
---|
376 | break; |
---|
377 | } |
---|
378 | |
---|
379 | case AWT_MODE_SETROOT: // set-root-mode / reset angles |
---|
380 | if (event == AW_Mouse_Press) { |
---|
381 | if (button == AW_BUTTON_LEFT) { // set root |
---|
382 | if (loop) { |
---|
383 | sec_root->set_root(loop); |
---|
384 | exports.request_save(); |
---|
385 | } |
---|
386 | else error = "Please click on a loop to change the root"; |
---|
387 | } |
---|
388 | else { // reset angles |
---|
389 | sec_assert(button == AW_BUTTON_RIGHT); |
---|
390 | elem->reset_angles(); |
---|
391 | elem->orientationChanged(); |
---|
392 | exports.request_save(); |
---|
393 | } |
---|
394 | } |
---|
395 | break; |
---|
396 | |
---|
397 | case AWT_MODE_FOLD: { // fold/unfold helix |
---|
398 | if (event == AW_Mouse_Press && button == AW_BUTTON_LEFT) { // fold helix |
---|
399 | if (loop) { |
---|
400 | const char *helix_nr = sec_root->helixNrAt(abspos); |
---|
401 | if (helix_nr) { |
---|
402 | const size_t *p = sec_root->getHelixPositions(helix_nr); |
---|
403 | error = sec_root->split_loop(p[0], p[1]+1, p[2], p[3]+1); |
---|
404 | |
---|
405 | if (!error) { |
---|
406 | sec_root->nail_position(abspos); |
---|
407 | exports.request_save(); |
---|
408 | } |
---|
409 | } |
---|
410 | else { |
---|
411 | error = GBS_global_string("No helix to fold at position %i", abspos); |
---|
412 | } |
---|
413 | } |
---|
414 | else { |
---|
415 | error = "Click on a loop region to fold a helix"; |
---|
416 | } |
---|
417 | } |
---|
418 | if (event == AW_Mouse_Release && button == AW_BUTTON_RIGHT) { // unfold helix |
---|
419 | if (helix) { |
---|
420 | // this section may only executed during mouse-release-callback, otherwise secedit crashes (see #828) |
---|
421 | sec_assert(event == AW_Mouse_Release); |
---|
422 | error = sec_root->unsplit_loop(helix->strandToRoot()); |
---|
423 | if (!error) { |
---|
424 | sec_root->nail_position(abspos); |
---|
425 | exports.request_save(); |
---|
426 | } |
---|
427 | } |
---|
428 | else { |
---|
429 | error = "Right click on a helix to remove it"; |
---|
430 | } |
---|
431 | } |
---|
432 | break; |
---|
433 | } |
---|
434 | |
---|
435 | case AWT_MODE_CURSOR: // set cursor in ARB_EDIT4 |
---|
436 | drag_target_detection(true); |
---|
437 | if (abspos >= 0 && size_t(abspos) < sec_root->max_index()) { |
---|
438 | // sequence position in AWAR_SET_CURSOR_POSITION is starting with 0! |
---|
439 | aw_root->awar_int(AWAR_SET_CURSOR_POSITION)->write_int(abspos); |
---|
440 | } |
---|
441 | break; |
---|
442 | |
---|
443 | case AWT_MODE_PINFO: // display search pattern |
---|
444 | if (event == AW_Mouse_Press) { |
---|
445 | if (button == AW_BUTTON_LEFT) { |
---|
446 | if (abspos >= 0 && size_t(abspos) < sec_root->max_index()) { |
---|
447 | sec_root->paintSearchPatternStrings(device, abspos, world.xpos()+1, world.ypos()); |
---|
448 | } |
---|
449 | // don't refresh here! |
---|
450 | } |
---|
451 | else { |
---|
452 | sec_assert(button == AW_BUTTON_RIGHT); |
---|
453 | exports.request_refresh(); // simply refresh to remove drawn patterns |
---|
454 | } |
---|
455 | } |
---|
456 | break; |
---|
457 | |
---|
458 | default: sec_assert(0); break; |
---|
459 | } |
---|
460 | |
---|
461 | return error; |
---|
462 | } |
---|
463 | |
---|
464 | void SEC_graphic::handle_command(AW_device *device, AWT_graphic_event& event) { |
---|
465 | if (event.cmd() != AWT_MODE_EDIT && event.cmd() != AWT_MODE_STRETCH) sec_root->set_show_constraints(SEC_NO_TYPE); |
---|
466 | |
---|
467 | GB_ERROR error = NULp; |
---|
468 | if (event.type() == AW_Keyboard_Press || event.type() == AW_Keyboard_Release) { |
---|
469 | error = handleKey(event.type(), event.key_modifier(), event.key_code(), event.key_char()); |
---|
470 | } |
---|
471 | else { |
---|
472 | if (event.button() != AW_BUTTON_MIDDLE && event.cmd() != AWT_MODE_ZOOM) { // don't handle scroll + zoom |
---|
473 | static SEC_base *elem = NULp; // Warning: if handleMouse() destroys 'elem' during mouse press callback, this pointer will become stale! |
---|
474 | static int abspos = -1; |
---|
475 | |
---|
476 | bool updateClicked = false; |
---|
477 | |
---|
478 | if (event.type() == AW_Mouse_Press) updateClicked = true; // initial button-down |
---|
479 | else if (event.cmd() == AWT_MODE_CURSOR) { // special modes which act identical in click/drag/release |
---|
480 | updateClicked = true; |
---|
481 | } |
---|
482 | |
---|
483 | if (updateClicked) { |
---|
484 | // store information about clicked SEC_base |
---|
485 | const AW_clicked_element *clicked = event.best_click(); |
---|
486 | if (clicked) { |
---|
487 | elem = reinterpret_cast<SEC_base*>(clicked->cd1()); |
---|
488 | abspos = clicked->cd2(); |
---|
489 | } |
---|
490 | else { |
---|
491 | elem = NULp; |
---|
492 | } |
---|
493 | } |
---|
494 | |
---|
495 | if (elem) { |
---|
496 | Position world = device->rtransform(event.position()); |
---|
497 | error = handleMouse(device, event.type(), event.button(), event.cmd(), world, elem, abspos); |
---|
498 | } |
---|
499 | |
---|
500 | if (event.type() == AW_Mouse_Release) elem = NULp; // forget last clicked SEC_base |
---|
501 | } |
---|
502 | } |
---|
503 | |
---|
504 | if (error) aw_message(error); |
---|
505 | } |
---|
506 | |
---|
507 | SEC_graphic::SEC_graphic(AW_root *aw_rooti, GBDATA *gb_maini) |
---|
508 | : update_requested(SEC_UPDATE_RELOAD), |
---|
509 | load_error(NULp), |
---|
510 | disp_device(NULp), |
---|
511 | gb_main(gb_maini), |
---|
512 | aw_root(aw_rooti), |
---|
513 | sec_root(new SEC_root), |
---|
514 | gb_struct(NULp), |
---|
515 | gb_struct_ref(NULp), |
---|
516 | last_saved(0) |
---|
517 | { |
---|
518 | exports.set_standard_default_padding(); |
---|
519 | } |
---|
520 | |
---|
521 | SEC_graphic::~SEC_graphic() { |
---|
522 | delete sec_root; |
---|
523 | delete load_error; |
---|
524 | } |
---|
525 | |
---|
526 | static void SEC_structure_changed_cb(GBDATA *gb_seq, SEC_graphic *gfx, GB_CB_TYPE type) { |
---|
527 | LocallyModify<int> allow_flag_modify(gfx->exports.get_modifying_flag_ref(), -1); // hack, allow flag modification during exit (w/o forcing a refresh) |
---|
528 | |
---|
529 | if (type == GB_CB_DELETE) { |
---|
530 | gfx->gb_struct = NULp; |
---|
531 | gfx->gb_struct_ref = NULp; |
---|
532 | |
---|
533 | gfx->request_update(SEC_UPDATE_RELOAD); |
---|
534 | } |
---|
535 | else if (GB_read_clock(gb_seq) > gfx->last_saved) { // not changed by secedit self |
---|
536 | gfx->request_update(SEC_UPDATE_RELOAD); |
---|
537 | } |
---|
538 | } |
---|
539 | |
---|
540 | GB_ERROR SEC_graphic::load_from_DB(GBDATA *, const char *) { |
---|
541 | //! (Re-)Load secondary structure from database |
---|
542 | |
---|
543 | sec_assert(sec_root->get_db()->canDisplay()); // need a sequence loaded (to fix bugs in versions < 3) |
---|
544 | sec_root->nail_cursor(); |
---|
545 | |
---|
546 | GB_transaction ta(gb_main); |
---|
547 | // first check timestamp, do not load structure that we have saved !!!! |
---|
548 | if (gb_struct) { |
---|
549 | if (GB_read_clock(gb_struct) <= last_saved) return NULp; |
---|
550 | } |
---|
551 | |
---|
552 | // Reset structure: |
---|
553 | if (gb_struct) { |
---|
554 | GB_remove_callback(gb_struct, GB_CB_ALL, makeDatabaseCallback(SEC_structure_changed_cb, this)); gb_struct = NULp; |
---|
555 | GB_remove_callback(gb_struct_ref, GB_CB_ALL, makeDatabaseCallback(SEC_structure_changed_cb, this)); gb_struct_ref = NULp; |
---|
556 | } |
---|
557 | |
---|
558 | request_update(SEC_UPDATE_RECOUNT); |
---|
559 | |
---|
560 | if (gb_struct) { |
---|
561 | this->last_saved = GB_read_clock(gb_struct); // mark as loaded |
---|
562 | } |
---|
563 | |
---|
564 | |
---|
565 | |
---|
566 | // Setup new structure: |
---|
567 | GB_ERROR err = NULp; |
---|
568 | GBDATA *gb_ali = NULp; |
---|
569 | { |
---|
570 | char *helix_name = GBT_get_default_helix(gb_main); |
---|
571 | char *name = GBT_readOrCreate_string(gb_main, AWAR_HELIX_NAME, helix_name); |
---|
572 | sec_assert(name); |
---|
573 | |
---|
574 | GBDATA *gb_species = GBT_find_SAI(gb_main, name); |
---|
575 | if (!gb_species) { |
---|
576 | err = GB_export_errorf("Cannot find helix template SAI '%s'", name); |
---|
577 | } |
---|
578 | else { |
---|
579 | char *ali_name = GBT_get_default_alignment(gb_main); |
---|
580 | long ali_len = GBT_get_alignment_len(gb_main, ali_name); |
---|
581 | |
---|
582 | if (ali_len < 10) { |
---|
583 | err = GB_export_errorf("alignment '%s' to short to generate helix", ali_name); |
---|
584 | } |
---|
585 | else { |
---|
586 | gb_ali = GB_search(gb_species, ali_name, GB_FIND); |
---|
587 | if (!gb_ali) { |
---|
588 | err = GB_export_errorf("Your helix structure template '%s' has no valid sequence for alignment '%s'", name, ali_name); // no sequence for name in the database !!! |
---|
589 | } |
---|
590 | } |
---|
591 | free(ali_name); |
---|
592 | } |
---|
593 | |
---|
594 | free(name); |
---|
595 | free(helix_name); |
---|
596 | } |
---|
597 | |
---|
598 | // ------------------------ |
---|
599 | // read structure |
---|
600 | |
---|
601 | if (!err) { |
---|
602 | gb_struct = GB_search(gb_ali, NAME_OF_STRUCT_SEQ, GB_FIND); |
---|
603 | |
---|
604 | if (gb_struct) { |
---|
605 | gb_struct_ref = GB_search(gb_ali, NAME_OF_REF_SEQ, GB_STRING); |
---|
606 | |
---|
607 | char *strct = GB_read_string(gb_struct); |
---|
608 | char *ref = GB_read_string(gb_struct_ref); |
---|
609 | err = sec_root->read_data(strct, ref); |
---|
610 | if (err) { |
---|
611 | err = GBS_global_string("Defect structure in DB (read-error: '%s')", err); |
---|
612 | } |
---|
613 | #if defined(CHECK_INTEGRITY) |
---|
614 | else { |
---|
615 | sec_root->check_integrity(CHECK_STRUCTURE); |
---|
616 | } |
---|
617 | #endif // CHECK_INTEGRITY |
---|
618 | |
---|
619 | free(strct); |
---|
620 | free(ref); |
---|
621 | |
---|
622 | // on first load init structure toggler: |
---|
623 | if (!err) { |
---|
624 | sec_root->get_db()->init_toggler(); |
---|
625 | } |
---|
626 | } |
---|
627 | else { |
---|
628 | err = "no secondary structure was found in your database"; |
---|
629 | } |
---|
630 | |
---|
631 | if (!err) { |
---|
632 | // in the past one additional NAME_OF_STRUCT_SEQ-entry was added everytime the default bone was created |
---|
633 | // Fix: delete additional entries |
---|
634 | |
---|
635 | GBDATA *gb_add = gb_struct; |
---|
636 | do { |
---|
637 | sec_assert(GB_has_key(gb_add, NAME_OF_STRUCT_SEQ)); |
---|
638 | gb_add = GB_nextEntry(gb_add); |
---|
639 | if (gb_add) { |
---|
640 | err = GB_delete(gb_add); |
---|
641 | printf("* Deleting duplicated entry '%s' (%p)\n", NAME_OF_STRUCT_SEQ, gb_add); |
---|
642 | } |
---|
643 | } |
---|
644 | while (gb_add && !err); |
---|
645 | } |
---|
646 | } |
---|
647 | |
---|
648 | if (!err) { |
---|
649 | last_saved = GB_read_clock(gb_struct); // mark as loaded |
---|
650 | request_update(SEC_UPDATE_RECOUNT); |
---|
651 | if (load_error) { // previous load error? |
---|
652 | freenull(load_error); |
---|
653 | exports.request_zoom_reset(); |
---|
654 | } |
---|
655 | } |
---|
656 | else { |
---|
657 | load_error = ARB_strdup(err); |
---|
658 | exports.request_zoom_reset(); |
---|
659 | } |
---|
660 | |
---|
661 | // set structure-change-callbacks: |
---|
662 | if(gb_struct) GB_add_callback(gb_struct, GB_CB_ALL, makeDatabaseCallback(SEC_structure_changed_cb, this)); |
---|
663 | if(gb_struct_ref) GB_add_callback(gb_struct_ref, GB_CB_ALL, makeDatabaseCallback(SEC_structure_changed_cb, this)); |
---|
664 | |
---|
665 | return err; |
---|
666 | } |
---|
667 | |
---|
668 | GB_ERROR SEC_graphic::save_to_DB(GBDATA *, const char *) { |
---|
669 | //! Save secondary structure to database |
---|
670 | |
---|
671 | if (!gb_struct) return NULp; // not loaded, so don't save |
---|
672 | if (!sec_root) return NULp; |
---|
673 | |
---|
674 | char *data = sec_root->buildStructureString(); |
---|
675 | GB_transaction ta(gb_main); |
---|
676 | GB_ERROR error = GB_write_string(gb_struct, data); |
---|
677 | if (!error) { |
---|
678 | const XString& xstr = sec_root->get_xString(); |
---|
679 | const char *x_string = xstr.get_x_string(); |
---|
680 | |
---|
681 | error = GB_write_string(gb_struct_ref, x_string); |
---|
682 | |
---|
683 | if (!error && xstr.alignment_too_short()) { |
---|
684 | aw_message("Your helix needs one gap at end. Please format your alignment!"); |
---|
685 | } |
---|
686 | } |
---|
687 | this->last_saved = GB_read_clock(gb_struct); |
---|
688 | if (error) { |
---|
689 | error = ta.close(error); |
---|
690 | aw_message(error); |
---|
691 | } |
---|
692 | return NULp; |
---|
693 | } |
---|
694 | |
---|
695 | GB_ERROR SEC_graphic::read_data_from_db(char **data, char **x_string) const { |
---|
696 | GB_ERROR error = NULp; |
---|
697 | |
---|
698 | sec_assert(gb_struct && gb_struct_ref); |
---|
699 | *data = GB_read_string(gb_struct); |
---|
700 | if (!*data) error = GB_await_error(); |
---|
701 | else { |
---|
702 | *x_string = GB_read_string(gb_struct_ref); |
---|
703 | if (!*x_string) error = GB_await_error(); |
---|
704 | } |
---|
705 | return error; |
---|
706 | } |
---|
707 | |
---|
708 | GB_ERROR SEC_graphic::write_data_to_db(const char *data, const char *x_string) const { |
---|
709 | if (!gb_struct) return NULp; |
---|
710 | if (!sec_root) return NULp; |
---|
711 | |
---|
712 | GB_transaction ta(gb_main); |
---|
713 | GB_ERROR error = GB_write_string(gb_struct, data); |
---|
714 | if (!error) { |
---|
715 | error = GB_write_string(gb_struct_ref, x_string); |
---|
716 | } |
---|
717 | last_saved = 0; // force reload of data |
---|
718 | return ta.close(error); |
---|
719 | } |
---|
720 | |
---|
721 | void SEC_graphic::check_for_DB_update(GBDATA *) { |
---|
722 | } |
---|
723 | |
---|
724 | void SEC_graphic::update_structure() { |
---|
725 | GB_transaction ta(gb_main); |
---|
726 | |
---|
727 | const SEC_db_interface *db = sec_root->get_db(); |
---|
728 | |
---|
729 | if (db && db->canDisplay()) { |
---|
730 | if (update_requested & SEC_UPDATE_RELOAD) { |
---|
731 | GB_ERROR error = load_from_DB(NULp, NULp); // sets change_flag |
---|
732 | if (error) { |
---|
733 | error = ta.close(error); |
---|
734 | aw_message(error); |
---|
735 | } |
---|
736 | // clear reload flag + set recount flag: |
---|
737 | update_requested = static_cast<SEC_update_request>((update_requested^SEC_UPDATE_RELOAD)|SEC_UPDATE_RECOUNT); |
---|
738 | } |
---|
739 | |
---|
740 | if (update_requested & SEC_UPDATE_SHOWN_POSITIONS) { |
---|
741 | sec_root->update_shown_positions(); |
---|
742 | // clear reload flag + set recount flag: |
---|
743 | update_requested = static_cast<SEC_update_request>((update_requested^SEC_UPDATE_SHOWN_POSITIONS)|SEC_UPDATE_RECOUNT); |
---|
744 | } |
---|
745 | |
---|
746 | if (update_requested & SEC_UPDATE_RECOUNT) { |
---|
747 | sec_root->invalidate_base_positions(); |
---|
748 | sec_root->relayout(); |
---|
749 | |
---|
750 | update_requested = static_cast<SEC_update_request>(update_requested^SEC_UPDATE_RECOUNT); // clear recount flag |
---|
751 | exports.request_refresh(); |
---|
752 | } |
---|
753 | |
---|
754 | sec_root->perform_autoscroll(); |
---|
755 | } |
---|
756 | } |
---|
757 | |
---|
758 | void SEC_graphic::notify_synchronized(GBDATA *) { |
---|
759 | // nothing todo here |
---|
760 | } |
---|
761 | |
---|
762 | void SEC_graphic::show(AW_device *device) { |
---|
763 | const char *textToDisplay = NULp; |
---|
764 | |
---|
765 | sec_assert(sec_root); |
---|
766 | sec_root->clear_last_drawed_cursor_position(); |
---|
767 | |
---|
768 | if (sec_root->canDisplay()) { |
---|
769 | if (sec_root->get_root_loop()) { |
---|
770 | GB_ERROR paint_error = sec_root->paint(device); |
---|
771 | if (paint_error) textToDisplay = GBS_global_string("Error: %s", paint_error); |
---|
772 | } |
---|
773 | else { |
---|
774 | if (load_error) textToDisplay = GBS_global_string("Load error: %s", load_error); |
---|
775 | else textToDisplay = "No structure loaded (yet)"; |
---|
776 | } |
---|
777 | } |
---|
778 | else { |
---|
779 | const SEC_db_interface *db = sec_root->get_db(); |
---|
780 | |
---|
781 | if (!db) textToDisplay = "Not connected to database"; |
---|
782 | else if (!db->helix()) textToDisplay = "No helix info"; |
---|
783 | else textToDisplay = "No species selected"; |
---|
784 | } |
---|
785 | |
---|
786 | if (textToDisplay) { // no structure |
---|
787 | sec_assert(!strchr(textToDisplay, '\n')); // linefeeds do not work here |
---|
788 | device->text(SEC_GC_ECOLI, textToDisplay, 0, 0, 0, AW_SCREEN); |
---|
789 | sec_root->set_last_drawed_cursor_position(LineVector(Origin, ZeroVector)); |
---|
790 | } |
---|
791 | } |
---|
792 | |
---|