source: tags/svn.1.5.4/WINDOW/AW_window.cxx

Last change on this file was 8360, checked in by westram, 13 years ago
  • renamed menu separator functions
  • removed macro AWMIMT
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 134.9 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : AW_window.cxx                                     //
4//   Purpose   :                                                   //
5//                                                                 //
6//   Institute of Microbiology (Technical University Munich)       //
7//   http://www.arb-home.de/                                       //
8//                                                                 //
9// =============================================================== //
10
11#include "aw_at.hxx"
12#include "aw_nawar.hxx"
13#include "aw_xfig.hxx"
14#include "aw_Xm.hxx"
15#include "aw_window_Xm.hxx"
16#include "aw_xkey.hxx"
17#include "aw_select.hxx"
18#include "aw_global.hxx"
19#include "aw_awar.hxx"
20#include "aw_msg.hxx"
21#include "aw_status.hxx"
22#include "aw_root.hxx"
23#include "aw_question.hxx"
24
25#include <arbdbt.h>
26#include <arb_handlers.h>
27#include <arb_file.h>
28
29#include <X11/keysym.h>
30#include <X11/Xutil.h>
31#include <X11/Shell.h>
32#include <X11/cursorfont.h>
33#include <Xm/AtomMgr.h>
34#include <Xm/Frame.h>
35#include <Xm/PushB.h>
36#include <Xm/Protocols.h>
37#include <Xm/RowColumn.h>
38#include <Xm/DrawingA.h>
39#include <Xm/Form.h>
40#include <Xm/Separator.h>
41#include <Xm/MainW.h>
42#include <Xm/CascadeB.h>
43#include <Xm/MenuShell.h>
44#include <Xm/ScrollBar.h>
45#include <Xm/MwmUtil.h>
46
47#include <cstdlib>
48#include <cstdarg>
49#include <cctype>
50
51#include <sys/types.h>
52#include <sys/uio.h>
53#include <unistd.h>
54
55AW_root *AW_root::SINGLETON = NULL;
56
57AW_cb_struct::AW_cb_struct(AW_window *awi, void (*g)(AW_window*, AW_CL, AW_CL), AW_CL cd1i, AW_CL cd2i, const char *help_texti, class AW_cb_struct *nexti) {
58    aw            = awi;
59    f             = g;
60    cd1           = cd1i;
61    cd2           = cd2i;
62    help_text     = help_texti;
63    pop_up_window = NULL;
64    this->next = nexti;
65
66    id = NULL;
67}
68
69AW_timer_cb_struct::AW_timer_cb_struct(AW_root *ari, AW_RCB g, AW_CL cd1i, AW_CL cd2i) {
70    ar = ari;
71    f = g;
72    cd1 = cd1i;
73    cd2 = cd2i;
74}
75AW_timer_cb_struct::~AW_timer_cb_struct() {
76}
77
78void AW_root::make_sensitive(Widget w, AW_active mask) {
79    // Don't call make_sensitive directly!
80    //
81    // Simply set sens_mask(AWM_EXP) and after creating the expert-mode-only widgets,
82    // set it back using sens_mask(AWM_ALL)
83
84    aw_assert(w);
85    aw_assert(legal_mask(mask));
86
87    prvt->set_last_widget(w);
88
89    if (mask != AWM_ALL) { // no need to make widget sensitive, if its shown unconditionally
90        button_sens_list = new AW_buttons_struct(mask, w, button_sens_list);
91        if (!(mask & global_mask)) XtSetSensitive(w, False); // disable widget if mask doesn't match
92    }
93}
94
95AW_buttons_struct::AW_buttons_struct(AW_active maski, Widget w, AW_buttons_struct *prev_button) {
96    aw_assert(w);
97    aw_assert(legal_mask(maski));
98
99    mask     = maski;
100    button   = w;
101    next     = prev_button;
102}
103
104AW_buttons_struct::~AW_buttons_struct() {
105    delete next;
106}
107
108bool AW_root::remove_button_from_sens_list(Widget button) {
109    bool removed = false;
110    if (button_sens_list) {
111        AW_buttons_struct *prev = 0;
112        AW_buttons_struct *bl   = button_sens_list;
113
114        while (bl) {
115            if (bl->button == button) break; // found wanted widget
116            prev = bl;
117            bl = bl->next;
118        }
119
120        if (bl) {
121            // remove from list
122            if (prev) prev->next  = bl->next;
123            else button_sens_list = bl->next;
124
125            bl->next = 0;
126            removed  = true;
127
128            delete bl;
129        }
130    }
131    return removed;
132}
133
134AW_option_menu_struct::AW_option_menu_struct(int numberi, const char *variable_namei,
135                                             AW_VARIABLE_TYPE variable_typei, Widget label_widgeti,
136                                             Widget menu_widgeti, AW_pos xi, AW_pos yi, int correct) {
137    option_menu_number = numberi;
138    variable_name      = strdup(variable_namei);
139    variable_type      = variable_typei;
140    label_widget       = label_widgeti;
141    menu_widget        = menu_widgeti;
142    first_choice       = NULL;
143    last_choice        = NULL;
144    default_choice     = NULL;
145    next               = NULL;
146    x                  = xi;
147    y                  = yi;
148
149    correct_for_at_center_intern = correct;
150}
151
152AW_toggle_field_struct::AW_toggle_field_struct(int toggle_field_numberi,
153        const char *variable_namei, AW_VARIABLE_TYPE variable_typei,
154        Widget label_widgeti, int correct) {
155
156    toggle_field_number = toggle_field_numberi;
157    variable_name = strdup(variable_namei);
158    variable_type = variable_typei;
159    label_widget = label_widgeti;
160    first_toggle = NULL;
161    last_toggle = NULL;
162    default_toggle = NULL;
163    next = NULL;
164    correct_for_at_center_intern = correct;
165}
166
167char *AW_selection_list_entry::copy_string_for_display(const char *str) {
168    char *out = strdup(str);
169    char *p   = out;
170    int   ch;
171
172    while ((ch=*(p++)) != 0) {
173        if (ch==',')
174            p[-1] = ';';
175        if (ch=='\n')
176            p[-1] = '#';
177    }
178    return out;
179}
180
181AW_selection_list::AW_selection_list(const char *variable_namei, int variable_typei, Widget select_list_widgeti) {
182    memset((char *)this, 0, sizeof(AW_selection_list));
183    variable_name = nulldup(variable_namei);
184    variable_type = (AW_VARIABLE_TYPE)variable_typei;
185    select_list_widget = select_list_widgeti;
186    list_table = NULL;
187    last_of_list_table = NULL;
188    default_select = NULL;
189    value_equal_display = false;
190}
191
192static void destroy_AW_root() {
193    delete AW_root::SINGLETON; AW_root::SINGLETON = NULL;
194}
195
196AW_root::AW_root(const char *propertyFile, const char *program, bool no_exit) {
197    aw_assert(!AW_root::SINGLETON);                 // only one instance allowed
198    AW_root::SINGLETON = this;
199
200    memset((char *)this, 0, sizeof(AW_root));
201
202    prvt = new AW_root_Motif;
203
204    init_variables(load_properties(propertyFile));
205    init_root(program, no_exit);
206
207    atexit(destroy_AW_root); // do not call this before opening properties DB!
208}
209
210#if defined(UNIT_TESTS)
211AW_root::AW_root(const char *propertyFile) {
212    aw_assert(!AW_root::SINGLETON);                 // only one instance allowed
213    AW_root::SINGLETON = this;
214
215    memset((char *)this, 0, sizeof(AW_root));
216    init_variables(load_properties(propertyFile));
217    atexit(destroy_AW_root); // do not call this before opening properties DB!
218}
219#endif
220
221
222AW_root::~AW_root() {
223    AW_root_cblist::clear(focus_callback_list);
224    delete button_sens_list;    button_sens_list = NULL;
225
226    exit_root();
227    exit_variables();
228    aw_assert(this == AW_root::SINGLETON);
229
230    delete prvt;
231
232    free(program_name);
233
234    AW_root::SINGLETON = NULL;
235}
236
237AW_window_Motif::AW_window_Motif() {
238    memset((char*)this, 0, sizeof(AW_window_Motif));
239}
240
241AW_window::AW_window() {
242    memset((char *)this, 0, sizeof(AW_window));
243    p_w = new AW_window_Motif;
244    _at = new AW_at; // Note to valgrinders : the whole AW_window memory management suffers because Windows are NEVER deleted
245    picture = new AW_screen_area;
246    reset_scrolled_picture_size();
247    slider_pos_vertical = 0;
248    slider_pos_horizontal = 0;
249
250}
251
252AW_window::~AW_window() {
253    delete p_w;
254    delete picture;
255}
256
257#if defined(DEBUG)
258// #define DUMP_MENU_LIST          // this should NOT be defined normally (if defined, every window writes all menu-entries to stdout)
259#endif // DEBUG
260#if defined(DUMP_MENU_LIST)
261
262static char *window_name = 0;
263static char *sub_menu = 0;
264
265static void initMenuListing(const char *win_name) {
266    aw_assert(win_name);
267
268    freedup(window_name, win_name);
269    freenull(sub_menu);
270
271    printf("---------------------------------------- list of menus for '%s'\n", window_name);
272}
273
274static void dumpMenuEntry(const char *entry) {
275    aw_assert(window_name);
276    if (sub_menu) {
277        printf("'%s/%s/%s'\n", window_name, sub_menu, entry);
278    }
279    else {
280        printf("'%s/%s'\n", window_name, entry);
281    }
282}
283
284static void dumpOpenSubMenu(const char *sub_name) {
285    aw_assert(sub_name);
286
287    dumpMenuEntry(sub_name); // dump the menu itself
288
289    if (sub_menu) freeset(sub_menu, GBS_global_string_copy("%s/%s", sub_menu, sub_name));
290    else sub_menu = strdup(sub_name);
291}
292
293static void dumpCloseSubMenu() {
294    aw_assert(sub_menu);
295    char *lslash = strrchr(sub_menu, '/');
296    if (lslash) {
297        lslash[0] = 0;
298    }
299    else freenull(sub_menu);
300}
301
302static void dumpCloseAllSubMenus() {
303    freenull(sub_menu);
304}
305
306#endif // DUMP_MENU_LIST
307
308AW_window_menu_modes::AW_window_menu_modes() : AW_window_menu_modes_private(NULL) {}
309AW_window_menu_modes::~AW_window_menu_modes() {}
310
311AW_window_menu::AW_window_menu() {}
312AW_window_menu::~AW_window_menu() {}
313
314AW_window_simple::AW_window_simple() {}
315AW_window_simple::~AW_window_simple() {}
316
317AW_window_simple_menu::AW_window_simple_menu() {}
318AW_window_simple_menu::~AW_window_simple_menu() {}
319
320AW_window_message::AW_window_message() {}
321AW_window_message::~AW_window_message() {}
322
323void AW_window::set_horizontal_scrollbar_left_indent(int indent) {
324    XtVaSetValues(p_w->scroll_bar_horizontal, XmNleftOffset, (int)indent, NULL);
325    left_indent_of_horizontal_scrollbar = indent;
326}
327
328static void value_changed_scroll_bar_horizontal(Widget /*wgt*/, XtPointer aw_cb_struct, XtPointer call_data) {
329    XmScrollBarCallbackStruct *sbcbs = (XmScrollBarCallbackStruct *)call_data;
330    AW_cb_struct *cbs = (AW_cb_struct *) aw_cb_struct;
331    (cbs->aw)->slider_pos_horizontal = sbcbs->value; // setzt Scrollwerte im AW_window
332    cbs->run_callback();
333}
334static void drag_scroll_bar_horizontal(Widget /*wgt*/, XtPointer aw_cb_struct, XtPointer call_data) {
335    XmScrollBarCallbackStruct *sbcbs = (XmScrollBarCallbackStruct *)call_data;
336    AW_cb_struct *cbs = (AW_cb_struct *) aw_cb_struct;
337    (cbs->aw)->slider_pos_horizontal = sbcbs->value; // setzt Scrollwerte im AW_window
338    cbs->run_callback();
339}
340void AW_window::set_horizontal_change_callback(void (*f)(AW_window*, AW_CL, AW_CL), AW_CL cd1, AW_CL cd2) {
341    XtAddCallback(p_w->scroll_bar_horizontal, XmNvalueChangedCallback,
342            (XtCallbackProc) value_changed_scroll_bar_horizontal,
343            (XtPointer) new AW_cb_struct(this, f, cd1, cd2, ""));
344    XtAddCallback(p_w->scroll_bar_horizontal, XmNdragCallback,
345            (XtCallbackProc) drag_scroll_bar_horizontal,
346            (XtPointer) new AW_cb_struct(this, f, cd1, cd2, ""));
347}
348
349static void value_changed_scroll_bar_vertical(Widget /*wgt*/, XtPointer aw_cb_struct, XtPointer call_data) {
350    XmScrollBarCallbackStruct *sbcbs = (XmScrollBarCallbackStruct *)call_data;
351    AW_cb_struct *cbs = (AW_cb_struct *) aw_cb_struct;
352    cbs->aw->slider_pos_vertical = sbcbs->value; // setzt Scrollwerte im AW_window
353    cbs->run_callback();
354}
355static void drag_scroll_bar_vertical(Widget /*wgt*/, XtPointer aw_cb_struct, XtPointer call_data) {
356    XmScrollBarCallbackStruct *sbcbs = (XmScrollBarCallbackStruct *)call_data;
357    AW_cb_struct *cbs = (AW_cb_struct *) aw_cb_struct;
358    cbs->aw->slider_pos_vertical = sbcbs->value; // setzt Scrollwerte im AW_window
359    cbs->run_callback();
360}
361
362void AW_window::set_vertical_change_callback(void (*f)(AW_window*, AW_CL, AW_CL), AW_CL cd1, AW_CL cd2) {
363    XtAddCallback(p_w->scroll_bar_vertical, XmNvalueChangedCallback,
364            (XtCallbackProc) value_changed_scroll_bar_vertical,
365            (XtPointer) new AW_cb_struct(this, f, cd1, cd2, ""));
366    XtAddCallback(p_w->scroll_bar_vertical, XmNdragCallback,
367            (XtCallbackProc) drag_scroll_bar_vertical,
368            (XtPointer) new AW_cb_struct(this, f, cd1, cd2, ""));
369
370    XtAddCallback(p_w->scroll_bar_vertical, XmNpageIncrementCallback,
371            (XtCallbackProc) drag_scroll_bar_vertical,
372            (XtPointer) new AW_cb_struct(this, f, cd1, cd2, ""));
373    XtAddCallback(p_w->scroll_bar_vertical, XmNpageDecrementCallback,
374            (XtCallbackProc) drag_scroll_bar_vertical,
375            (XtPointer) new AW_cb_struct(this, f, cd1, cd2, ""));
376}
377
378void AW_window::tell_scrolled_picture_size(AW_screen_area rectangle) {
379    picture->l = rectangle.l;
380    picture->r = rectangle.r;
381    picture->t = rectangle.t;
382    picture->b = rectangle.b;
383}
384void AW_window::tell_scrolled_picture_size(AW_world rectangle) {
385    picture->l = (int)rectangle.l;
386    picture->r = (int)rectangle.r;
387    picture->t = (int)rectangle.t;
388    picture->b = (int)rectangle.b;
389}
390
391void AW_window::reset_scrolled_picture_size() {
392    picture->l = 0;
393    picture->r = 0;
394    picture->t = 0;
395    picture->b = 0;
396}
397AW_pos AW_window::get_scrolled_picture_width() const {
398    return (picture->r - picture->l);
399}
400
401AW_pos AW_window::get_scrolled_picture_height() const {
402    return (picture->b - picture->t);
403}
404
405void AW_window::calculate_scrollbars() {
406    AW_screen_area screen;
407
408    int  slider_size_horizontal;
409    int  slider_size_vertical;
410    bool vertical, horizontal;
411    int  position_of_slider;
412    int  slider_max;
413
414    vertical = horizontal = true; // es gibt verticalen & horizontalen scrollbar
415
416    this->_get_area_size(AW_MIDDLE_AREA, &screen);
417
418    // HORIZONTAL
419    slider_max = (int)get_scrolled_picture_width();
420    if (slider_max <1) {
421        slider_max = 1;
422        XtVaSetValues(p_w->scroll_bar_horizontal, XmNsliderSize, 1, NULL);
423    }
424
425    slider_size_horizontal = (int)((screen.r
426            -left_indent_of_horizontal_scrollbar));
427    if (slider_size_horizontal < 1)
428        slider_size_horizontal = 1; // ist der slider zu klein (<1) ?
429    if (slider_size_horizontal > slider_max) { // Schirm groesser als Bild
430        slider_size_horizontal = slider_max; // slider nimmt ganze laenge ein
431        XtVaSetValues(p_w->scroll_bar_horizontal, XmNvalue, 0, NULL); // slider ganz links setzen
432        horizontal = false; // kein horizontaler slider mehr
433    }
434
435    // check wether XmNValue is to big
436    XtVaGetValues(p_w->scroll_bar_horizontal, XmNvalue, &position_of_slider,
437            NULL);
438    if (position_of_slider > (slider_max-slider_size_horizontal)) { // steht der slider fuer slidergroesse zu rechts ?
439        position_of_slider = slider_max-slider_size_horizontal; // -1 ? vielleicht !
440        if (position_of_slider < 0)
441            position_of_slider = 0;
442        XtVaSetValues(p_w->scroll_bar_horizontal, XmNvalue, position_of_slider,
443                NULL);
444    }
445    // Anpassung fuer resize, wenn unbeschriebener Bereich vergroessert wird
446    if (-slider_pos_horizontal + get_scrolled_picture_width() < screen.r
447            -left_indent_of_horizontal_scrollbar) {
448        if (horizontal) {
449            slider_pos_horizontal = (int)(get_scrolled_picture_width()
450                    - (screen.r-left_indent_of_horizontal_scrollbar));
451        }
452        else {
453            slider_pos_horizontal = 0; // slider nach ganz oben, da alles sichtbar
454        }
455    }
456    XtVaSetValues(p_w->scroll_bar_horizontal, XmNsliderSize, 1, NULL);
457    XtVaSetValues(p_w->scroll_bar_horizontal, XmNmaximum, slider_max, NULL);
458    XtVaSetValues(p_w->scroll_bar_horizontal, XmNsliderSize,
459            slider_size_horizontal, NULL);
460    char buffer[200];
461    sprintf(buffer, "window/%s/horizontal_page_increment", window_defaults_name);
462    XtVaSetValues(p_w->scroll_bar_horizontal, XmNpageIncrement, (int)((screen.r
463            -left_indent_of_horizontal_scrollbar)*(get_root()->awar(buffer)->read_int()*0.01)),
464    NULL);
465
466    sprintf(buffer, "window/%s/scroll_width_horizontal", window_defaults_name);
467    XtVaSetValues(p_w->scroll_bar_horizontal, XmNincrement, (int)(get_root()->awar(buffer)->read_int()), NULL);
468
469    sprintf(buffer, "window/%s/scroll_delay_horizontal", window_defaults_name);
470    XtVaSetValues(p_w->scroll_bar_horizontal, XmNrepeatDelay, (int)(get_root()->awar(buffer)->read_int()), NULL);
471
472    // VERTICAL
473    slider_max = (int)get_scrolled_picture_height();
474    if (slider_max <1) {
475        slider_max = 1;
476        XtVaSetValues(p_w->scroll_bar_vertical, XmNsliderSize, 1, NULL);
477    }
478
479    slider_size_vertical = (int)((screen.b-top_indent_of_vertical_scrollbar
480            -bottom_indent_of_vertical_scrollbar));
481    if (slider_size_vertical < 1)
482        slider_size_vertical = 1;
483    if (slider_size_vertical > slider_max) {
484        slider_size_vertical = slider_max;
485        XtVaSetValues(p_w->scroll_bar_vertical, XmNvalue, 0, NULL);
486        vertical = false;
487    }
488
489    // check wether XmNValue is to big
490    XtVaGetValues(p_w->scroll_bar_vertical, XmNvalue, &position_of_slider, NULL);
491    if (position_of_slider > (slider_max-slider_size_vertical)) {
492        position_of_slider = slider_max-slider_size_vertical; // -1 ? vielleicht !
493        if (position_of_slider < 0)
494            position_of_slider = 0;
495        XtVaSetValues(p_w->scroll_bar_vertical, XmNvalue, position_of_slider,
496                NULL);
497    }
498    // Anpassung fuer resize, wenn unbeschriebener Bereich vergroessert wird
499    if (-slider_pos_vertical + get_scrolled_picture_height() < screen.b
500            -top_indent_of_vertical_scrollbar
501            -bottom_indent_of_vertical_scrollbar) {
502        if (vertical) {
503            slider_pos_vertical = (int)(get_scrolled_picture_height()
504                    - (screen.b-top_indent_of_vertical_scrollbar
505                            -bottom_indent_of_vertical_scrollbar));
506        }
507        else {
508            slider_pos_vertical = 0; // slider nach ganz oben, da alles sichtbar
509        }
510    }
511    XtVaSetValues(p_w->scroll_bar_vertical, XmNsliderSize, 1, NULL);
512    XtVaSetValues(p_w->scroll_bar_vertical, XmNmaximum, slider_max, NULL);
513    XtVaSetValues(p_w->scroll_bar_vertical, XmNsliderSize,
514            slider_size_vertical, NULL);
515    sprintf(buffer, "window/%s/vertical_page_increment", window_defaults_name);
516    XtVaSetValues(p_w->scroll_bar_vertical, XmNpageIncrement, (int)((screen.b
517            -top_indent_of_vertical_scrollbar
518            -bottom_indent_of_vertical_scrollbar)*(get_root()->awar(buffer)->read_int()*0.01)),
519    NULL);
520
521    sprintf(buffer, "window/%s/scroll_width_vertical", window_defaults_name);
522    XtVaSetValues(p_w->scroll_bar_vertical, XmNincrement, (int)(get_root()->awar(buffer)->read_int()), NULL);
523
524    sprintf(buffer, "window/%s/scroll_delay_vertical", window_defaults_name);
525    XtVaSetValues(p_w->scroll_bar_vertical, XmNrepeatDelay, (int)(get_root()->awar(buffer)->read_int()), NULL);
526}
527
528void AW_window::set_vertical_scrollbar_position(int position) {
529    slider_pos_vertical = position;
530    XtVaSetValues(p_w->scroll_bar_vertical, XmNvalue, position, NULL);
531}
532
533void AW_window::set_horizontal_scrollbar_position(int position) {
534    slider_pos_horizontal = position;
535    XtVaSetValues(p_w->scroll_bar_horizontal, XmNvalue, position, NULL);
536}
537
538static void AW_timer_callback(XtPointer aw_timer_cb_struct, XtIntervalId */*id*/) {
539    AW_timer_cb_struct *tcbs = (AW_timer_cb_struct *) aw_timer_cb_struct;
540    if (!tcbs)
541        return;
542
543    AW_root *root = tcbs->ar;
544    if (root->disable_callbacks) {
545        // delay the timer callback for 25ms
546        XtAppAddTimeOut(p_global->context,
547        (unsigned long)25, // wait 25 msec = 1/40 sec
548        (XtTimerCallbackProc)AW_timer_callback,
549        aw_timer_cb_struct);
550    }
551    else {
552        tcbs->f(root, tcbs->cd1, tcbs->cd2);
553        delete tcbs; // timer only once
554    }
555}
556
557static void AW_timer_callback_never_disabled(XtPointer aw_timer_cb_struct, XtIntervalId */*id*/) {
558    AW_timer_cb_struct *tcbs = (AW_timer_cb_struct *) aw_timer_cb_struct;
559    if (!tcbs)
560        return;
561
562    tcbs->f(tcbs->ar, tcbs->cd1, tcbs->cd2);
563    delete tcbs; // timer only once
564}
565
566void AW_root::add_timed_callback(int ms, AW_RCB f, AW_CL cd1, AW_CL cd2) {
567    XtAppAddTimeOut(p_r->context,
568    (unsigned long)ms,
569    (XtTimerCallbackProc)AW_timer_callback,
570    (XtPointer) new AW_timer_cb_struct(this, f, cd1, cd2));
571}
572
573void AW_root::add_timed_callback_never_disabled(int ms, AW_RCB f, AW_CL cd1, AW_CL cd2) {
574    XtAppAddTimeOut(p_r->context,
575    (unsigned long)ms,
576    (XtTimerCallbackProc)AW_timer_callback_never_disabled,
577    (XtPointer) new AW_timer_cb_struct(this, f, cd1, cd2));
578}
579
580void AW_POPDOWN(AW_window *aww) {
581    aww->hide();
582}
583
584#define BUFSIZE 256
585static char aw_size_awar_name_buffer[BUFSIZE];
586static const char *aw_size_awar_name(AW_window *aww, const char *sub_entry) {
587#if defined(DEBUG)
588    int size =
589#endif // DEBUG
590            sprintf(aw_size_awar_name_buffer, "window/windows/%s/%s",
591                    aww->window_defaults_name, sub_entry);
592#if defined(DEBUG)
593    aw_assert(size < BUFSIZE);
594#endif // DEBUG
595    return aw_size_awar_name_buffer;
596}
597#undef BUFSIZE
598
599#define aw_awar_name_posx(aww)   aw_size_awar_name((aww), "posx")
600#define aw_awar_name_posy(aww)   aw_size_awar_name((aww), "posy")
601#define aw_awar_name_width(aww)  aw_size_awar_name((aww), "width")
602#define aw_awar_name_height(aww) aw_size_awar_name((aww), "height")
603
604void AW_window::create_user_geometry_awars(int posx, int posy, int width, int height) {
605    get_root()->awar_int(aw_awar_name_posx(this), posx);
606    get_root()->awar_int(aw_awar_name_posy(this), posy);
607    get_root()->awar_int(aw_awar_name_width(this), width);
608    get_root()->awar_int(aw_awar_name_height(this), height);
609}
610
611
612void AW_window::store_size_in_awars(int width, int height) {
613    get_root()->awar(aw_awar_name_width(this))->write_int(width);
614    get_root()->awar(aw_awar_name_height(this))->write_int(height);
615}
616
617void AW_window::get_size_from_awars(int& width, int& height) {
618    width  = get_root()->awar(aw_awar_name_width(this))->read_int();
619    height = get_root()->awar(aw_awar_name_height(this))->read_int();
620}
621
622void AW_window::store_pos_in_awars(int posx, int posy) {
623    get_root()->awar(aw_awar_name_posx(this))->write_int(posx);
624    get_root()->awar(aw_awar_name_posy(this))->write_int(posy);
625}
626
627void AW_window::get_pos_from_awars(int& posx, int& posy) {
628    posx = get_root()->awar(aw_awar_name_posx(this))->read_int();
629    posy = get_root()->awar(aw_awar_name_posy(this))->read_int();
630}
631
632#undef aw_awar_name_posx
633#undef aw_awar_name_posy
634#undef aw_awar_name_width
635#undef aw_awar_name_height
636
637static void aw_onExpose_calc_WM_offsets(AW_window *aww);
638static void aw_calc_WM_offsets_delayed(AW_root *, AW_CL cl_aww, AW_CL) { aw_onExpose_calc_WM_offsets((AW_window*)cl_aww); }
639
640static void aw_onExpose_calc_WM_offsets(AW_window *aww) {
641    AW_window_Motif *motif = p_aww(aww);
642
643    int posx,  posy;  aww->get_window_content_pos(posx, posy);
644
645    bool knows_window_position = posx != 0 || posy != 0;
646
647    if (!knows_window_position) { // oops - motif has no idea where the window has been placed
648        // assume positions stored in awars are correct and use them.
649        // This works around problems with unclickable GUI-elements when running on 'Unity'
650
651        int oposx, oposy; aww->get_pos_from_awars(oposx, oposy);
652        aww->set_window_frame_pos(oposx, oposy);
653
654        if (!motif->knows_WM_offset()) {
655            aww->get_root()->add_timed_callback(100, aw_calc_WM_offsets_delayed, (AW_CL)aww, 0);
656        }
657    }
658    else if (!motif->knows_WM_offset()) {
659        int oposx, oposy; aww->get_pos_from_awars(oposx, oposy);
660        // calculate offset
661        motif->WM_top_offset  = posy-oposy;
662        motif->WM_left_offset = posx-oposx;
663    }
664}
665
666bool AW_cb_struct::is_equal(const AW_cb_struct& other) const {
667    bool equal = false;
668    if (f == other.f) {                             // same callback function
669        equal = (cd1 == other.cd1) && (cd2 == other.cd2);
670        if (equal) {
671            if (f == AW_POPUP) {
672                equal = aw->get_root() == other.aw->get_root();
673            }
674            else {
675                equal = aw == other.aw;
676                if (!equal) {
677                    equal = aw->get_root() == other.aw->get_root();
678#if defined(DEBUG) && 0
679                    if (equal) {
680                        fprintf(stderr,
681                                "callback '%s' instantiated twice with different windows (w1='%s' w2='%s') -- assuming the callbacks are equal\n",
682                                id, aw->get_window_id(), other.aw->get_window_id());
683                    }
684#endif // DEBUG
685                }
686            }
687        }
688    }
689    return equal;
690}
691
692#if defined(DEBUG)
693// #define TRACE_CALLBACKS
694#endif // DEBUG
695
696
697AW_cb_struct_guard AW_cb_struct::guard_before = NULL;
698AW_cb_struct_guard AW_cb_struct::guard_after  = NULL;
699
700void AW_cb_struct::run_callback() {
701    if (next) next->run_callback();                 // callback the whole list
702    if (!f) return;                                 // run no callback
703
704    AW_root *root = aw->get_root();
705    if (root->disable_callbacks) {
706        // some functions (namely aw_message, aw_input, aw_string_selection and aw_file_selection)
707        // have to disable most callbacks, because they are often called from inside these callbacks
708        // (e.g. because some exceptional condition occurred which needs user interaction) and if
709        // callbacks weren't disabled, a recursive deadlock occurs.
710
711        // the following callbacks are allowed even if disable_callbacks is true
712
713        bool isModalCallback = (f == AW_CB(message_cb) ||
714                                f == AW_CB(input_history_cb) ||
715                                f == AW_CB(input_cb) ||
716                                f == AW_CB(file_selection_cb));
717
718        bool isPopdown = (f == AW_CB(AW_POPDOWN));
719        bool isHelp    = (f == AW_CB(AW_POPUP_HELP));
720        bool allow     = isModalCallback || isHelp || isPopdown;
721
722        bool isInfoResizeExpose = false;
723
724        if (!allow) {
725            isInfoResizeExpose = aw->is_expose_callback(AW_INFO_AREA, f) || aw->is_resize_callback(AW_INFO_AREA, f);
726            allow              = isInfoResizeExpose;
727        }
728
729        if (!allow) {
730            // don't warn about the following callbacks, just silently ignore them
731            bool silentlyIgnore =
732                aw->is_expose_callback(AW_MIDDLE_AREA, f) ||
733                aw->is_resize_callback(AW_MIDDLE_AREA, f);
734
735            if (!silentlyIgnore) { // otherwise remind the user to answer the prompt:
736                aw_message("That has been ignored. Answer the prompt first!");
737            }
738#if defined(TRACE_CALLBACKS)
739            printf("suppressing callback %p\n", f);
740#endif // TRACE_CALLBACKS
741            return; // suppress the callback!
742        }
743#if defined(TRACE_CALLBACKS)
744        else {
745            if (isModalCallback) printf("allowed modal callback %p\n", f);
746            else if (isPopdown) printf("allowed AW_POPDOWN\n");
747            else if (isHelp) printf("allowed AW_POPUP_HELP\n");
748            else if (isInfoResizeExpose) printf("allowed expose/resize infoarea\n");
749            else printf("allowed other (unknown) callback %p\n", f);
750        }
751#endif // TRACE_CALLBACKS
752    }
753    else {
754#if defined(TRACE_CALLBACKS)
755        printf("Callbacks are allowed (executing %p)\n", f);
756#endif // TRACE_CALLBACKS
757    }
758
759    if (guard_before) guard_before();
760
761    if (f == AW_POPUP) {
762        if (pop_up_window) { // already exists
763            pop_up_window->activate();
764        }
765        else {
766            AW_PPP g = (AW_PPP)cd1;
767            if (g) {
768                pop_up_window = g(aw->get_root(), cd2, 0);
769                pop_up_window->show();
770            }
771            else {
772                aw_message("not implemented -- please report to devel@arb-home.de");
773            }
774        }
775        if (pop_up_window && p_aww(pop_up_window)->popup_cb)
776            p_aww(pop_up_window)->popup_cb->run_callback();
777    }
778    else {
779        f(aw, cd1, cd2);
780    }
781
782    if (guard_after) guard_after();
783}
784
785bool AW_cb_struct::contains(void (*g)(AW_window*, AW_CL, AW_CL)) {
786    return (f == g) || (next && next->contains(g));
787}
788
789AW_root_Motif::AW_root_Motif() {
790    memset((char*)this, 0, sizeof(*this));
791}
792
793AW_root_Motif::~AW_root_Motif() {
794    GBS_free_hash(action_hash);
795    XmFontListFree(fontlist);
796}
797
798void AW_root_Motif::set_cursor(Display *d, Window w, Cursor c) {
799    XSetWindowAttributes attrs;
800    old_cursor_display = d;
801    old_cursor_window = w;
802
803    if (c)
804        attrs.cursor = c;
805    else
806        attrs.cursor = None;
807
808    if (d && w) {
809        XChangeWindowAttributes(d, w, CWCursor, &attrs);
810    }
811    XChangeWindowAttributes(XtDisplay(main_widget), XtWindow(main_widget),
812            CWCursor, &attrs);
813    XFlush(XtDisplay(main_widget));
814}
815
816void AW_root_Motif::normal_cursor() {
817    set_cursor(old_cursor_display, old_cursor_window, 0);
818}
819
820void AW_server_callback(Widget /*wgt*/, XtPointer aw_cb_struct, XtPointer /*call_data*/) {
821    AW_cb_struct *cbs = (AW_cb_struct *) aw_cb_struct;
822
823    AW_root *root = cbs->aw->get_root();
824    if (p_global->help_active) {
825        p_global->help_active = 0;
826        p_global->normal_cursor();
827
828        if (cbs->help_text && ((GBS_string_matches(cbs->help_text, "*.ps", GB_IGNORE_CASE)) ||
829                               (GBS_string_matches(cbs->help_text, "*.hlp", GB_IGNORE_CASE)) ||
830                               (GBS_string_matches(cbs->help_text, "*.help", GB_IGNORE_CASE))))
831        {
832            AW_POPUP_HELP(cbs->aw, (AW_CL)cbs->help_text);
833        }
834        else {
835            aw_message("Sorry no help available");
836        }
837        return;
838    }
839
840    if (root->prvt->recording_macro_file) {
841        if (cbs->id && strcmp(cbs->id, root->prvt->stop_action_name)) {
842            fprintf(root->prvt->recording_macro_file,
843                    "BIO::remote_action($gb_main,\"%s\",",
844                    root->prvt->application_name_for_macros);
845            GBS_fwrite_string(cbs->id, root->prvt->recording_macro_file);
846            fprintf(root->prvt->recording_macro_file, ");\n");
847        }
848    }
849
850    if (cbs->f == AW_POPUP) {
851        cbs->run_callback();
852    }
853    else {
854        p_global->set_cursor(XtDisplay(p_global->toplevel_widget),
855                XtWindow(p_aww(cbs->aw)->shell),
856                p_global->clock_cursor);
857        cbs->run_callback();
858
859        XEvent event; // destroy all old events !!!
860        while (XCheckMaskEvent(XtDisplay(p_global->toplevel_widget),
861        ButtonPressMask|ButtonReleaseMask|ButtonMotionMask|
862        KeyPressMask|KeyReleaseMask|PointerMotionMask, &event)) {
863        }
864
865        if (p_global->help_active) {
866            p_global->set_cursor(XtDisplay(p_global->toplevel_widget),
867                    XtWindow(p_aww(cbs->aw)->shell),
868                    p_global->question_cursor);
869        }
870        else {
871            p_global->set_cursor(XtDisplay(p_global->toplevel_widget),
872                    XtWindow(p_aww(cbs->aw)->shell),
873                    0);
874        }
875    }
876
877}
878
879void AW_clock_cursor(AW_root *root) {
880    p_global->set_cursor(0, 0, p_global->clock_cursor);
881}
882
883void AW_normal_cursor(AW_root *root) {
884    p_global->set_cursor(0, 0, 0);
885}
886
887// --------------
888//      focus
889
890
891static void AW_root_focusCB(Widget /*wgt*/, XtPointer awrp, XEvent*, Boolean*) {
892    AW_root *aw_root = (AW_root *)awrp;
893    AW_root_cblist::call(aw_root->focus_callback_list, aw_root);
894}
895
896void AW_root::set_focus_callback(AW_RCB f, AW_CL cd1, AW_CL cd2) {
897    AW_root_cblist::add(focus_callback_list, AW_root_callback(f, cd1, cd2));
898}
899
900static void AW_focusCB(Widget /*wgt*/, XtPointer aw_cb_struct, XEvent*, Boolean*) {
901    AW_cb_struct *cbs = (AW_cb_struct *) aw_cb_struct;
902    cbs->run_callback();
903}
904
905void AW_window::set_popup_callback(void (*f)(AW_window*, AW_CL, AW_CL), AW_CL cd1, AW_CL cd2) {
906    p_w->popup_cb = new AW_cb_struct(this, f, cd1, cd2, 0, p_w->popup_cb);
907}
908
909void AW_window::set_focus_callback(void (*f)(AW_window*, AW_CL, AW_CL), AW_CL cd1, AW_CL cd2) {
910    XtAddEventHandler(MIDDLE_WIDGET, EnterWindowMask, FALSE,
911    AW_focusCB, (XtPointer) new AW_cb_struct(this, f, cd1, cd2, 0));
912}
913
914// ---------------
915//      expose
916
917inline void AW_area_management::run_expose_callback() {
918    if (expose_cb) expose_cb->run_callback();
919}
920
921static void AW_exposeCB(Widget /*wgt*/, XtPointer aw_cb_struct, XmDrawingAreaCallbackStruct *call_data) {
922    XEvent *ev = call_data->event;
923    AW_area_management *aram = (AW_area_management *) aw_cb_struct;
924    if (ev->xexpose.count == 0) { // last expose cb
925        aram->run_expose_callback();
926    }
927}
928
929void AW_area_management::set_expose_callback(AW_window *aww, void (*f)(AW_window*, AW_CL, AW_CL), AW_CL cd1, AW_CL cd2) {
930    // insert expose callback for draw_area
931    if (!expose_cb) {
932        XtAddCallback(area, XmNexposeCallback, (XtCallbackProc) AW_exposeCB,
933                (XtPointer) this);
934    }
935    expose_cb = new AW_cb_struct(aww, f, cd1, cd2, 0, expose_cb);
936}
937
938void AW_window::set_expose_callback(AW_area area, void (*f)(AW_window*, AW_CL, AW_CL), AW_CL cd1, AW_CL cd2) {
939    AW_area_management *aram = MAP_ARAM(area);
940    if (aram) aram->set_expose_callback(this, f, cd1, cd2);
941}
942
943bool AW_area_management::is_expose_callback(AW_window * /* aww */, void (*f)(AW_window*, AW_CL, AW_CL)) {
944    return expose_cb && expose_cb->contains(f);
945}
946
947bool AW_window::is_expose_callback(AW_area area, void (*f)(AW_window*, AW_CL, AW_CL)) {
948    AW_area_management *aram = MAP_ARAM(area);
949    return aram && aram->is_expose_callback(this, f);
950}
951
952void AW_window::force_expose() {
953    XmDrawingAreaCallbackStruct da_struct;
954
955    da_struct.reason = XmCR_EXPOSE;
956    da_struct.event = (XEvent *) NULL;
957    da_struct.window = XtWindow(p_w->shell);
958
959    XtCallCallbacks(p_w->shell, XmNexposeCallback, (XtPointer) &da_struct);
960}
961
962// ---------------
963//      resize
964
965
966bool AW_area_management::is_resize_callback(AW_window * /* aww */, void (*f)(AW_window*, AW_CL, AW_CL)) {
967    return resize_cb && resize_cb->contains(f);
968}
969
970bool AW_window::is_resize_callback(AW_area area, void (*f)(AW_window*, AW_CL, AW_CL)) {
971    AW_area_management *aram = MAP_ARAM(area);
972    return aram && aram->is_resize_callback(this, f);
973}
974
975void AW_window::set_window_size(int width, int height) {
976    XtVaSetValues(p_w->shell, XmNwidth, (int)width, XmNheight, (int)height, NULL);
977}
978void AW_window::get_window_size(int &width, int &height) {
979    unsigned short hoffset = 0;
980    if (p_w->menu_bar[0]) XtVaGetValues(p_w->menu_bar[0], XmNheight, &hoffset, NULL);
981    width = _at->max_x_size;
982    height = hoffset + _at->max_y_size;
983}
984
985void AW_window::set_window_frame_pos(int x, int y) {
986    // this will set the position of the frame around the client-area (see also WM_top_offset ..)
987    XtVaSetValues(p_w->shell, XmNx, (int)x, XmNy, (int)y, NULL);
988}
989void AW_window::get_window_content_pos(int& xpos, int& ypos) {
990    // this will report the position of the client-area (see also WM_top_offset ..)
991    unsigned short x, y;
992    XtVaGetValues(p_w->shell, XmNx, &x, XmNy, &y, NULL);
993    xpos = x;
994    ypos = y;
995}
996
997void AW_window::window_fit() {
998    int width, height;
999    get_window_size(width, height);
1000    set_window_size(width, height);
1001}
1002
1003void AW_window::get_screen_size(int &width, int &height) {
1004    Screen *screen = XtScreen(p_w->shell);
1005
1006    width  = WidthOfScreen(screen);
1007    height = HeightOfScreen(screen);
1008}
1009
1010bool AW_window::get_mouse_pos(int& x, int& y) {
1011    Display      *= XtDisplay(p_w->shell);
1012    Window        w1 = XtWindow(p_w->shell);
1013    Window        w2;
1014    Window        w3;
1015    int           rx, ry;
1016    int           wx, wy;
1017    unsigned int  mask;
1018
1019    Bool ok = XQueryPointer(d, w1, &w2, &w3, &rx, &ry, &wx, &wy, &mask);
1020
1021    if (ok) {
1022#if defined(DEBUG) && 0
1023        printf("get_mouse_pos: rx/ry=%i/%i wx/wy=%i/%i\n", rx, ry, wx, wy);
1024#endif // DEBUG
1025        x = rx;
1026        y = ry;
1027    }
1028    return ok;
1029}
1030
1031static int is_resize_event(Display *display, XEvent *event, XPointer) {
1032    // Predicate function: checks, if the given event is a ResizeEvent
1033    if (event && (event->type == ResizeRequest || event->type
1034            == ConfigureNotify) && event->xany.display == display) {
1035        return 1;
1036    }
1037    return 0;
1038}
1039
1040static void cleanupResizeEvents(Display *display) {
1041    // Removes redundant resize events from the x-event queue
1042    if (display) {
1043        XLockDisplay(display);
1044        XEvent event;
1045        if (XCheckIfEvent(display, &event, is_resize_event, 0)) {
1046            // Some magic happens here... ;-) (removing redundant events from queue)
1047            while (XCheckIfEvent(display, &event, is_resize_event, 0))
1048                ;
1049            // Keep last Event in queue
1050            XPutBackEvent(display, &event);
1051        }
1052        XUnlockDisplay(display);
1053    }
1054}
1055
1056
1057inline void AW_area_management::run_resize_callback() {
1058    if (resize_cb) resize_cb->run_callback();
1059}
1060
1061static void AW_resizeCB_draw_area(Widget /*wgt*/, XtPointer aw_cb_struct, XtPointer /*call_data*/) {
1062    AW_area_management *aram = (AW_area_management *) aw_cb_struct;
1063    cleanupResizeEvents(aram->get_common()->get_display());
1064    aram->run_resize_callback();
1065}
1066
1067void AW_area_management::set_resize_callback(AW_window *aww, void (*f)(AW_window*, AW_CL, AW_CL), AW_CL cd1, AW_CL cd2) {
1068    // insert resize callback for draw_area
1069    if (!resize_cb) {
1070        XtAddCallback(area, XmNresizeCallback,
1071                (XtCallbackProc) AW_resizeCB_draw_area, (XtPointer) this);
1072    }
1073    resize_cb = new AW_cb_struct(aww, f, cd1, cd2, 0, resize_cb);
1074}
1075
1076void AW_window::set_resize_callback(AW_area area, void (*f)(AW_window*, AW_CL, AW_CL), AW_CL cd1, AW_CL cd2) {
1077    AW_area_management *aram = MAP_ARAM(area);
1078    if (!aram)
1079        return;
1080    aram->set_resize_callback(this, f, cd1, cd2);
1081}
1082
1083// -------------------
1084//      user input
1085
1086
1087static void AW_inputCB_draw_area(Widget wgt, XtPointer aw_cb_struct, XmDrawingAreaCallbackStruct *call_data) {
1088    XEvent             *ev                        = call_data->event;
1089    AW_cb_struct       *cbs                       = (AW_cb_struct *) aw_cb_struct;
1090    AW_window          *aww                       = cbs->aw;
1091    bool                run_callback              = false;
1092    bool                run_double_click_callback = false;
1093    AW_area_management *area                      = 0;
1094    {
1095        int i;
1096        for (i=0; i<AW_MAX_AREA; i++) {
1097            if (p_aww(aww)->areas[i]->get_area() == wgt) {
1098                area = p_aww(aww)->areas[i];
1099                break;
1100            }
1101        }
1102    }
1103
1104    if (ev->xbutton.type == ButtonPress) {
1105        aww->event.type = AW_Mouse_Press;
1106        aww->event.button = ev->xbutton.button;
1107        aww->event.x = ev->xbutton.x;
1108        aww->event.y = ev->xbutton.y;
1109        aww->event.keycode = AW_KEY_NONE;
1110        aww->event.keymodifier = AW_KEYMODE_NONE;
1111        aww->event.character = '\0';
1112
1113        if (area && area->get_double_click_cb()) {
1114            if ((ev->xbutton.time - area->get_click_time()) < 200) {
1115                run_double_click_callback = true;
1116            }
1117            else {
1118                run_callback = true;
1119            }
1120            area->set_click_time(ev->xbutton.time);
1121        }
1122        else {
1123            run_callback = true;
1124        }
1125
1126        aww->event.time = ev->xbutton.time;
1127    }
1128    else if (ev->xbutton.type == ButtonRelease) {
1129        aww->event.type = AW_Mouse_Release;
1130        aww->event.button = ev->xbutton.button;
1131        aww->event.x = ev->xbutton.x;
1132        aww->event.y = ev->xbutton.y;
1133        aww->event.keycode = AW_KEY_NONE;
1134        aww->event.keymodifier = AW_KEYMODE_NONE;
1135        aww->event.character = '\0';
1136        //  aww->event.time     use old time
1137
1138        run_callback = true;
1139    }
1140    else if (ev->xkey.type == KeyPress || ev->xkey.type == KeyRelease) {
1141        aww->event.time = ev->xbutton.time;
1142
1143        const awXKeymap *mykey = aw_xkey_2_awkey(&(ev->xkey));
1144
1145        aww->event.keycode = mykey->awkey;
1146        aww->event.keymodifier = mykey->awmod;
1147
1148        if (mykey->awstr) {
1149            aww->event.character = mykey->awstr[0];
1150        }
1151        else {
1152            aww->event.character = 0;
1153        }
1154
1155        if (ev->xkey.type == KeyPress) {
1156            aww->event.type = AW_Keyboard_Press;
1157        }
1158        else {
1159            aww->event.type = AW_Keyboard_Release;
1160        }
1161        aww->event.button = 0;
1162        aww->event.x = ev->xbutton.x;
1163        aww->event.y = ev->xbutton.y;
1164
1165        if (!mykey->awmod && mykey->awkey >= AW_KEY_F1 && mykey->awkey
1166                <= AW_KEY_F12 && p_aww(aww)->modes_f_callbacks && p_aww(aww)->modes_f_callbacks[mykey->awkey-AW_KEY_F1]
1167                && aww->event.type == AW_Keyboard_Press) {
1168            p_aww(aww)->modes_f_callbacks[mykey->awkey-AW_KEY_F1]->run_callback();
1169        }
1170        else {
1171            run_callback = true;
1172        }
1173    }
1174
1175    if (run_double_click_callback) {
1176        if (cbs->help_text == (char*)1) {
1177            cbs->run_callback();
1178        }
1179        else {
1180            if (area)
1181                area->get_double_click_cb()->run_callback();
1182        }
1183    }
1184
1185    if (run_callback && (cbs->help_text == (char*)0)) {
1186        cbs->run_callback();
1187    }
1188}
1189
1190void AW_area_management::set_input_callback(AW_window *aww, void (*f)(AW_window*, AW_CL, AW_CL), AW_CL cd1, AW_CL cd2) {
1191    XtAddCallback(area, XmNinputCallback,
1192            (XtCallbackProc) AW_inputCB_draw_area,
1193            (XtPointer) new AW_cb_struct(aww, f, cd1, cd2, (char*)0));
1194}
1195
1196void AW_window::set_input_callback(AW_area area, void (*f)(AW_window*, AW_CL, AW_CL), AW_CL cd1, AW_CL cd2) {
1197    AW_area_management *aram = MAP_ARAM(area);
1198    if (!aram)
1199        return;
1200    aram->set_input_callback(this, f, cd1, cd2);
1201}
1202
1203void AW_area_management::set_double_click_callback(AW_window *aww, void (*f)(AW_window*, AW_CL, AW_CL), AW_CL cd1, AW_CL cd2) {
1204    double_click_cb = new AW_cb_struct(aww, f, cd1, cd2, (char*)0, double_click_cb);
1205}
1206
1207void AW_window::set_double_click_callback(AW_area area, void (*f)(AW_window*, AW_CL, AW_CL), AW_CL cd1, AW_CL cd2) {
1208    AW_area_management *aram = MAP_ARAM(area);
1209    if (!aram)
1210        return;
1211    aram->set_double_click_callback(this, f, cd1, cd2);
1212}
1213
1214void AW_window::get_event(AW_event *eventi) const {
1215    *eventi = event;
1216}
1217
1218// ---------------
1219//      motion
1220
1221static void AW_motionCB(Widget /*w*/, XtPointer aw_cb_struct, XEvent *ev, Boolean*) {
1222    AW_cb_struct *cbs = (AW_cb_struct *) aw_cb_struct;
1223
1224    cbs->aw->event.type    = AW_Mouse_Drag;
1225    cbs->aw->event.x       = ev->xmotion.x;
1226    cbs->aw->event.y       = ev->xmotion.y;
1227    cbs->aw->event.keycode = AW_KEY_NONE;
1228
1229    cbs->run_callback();
1230}
1231void AW_area_management::set_motion_callback(AW_window *aww, void (*f)(AW_window *, AW_CL, AW_CL), AW_CL cd1, AW_CL cd2) {
1232    XtAddEventHandler(area, ButtonMotionMask, False,
1233                      AW_motionCB, (XtPointer) new AW_cb_struct(aww, f, cd1, cd2, ""));
1234}
1235void AW_window::set_motion_callback(AW_area area, void (*f)(AW_window*, AW_CL, AW_CL), AW_CL cd1, AW_CL cd2) {
1236    AW_area_management *aram = MAP_ARAM(area);
1237    if (!aram)
1238        return;
1239    aram->set_motion_callback(this, f, cd1, cd2);
1240}
1241
1242struct fallbacks {
1243    const char *fb;
1244    const char *awar;
1245    const char *init;
1246};
1247
1248static struct fallbacks aw_fb[] = {
1249    // Name         fallback awarname    default value
1250    { "FontList",   "window/font",       "8x13bold" },
1251    { "background", "window/background", "grey" },
1252    { "foreground", "window/foreground", "Black", },
1253    { 0,            "window/color_1",    "red", },
1254    { 0,            "window/color_2",    "green", },
1255    { 0,            "window/color_3",    "blue", },
1256    { 0,            0,                   0 }
1257};
1258
1259static const char *aw_awar_2_color[] = {
1260    "window/background",
1261    "window/foreground",
1262    "window/color_1",
1263    "window/color_2",
1264    "window/color_3",
1265    0
1266};
1267
1268
1269void AW_root::init_variables(AW_default database) {
1270    application_database     = database;
1271    hash_table_for_variables = GBS_create_hash(1000, GB_MIND_CASE);
1272    hash_for_windows         = GBS_create_hash(100, GB_MIND_CASE);
1273
1274    for (int i=0; aw_fb[i].awar; ++i) {
1275        awar_string(aw_fb[i].awar, aw_fb[i].init, application_database);
1276    }
1277}
1278
1279static long destroy_awar(const char *, long val, void *) {
1280    AW_awar *awar = (AW_awar*)val;
1281    delete   awar;
1282    return 0; // remove from hash
1283}
1284
1285void AW_root::exit_variables() {
1286    if (hash_table_for_variables) {
1287        GBS_hash_do_loop(hash_table_for_variables, destroy_awar, NULL);
1288        GBS_free_hash(hash_table_for_variables);
1289        hash_table_for_variables = NULL;
1290    }
1291
1292    if (hash_for_windows) {
1293        GBS_free_hash(hash_for_windows);
1294        hash_for_windows = NULL;
1295    }
1296
1297    if (application_database) {
1298        GBDATA *prop_main    = application_database;
1299        application_database = NULL;
1300        GB_close(prop_main);
1301    }
1302}
1303
1304static void aw_root_create_color_map(AW_root *root) {
1305    XColor xcolor_returned, xcolor_exakt;
1306    GBDATA *gbd = root->check_properties(NULL);
1307    p_global->color_table = (AW_rgb*)GB_calloc(sizeof(AW_rgb), AW_STD_COLOR_IDX_MAX);
1308
1309    if (p_global->screen_depth == 1) { // Black and White Monitor
1310        unsigned long white = WhitePixelOfScreen(XtScreen(p_global->toplevel_widget));
1311        unsigned long black = BlackPixelOfScreen(XtScreen(p_global->toplevel_widget));
1312        p_global->foreground = black;
1313        p_global->background = white;
1314        for (int i=0; i< AW_STD_COLOR_IDX_MAX; i++) {
1315            p_global->color_table[i] = black;
1316        }
1317        p_global->color_table[AW_WINDOW_FG] = white;
1318        p_global->color_table[AW_WINDOW_C1] = white;
1319        p_global->color_table[AW_WINDOW_C2] = white;
1320        p_global->color_table[AW_WINDOW_C3] = white;
1321    }
1322    else { // Color monitor
1323        const char **awar_2_color;
1324        int color;
1325        for (color = 0, awar_2_color = aw_awar_2_color;
1326             *awar_2_color;
1327             awar_2_color++, color++)
1328        {
1329            const char *name_of_color = GB_read_char_pntr(GB_search(gbd, *awar_2_color, GB_FIND));
1330            if (XAllocNamedColor(p_global->display, p_global->colormap, name_of_color, &xcolor_returned, &xcolor_exakt) == 0) {
1331                fprintf(stderr, "XAllocColor failed: %s\n", name_of_color);
1332            }
1333            else {
1334                p_global->color_table[color] = xcolor_returned.pixel;
1335            }
1336        }
1337        p_global->foreground = BlackPixelOfScreen(XtScreen(p_global->toplevel_widget));
1338        XtVaGetValues(p_global->toplevel_widget, XmNbackground,
1339        &p_global->background, NULL);
1340    }
1341    // AW_WINDOW_DRAG see init_devices
1342
1343}
1344
1345static void dump_stdout(const char *msg) {
1346    fprintf(stdout, "ARB: %s\n", msg);
1347}
1348static void aw_message_and_dump_stderr(const char *msg) {
1349    fflush(stdout);
1350    fprintf(stderr, "ARB: %s\n", msg); // print to console as well
1351    fflush(stderr);
1352    aw_message(msg);
1353}
1354
1355static arb_status_implementation AW_status_impl = {
1356    AST_RANDOM, 
1357    aw_openstatus,
1358    aw_closestatus,
1359    aw_status_title, // set_title
1360    AW_status, // set_subtitle
1361    AW_status, // set_gauge
1362    AW_status, // user_abort
1363};
1364
1365static arb_handlers aw_handlers = {
1366    aw_message_and_dump_stderr,
1367    aw_message,
1368    dump_stdout,
1369    AW_status_impl, 
1370};
1371
1372void AW_root::init_root(const char *programname, bool no_exit) {
1373    // initialize ARB X application
1374    int          a = 0;
1375    XFontStruct *fontstruct;
1376    char        *fallback_resources[100];
1377
1378    prvt->action_hash = GBS_create_hash(1000, GB_MIND_CASE);
1379
1380    p_r-> no_exit = no_exit;
1381    program_name  = strdup(programname);
1382
1383    int i;
1384    for (i=0; i<1000 && aw_fb[i].fb; i++) {
1385        GBDATA *gb_awar       = GB_search((GBDATA*)application_database, aw_fb[i].awar, GB_FIND);
1386        fallback_resources[i] = GBS_global_string_copy("*%s: %s", aw_fb[i].fb, GB_read_char_pntr(gb_awar));
1387    }
1388    fallback_resources[i] = 0;
1389
1390    ARB_install_handlers(aw_handlers);
1391   
1392    // @@@ FIXME: the next line hangs if program runs inside debugger
1393    p_r->toplevel_widget = XtOpenApplication(&(p_r->context), programname,
1394            NULL, 0, // XrmOptionDescRec+numOpts
1395            &a, // &argc
1396            NULL, // argv
1397            fallback_resources,
1398            applicationShellWidgetClass, // widget class
1399            NULL, 0);
1400
1401    for (i=0; i<1000 && fallback_resources[i]; i++) {
1402        free(fallback_resources[i]);
1403    }
1404
1405    p_r->display = XtDisplay(p_r->toplevel_widget);
1406
1407    if (p_r->display == NULL) {
1408        printf("cannot open display\n");
1409        exit(-1);
1410    }
1411    {
1412        GBDATA *gbd = (GBDATA*)application_database;
1413        const char *font = GB_read_char_pntr(GB_search(gbd, "window/font", GB_FIND));
1414        if (!(fontstruct = XLoadQueryFont(p_r->display, font))) {
1415            if (!(fontstruct = XLoadQueryFont(p_r->display, "fixed"))) {
1416                printf("can not load font\n");
1417                exit(-1);
1418            }
1419        }
1420    }
1421
1422    if (fontstruct->max_bounds.width == fontstruct->min_bounds.width) {
1423        font_width = fontstruct->max_bounds.width;
1424    }
1425    else {
1426        font_width = (fontstruct->min_bounds.width
1427                + fontstruct->max_bounds.width) / 2;
1428    }
1429
1430    font_height = fontstruct->max_bounds.ascent
1431            + fontstruct->max_bounds.descent;
1432    font_ascent = fontstruct->max_bounds.ascent;
1433
1434    p_r->fontlist = XmFontListCreate(fontstruct, XmSTRING_DEFAULT_CHARSET);
1435
1436    button_sens_list = 0;
1437
1438    p_r->last_option_menu = p_r->current_option_menu = p_r->option_menu_list = NULL;
1439    p_r->last_toggle_field = p_r->toggle_field_list = NULL;
1440    p_r->last_selection_list = p_r->selection_list = NULL;
1441
1442    value_changed = false;
1443    y_correction_for_input_labels = 5;
1444    global_mask = AWM_ALL;
1445
1446    p_r->screen_depth = PlanesOfScreen(XtScreen(p_r->toplevel_widget));
1447    if (p_r->screen_depth == 1) {
1448        color_mode = AW_MONO_COLOR;
1449    }
1450    else {
1451        color_mode = AW_RGB_COLOR;
1452    }
1453    p_r->colormap = DefaultColormapOfScreen(XtScreen(p_r->toplevel_widget));
1454    p_r->clock_cursor = XCreateFontCursor(XtDisplay(p_r->toplevel_widget), XC_watch);
1455    p_r->question_cursor = XCreateFontCursor(XtDisplay(p_r->toplevel_widget), XC_question_arrow);
1456
1457    aw_root_create_color_map(this);
1458    aw_root_init_font(XtDisplay(p_r->toplevel_widget));
1459    aw_install_xkeys(XtDisplay(p_r->toplevel_widget));
1460
1461}
1462
1463void AW_root::exit_root() {
1464    aw_uninstall_xkeys();
1465}
1466
1467void AW_window::_get_area_size(AW_area area, AW_screen_area *square) {
1468    AW_area_management *aram = MAP_ARAM(area);
1469    *square = aram->get_common()->get_screen();
1470}
1471
1472static void horizontal_scrollbar_redefinition_cb(class AW_root */*aw_root*/, AW_CL cd1, AW_CL cd2) {
1473    AW_screen_area screen;
1474
1475    char buffer[200];
1476    AW_window *aw = (AW_window *)cd1;
1477    Widget w = (Widget)cd2;
1478
1479    aw->_get_area_size(AW_MIDDLE_AREA, &screen);
1480
1481    sprintf(buffer, "window/%s/horizontal_page_increment",
1482            aw->window_defaults_name);
1483    XtVaSetValues(w, XmNpageIncrement, (int)((screen.r
1484            -aw->left_indent_of_horizontal_scrollbar)*(aw->get_root()->awar(buffer)->read_int()*0.01)), NULL);
1485
1486    sprintf(buffer, "window/%s/scroll_width_horizontal",
1487            aw->window_defaults_name);
1488    XtVaSetValues(w, XmNincrement, (int)(aw->get_root()->awar(buffer)->read_int()), NULL);
1489
1490    sprintf(buffer, "window/%s/scroll_delay_horizontal",
1491            aw->window_defaults_name);
1492    XtVaSetValues(w, XmNrepeatDelay, (int)(aw->get_root()->awar(buffer)->read_int()), NULL);
1493
1494}
1495
1496static void vertical_scrollbar_redefinition_cb(class AW_root */*aw_root*/, AW_CL cd1, AW_CL cd2) {
1497    AW_screen_area screen;
1498
1499    char buffer[200];
1500    AW_window *aw = (AW_window *)cd1;
1501    Widget w = (Widget)cd2;
1502
1503    aw->_get_area_size(AW_MIDDLE_AREA, &screen);
1504
1505    sprintf(buffer, "window/%s/vertical_page_increment",
1506            aw->window_defaults_name);
1507    XtVaSetValues(w, XmNpageIncrement, (int)((screen.b
1508            -aw->top_indent_of_vertical_scrollbar
1509            -aw->bottom_indent_of_vertical_scrollbar)*(aw->get_root()->awar(buffer)->read_int()*0.01)), NULL);
1510
1511    sprintf(buffer, "window/%s/scroll_width_vertical", aw->window_defaults_name);
1512    XtVaSetValues(w, XmNincrement, (int)(aw->get_root()->awar(buffer)->read_int()), NULL);
1513
1514    sprintf(buffer, "window/%s/scroll_delay_vertical", aw->window_defaults_name);
1515    XtVaSetValues(w, XmNrepeatDelay, (int)(aw->get_root()->awar(buffer)->read_int()), NULL);
1516}
1517
1518void AW_window::create_window_variables() {
1519
1520    char buffer[200];
1521    memset(buffer, 0, 200);
1522    sprintf(buffer, "window/%s/horizontal_page_increment", window_defaults_name);
1523    get_root()->awar_int(buffer, 50);
1524    get_root()->awar(buffer)->add_callback(
1525            (AW_RCB)horizontal_scrollbar_redefinition_cb, (AW_CL)this,
1526            (AW_CL)p_w->scroll_bar_horizontal);
1527
1528    sprintf(buffer, "window/%s/vertical_page_increment", window_defaults_name);
1529    get_root()->awar_int(buffer, 50);
1530    get_root()->awar(buffer)->add_callback((AW_RCB)vertical_scrollbar_redefinition_cb,
1531            (AW_CL)this, (AW_CL)p_w->scroll_bar_vertical);
1532
1533    sprintf(buffer, "window/%s/scroll_delay_vertical", window_defaults_name);
1534    get_root()->awar_int(buffer, 20);
1535    get_root()->awar(buffer)->add_callback((AW_RCB)vertical_scrollbar_redefinition_cb,
1536            (AW_CL)this, (AW_CL)p_w->scroll_bar_vertical);
1537
1538    sprintf(buffer, "window/%s/scroll_delay_horizontal", window_defaults_name);
1539    get_root()->awar_int(buffer, 20);
1540    get_root()->awar(buffer)->add_callback(
1541            (AW_RCB)horizontal_scrollbar_redefinition_cb, (AW_CL)this,
1542            (AW_CL)p_w->scroll_bar_horizontal);
1543
1544    sprintf(buffer, "window/%s/scroll_width_horizontal", window_defaults_name);
1545    get_root()->awar_int(buffer, 9);
1546    get_root()->awar(buffer)->add_callback(
1547            (AW_RCB)horizontal_scrollbar_redefinition_cb, (AW_CL)this,
1548            (AW_CL)p_w->scroll_bar_horizontal);
1549
1550    sprintf(buffer, "window/%s/scroll_width_vertical", window_defaults_name);
1551    get_root()->awar_int(buffer, 20);
1552    get_root()->awar(buffer)->add_callback((AW_RCB)vertical_scrollbar_redefinition_cb,
1553            (AW_CL)this, (AW_CL)p_w->scroll_bar_vertical);
1554
1555}
1556
1557void AW_area_management::create_devices(AW_window *aww, AW_area ar) {
1558    AW_root *root = aww->get_root();
1559    common = new AW_common_Xm(XtDisplay(area), XtWindow(area), p_global->color_table, aww->color_table, aww->color_table_size, aww, ar);
1560}
1561
1562AW_color_idx AW_window::alloc_named_data_color(int colnum, char *colorname) {
1563    if (!color_table_size) {
1564        color_table_size = AW_STD_COLOR_IDX_MAX + colnum;
1565        color_table      = (AW_rgb*)malloc(sizeof(AW_rgb) *color_table_size);
1566        for (int i = 0; i<color_table_size; ++i) color_table[i] = AW_NO_COLOR;
1567    }
1568    else {
1569        if (colnum>=color_table_size) {
1570            long new_size = colnum+8;
1571            color_table   = (AW_rgb*)realloc(color_table, new_size*sizeof(AW_rgb)); // valgrinders : never freed because AW_window never is freed
1572            for (int i = color_table_size; i<new_size; ++i) color_table[i] = AW_NO_COLOR;
1573            color_table_size = new_size;
1574        }
1575    }
1576    XColor xcolor_returned, xcolor_exakt;
1577
1578    if (p_global->screen_depth == 1) { // Black and White Monitor
1579        static int col = 1;
1580        if (colnum == AW_DATA_BG) {
1581            col = 1;
1582            if (strcmp(colorname, "white"))
1583                col *= -1;
1584        }
1585        if (col==1) {
1586            color_table[colnum] = WhitePixelOfScreen(XtScreen(p_global->toplevel_widget));
1587        }
1588        else {
1589            color_table[colnum] = BlackPixelOfScreen(XtScreen(p_global->toplevel_widget));
1590        }
1591        if (colnum == AW_DATA_BG)
1592            col *= -1;
1593    }
1594    else { // Color monitor
1595        if (color_table[colnum] != AW_NO_COLOR) {
1596            unsigned long color = color_table[colnum];
1597            XFreeColors(p_global->display, p_global->colormap, &color, 1, 0);
1598        }
1599        if (XAllocNamedColor(p_global->display, p_global->colormap, colorname, &xcolor_returned, &xcolor_exakt) == 0) {
1600            aw_message(GBS_global_string("XAllocColor failed: %s\n", colorname));
1601            color_table[colnum] = AW_NO_COLOR;
1602        }
1603        else {
1604            color_table[colnum] = xcolor_returned.pixel;
1605        }
1606    }
1607    if (colnum == AW_DATA_BG) {
1608        XtVaSetValues(p_w->areas[AW_MIDDLE_AREA]->get_area(), XmNbackground, color_table[colnum], NULL);
1609    }
1610    return (AW_color_idx)colnum;
1611}
1612
1613void AW_window::create_devices() {
1614    unsigned long background_color;
1615    if (p_w->areas[AW_INFO_AREA]) {
1616        p_w->areas[AW_INFO_AREA]->create_devices(this, AW_INFO_AREA);
1617        XtVaGetValues(p_w->areas[AW_INFO_AREA]->get_area(), XmNbackground, &background_color, NULL);
1618        p_global->color_table[AW_WINDOW_DRAG] = background_color ^ p_global->color_table[AW_WINDOW_FG];
1619    }
1620    if (p_w->areas[AW_MIDDLE_AREA]) {
1621        p_w->areas[AW_MIDDLE_AREA]->create_devices(this, AW_MIDDLE_AREA);
1622    }
1623    if (p_w->areas[AW_BOTTOM_AREA]) {
1624        p_w->areas[AW_BOTTOM_AREA]->create_devices(this, AW_BOTTOM_AREA);
1625    }
1626}
1627
1628void AW_help_entry_pressed(AW_window *aww) {
1629    AW_root *root = aww->get_root();
1630    p_global->help_active = 1;
1631}
1632
1633void aw_create_help_entry(AW_window *aww) {
1634    aww->insert_help_topic("Click here and then on the questionable button/menu/...", "P", 0,
1635                           AWM_ALL, (AW_CB)AW_help_entry_pressed, 0, 0);
1636}
1637
1638const char *aw_str_2_label(const char *str, AW_window *aww) {
1639    aw_assert(str);
1640
1641    static const char *last_label = 0;
1642    static const char *last_str   = 0;
1643    static AW_window  *last_aww   = 0;
1644
1645    const char *label;
1646    if (str == last_str && aww == last_aww) { // reuse result ?
1647        label = last_label;
1648    }
1649    else {
1650        if (str[0] == '#') {
1651            label = GB_path_in_ARBLIB("pixmaps", str+1);
1652        }
1653        else {
1654            AW_awar *is_awar = aww->get_root()->label_is_awar(str);
1655
1656            if (is_awar) { // for labels displaying awar values, insert dummy text here
1657                int wanted_len = aww->_at->length_of_buttons - 2;
1658                if (wanted_len < 1) wanted_len = 1;
1659
1660                char *labelbuf       = GB_give_buffer(wanted_len+1);
1661                memset(labelbuf, 'y', wanted_len);
1662                labelbuf[wanted_len] = 0;
1663
1664                label = labelbuf;
1665            }
1666            else {
1667                label = str;
1668            }
1669        }
1670
1671        // store results locally, cause aw_str_2_label is nearly always called twice with same arguments
1672        // (see RES_LABEL_CONVERT)
1673        last_label = label;
1674        last_str   = str;
1675        last_aww   = aww;
1676    }
1677    return label;
1678}
1679
1680void AW_label_in_awar_list(AW_window *aww, Widget widget, const char *str) {
1681    AW_awar *is_awar = aww->get_root()->label_is_awar(str);
1682    if (is_awar) {
1683        char *var_value = is_awar->read_as_string();
1684        if (var_value) {
1685            aww->update_label(widget, var_value);
1686        }
1687        else {
1688            aw_assert(0); // awar not found
1689            aww->update_label(widget, str);
1690        }
1691        free(var_value);
1692        is_awar->tie_widget(0, widget, AW_WIDGET_LABEL_FIELD, aww);
1693    }
1694}
1695
1696static void aw_window_avoid_destroy_cb(Widget, AW_window *, XmAnyCallbackStruct *) {
1697    aw_message("If YOU do not know what to answer, how should ARB know?\nPlease think again and answer the prompt!");
1698}
1699
1700static void aw_window_noexit_destroy_cb(Widget,  AW_window *aww, XmAnyCallbackStruct *) {
1701    aww->hide();
1702    // don't exit, when using destroy callback
1703}
1704
1705static void aw_window_destroy_cb(Widget,  AW_window *aww, XmAnyCallbackStruct *) {
1706    AW_root *root = aww->get_root();
1707    if ((p_global->main_aww == aww) || !p_global->main_aww->is_shown()) {
1708#ifdef NDEBUG
1709        if (!aw_ask_sure("quit_by_X", "Are you sure to quit?")) return;
1710#endif
1711        exit(0);
1712    }
1713    aww->hide();
1714}
1715
1716static void aw_update_window_geometry_awars(AW_window *aww) {
1717    AW_window_Motif *motif = p_aww(aww);
1718
1719    short          posx, posy;
1720    unsigned short width, height, borderwidth;
1721    XtVaGetValues(motif->shell, // bad hack
1722                  XmNborderWidth, &borderwidth,
1723                  XmNwidth, &width,
1724                  XmNheight, &height,
1725                  XmNx, &posx,
1726                  XmNy, &posy,
1727                  NULL);
1728
1729    if (motif->knows_WM_offset()) {
1730        posy -= motif->WM_top_offset;
1731        posx -= motif->WM_left_offset;
1732
1733        if (posx<0) posx = 0;
1734        if (posy<0) posy = 0;
1735
1736        aww->store_pos_in_awars(posx, posy);
1737    }
1738#if defined(DEBUG)
1739    else {
1740        fprintf(stderr, " WM_offsets unknown. Did not update awars!\n");
1741    }
1742#endif
1743    aww->store_size_in_awars(width, height);
1744}
1745
1746static long aw_loop_get_window_geometry(const char *, long val, void *) {
1747    aw_update_window_geometry_awars((AW_window *)val);
1748    return val;
1749}
1750
1751void aw_update_all_window_geometry_awars(AW_root *awr) {
1752    GBS_hash_do_loop(awr->hash_for_windows, aw_loop_get_window_geometry, NULL);
1753}
1754
1755static const char *existingPixmap(const char *iconpath, const char *name) {
1756    const char *icon = GBS_global_string("%s/%s.xpm", iconpath, name);
1757
1758    if (!GB_is_regularfile(icon)) {
1759        icon = GBS_global_string("%s/%s.bitmap", iconpath, name);
1760        if (!GB_is_regularfile(icon)) icon = NULL;
1761    }
1762
1763    return icon;
1764}
1765
1766static Pixmap getIcon(Screen *screen, const char *iconName, Pixel foreground, Pixel background) {
1767    static GB_HASH *icon_hash = 0;
1768    if (!icon_hash) icon_hash = GBS_create_hash(100, GB_MIND_CASE);
1769
1770    Pixmap pixmap = GBS_read_hash(icon_hash, iconName);
1771
1772    if (!pixmap && iconName) {
1773        const char *iconpath = GB_path_in_ARBLIB("pixmaps/icons");
1774        const char *iconFile = existingPixmap(iconpath, iconName);
1775
1776        if (iconFile) {
1777            char *ico = strdup(iconFile);
1778            pixmap    = XmGetPixmap(screen, ico, foreground, background);
1779            GBS_write_hash(icon_hash, iconName, pixmap);
1780            free(ico);
1781        }
1782    }
1783
1784    return pixmap;
1785}
1786
1787static void aw_set_delete_window_cb(AW_window *aww, Widget shell, bool allow_close) {
1788    Atom WM_DELETE_WINDOW = XmInternAtom(XtDisplay(shell), (char*)"WM_DELETE_WINDOW", False);
1789
1790    // remove any previous callbacks
1791    XmRemoveWMProtocolCallback(shell, WM_DELETE_WINDOW, (XtCallbackProc)aw_window_avoid_destroy_cb,  (caddr_t)aww);
1792    XmRemoveWMProtocolCallback(shell, WM_DELETE_WINDOW, (XtCallbackProc)aw_window_noexit_destroy_cb, (caddr_t)aww);
1793    XmRemoveWMProtocolCallback(shell, WM_DELETE_WINDOW, (XtCallbackProc)aw_window_destroy_cb,        (caddr_t)aww);
1794
1795    if (!allow_close) {
1796        XmAddWMProtocolCallback(shell, WM_DELETE_WINDOW, (XtCallbackProc)aw_window_avoid_destroy_cb, (caddr_t)aww);
1797    }
1798    else {
1799        AW_root *root = aww->get_root();
1800        if (p_global->no_exit) {
1801            XmAddWMProtocolCallback(shell, WM_DELETE_WINDOW, (XtCallbackProc)aw_window_noexit_destroy_cb, (caddr_t)aww);
1802        }
1803        else {
1804            XmAddWMProtocolCallback(shell, WM_DELETE_WINDOW, (XtCallbackProc)aw_window_destroy_cb, (caddr_t)aww);
1805        }
1806    }
1807}
1808
1809void AW_window::allow_delete_window(bool allow_close) {
1810    aw_set_delete_window_cb(this, p_w->shell, allow_close);
1811}
1812
1813Widget aw_create_shell(AW_window *aww, bool allow_resize, bool allow_close, int width, int height, int posx, int posy) {
1814    AW_root *root = aww->get_root();
1815    Widget shell;
1816
1817    // set minimum window size to size provided by init
1818    if (width >aww->_at->max_x_size) aww->_at->max_x_size = width;
1819    if (height>aww->_at->max_y_size) aww->_at->max_y_size = height;
1820
1821    if (!GBS_read_hash(root->hash_for_windows, aww->get_window_id())) {
1822        GBS_write_hash(root->hash_for_windows, aww->get_window_id(), (long)aww);
1823        bool has_user_geometry = false;
1824
1825        aww->create_user_geometry_awars(posx, posy, width, height);
1826
1827        int user_width, user_height; aww->get_size_from_awars(user_width, user_height);
1828        int user_posx,  user_posy;   aww->get_pos_from_awars(user_posx,  user_posy);
1829
1830        if (allow_resize) {
1831            if (width != user_width) { width = user_width; has_user_geometry = true; }
1832            if (height != user_height) { height = user_height; has_user_geometry = true; }
1833        }
1834
1835        // @@@ FIXME:  maximum should be set to current screen size minus some offset
1836        // to ensure that windows do not appear outside screen
1837        if (posx != user_posx) { posx = user_posx; has_user_geometry = true; }
1838        if (posy != user_posy) { posy = user_posy; has_user_geometry = true; }
1839
1840        if (has_user_geometry) {
1841            aww->recalc_size_atShow(AW_RESIZE_USER); // keep user geometry (only if user size is smaller than default size, the latter is used)
1842        }
1843        else { // no geometry yet
1844            aww->recalc_pos_atShow(AW_REPOS_TO_MOUSE_ONCE); // popup the window at current mouse position
1845        }
1846    }
1847
1848    if (allow_resize) {
1849        // create the window big enough to ensure that all widgets
1850        // are created in visible area (otherwise widget are crippled).
1851        // window will be resized later (on show)
1852
1853        width = 4000;
1854        height = 3000;
1855
1856        aww->recalc_size_atShow(AW_RESIZE_ANY);
1857    }
1858
1859    Widget  father      = p_global->toplevel_widget;
1860    Screen *screen      = XtScreen(father);
1861    Pixmap  icon_pixmap = getIcon(screen, aww->window_defaults_name, p_global->foreground, p_global->background);
1862
1863    if (!icon_pixmap) {
1864        icon_pixmap = getIcon(screen, root->program_name, p_global->foreground, p_global->background);
1865    }
1866
1867    if (!icon_pixmap) {
1868        GBK_terminatef("Missing icon pixmap for window '%s'\n", aww->window_defaults_name);
1869    }
1870    else if (icon_pixmap == XmUNSPECIFIED_PIXMAP) {
1871        GBK_terminatef("Failed to load icon pixmap for window '%s'\n", aww->window_defaults_name);
1872    }
1873
1874    int focusPolicy = root->focus_follows_mouse ? XmPOINTER : XmEXPLICIT;
1875
1876    if (!p_global->main_widget || !p_global->main_aww->is_shown()) {
1877        shell = XtVaCreatePopupShell("editor", applicationShellWidgetClass,
1878                                     father,
1879                                     XmNwidth, width,
1880                                     XmNheight, height,
1881                                     XmNx, posx,
1882                                     XmNy, posy,
1883                                     XmNtitle, aww->window_name,
1884                                     XmNiconName, aww->window_name,
1885                                     XmNkeyboardFocusPolicy, focusPolicy,
1886                                     XmNdeleteResponse, XmDO_NOTHING,
1887                                     XtNiconPixmap, icon_pixmap,
1888                                     NULL);
1889    }
1890    else {
1891        shell = XtVaCreatePopupShell("transient", transientShellWidgetClass,
1892                                     father,
1893                                     XmNwidth, width,
1894                                     XmNheight, height,
1895                                     XmNx, posx,
1896                                     XmNy, posy,
1897                                     XmNtitle, aww->window_name,
1898                                     XmNiconName, aww->window_name,
1899                                     XmNkeyboardFocusPolicy, focusPolicy,
1900                                     XmNdeleteResponse, XmDO_NOTHING,
1901                                     XtNiconPixmap, icon_pixmap,
1902                                     NULL);
1903    }
1904    XtAddEventHandler(shell, EnterWindowMask, FALSE, AW_root_focusCB, (XtPointer) aww->get_root());
1905
1906    if (!p_global->main_widget) {
1907        p_global->main_widget = shell;
1908        p_global->main_aww    = aww;
1909    }
1910    else {
1911        if (!p_global->main_aww->is_shown()) {  // now i am the root window
1912            p_global->main_widget = shell;
1913            p_global->main_aww    = aww;
1914        }
1915    }
1916
1917    aw_set_delete_window_cb(aww, shell, allow_close);
1918
1919    // set icon window (for window managers where iconified applications are dropped onto desktop or similar)
1920    {
1921        Window icon_window;
1922        XtVaGetValues(shell, XmNiconWindow, &icon_window, NULL);
1923
1924        Display *dpy = XtDisplay(shell);
1925        if (!icon_window) {
1926            XSetWindowAttributes attr;
1927            attr.background_pixmap = icon_pixmap;
1928
1929            int          xpos, ypos;
1930            unsigned int xsize, ysize, borderwidth, depth;
1931            Window       wroot;
1932
1933            if (XGetGeometry(dpy, icon_pixmap, &wroot, &xpos, &ypos, &xsize, &ysize, &borderwidth, &depth)) {
1934                icon_window = XCreateWindow(dpy, wroot, 0, 0, xsize, ysize, 0, depth, CopyFromParent, CopyFromParent, CWBackPixmap, &attr);
1935            }
1936        }
1937        if (!icon_window) {
1938            XtVaSetValues(shell, XmNiconPixmap, icon_pixmap, NULL);
1939        }
1940        else {
1941            XtVaSetValues(shell, XmNiconWindow, icon_window, NULL);
1942            XSetWindowBackgroundPixmap(dpy, icon_window, icon_pixmap);
1943            XClearWindow(dpy, icon_window);
1944        }
1945    }
1946
1947    return shell;
1948}
1949
1950
1951void aw_realize_widget(AW_window *aww) {
1952    if (p_aww(aww)->areas[AW_INFO_AREA] && p_aww(aww)->areas[AW_INFO_AREA]->get_form()) {
1953        XtManageChild(p_aww(aww)->areas[AW_INFO_AREA]->get_form());
1954    }
1955    if (p_aww(aww)->areas[AW_MIDDLE_AREA] && p_aww(aww)->areas[AW_MIDDLE_AREA]->get_form()) {
1956        XtManageChild(p_aww(aww)->areas[AW_MIDDLE_AREA]->get_form());
1957    }
1958    if (p_aww(aww)->areas[AW_BOTTOM_AREA] && p_aww(aww)->areas[AW_BOTTOM_AREA]->get_form()) {
1959        XtManageChild(p_aww(aww)->areas[AW_BOTTOM_AREA]->get_form());
1960    }
1961    XtRealizeWidget(p_aww(aww)->shell);
1962    p_aww(aww)->WM_top_offset = AW_CALC_OFFSET_ON_EXPOSE;
1963}
1964
1965void AW_window_menu_modes::init(AW_root *root_in, const char *wid, const char *windowname, int  width, int height) { 
1966    Widget      main_window;
1967    Widget      help_popup;
1968    Widget      help_label;
1969    Widget      separator;
1970    Widget      form1;
1971    Widget      form2;
1972    const char *help_button   = "HELP";
1973    const char *help_mnemonic = "H";
1974
1975#if defined(DUMP_MENU_LIST)
1976    initMenuListing(windowname);
1977#endif // DUMP_MENU_LIST
1978    root = root_in; // for macro
1979    window_name = strdup(windowname);
1980    window_defaults_name = GBS_string_2_key(wid);
1981
1982    int posx = 50;
1983    int posy = 50;
1984
1985    p_w->shell = aw_create_shell(this, true, true, width, height, posx, posy);
1986
1987    main_window = XtVaCreateManagedWidget("mainWindow1",
1988                                          xmMainWindowWidgetClass, p_w->shell,
1989                                          NULL);
1990
1991    p_w->menu_bar[0] = XtVaCreateManagedWidget("menu1", xmRowColumnWidgetClass,
1992                                               main_window,
1993                                               XmNrowColumnType, XmMENU_BAR,
1994                                               NULL);
1995
1996    // create shell for help-cascade
1997    help_popup = XtVaCreatePopupShell("menu_shell", xmMenuShellWidgetClass,
1998                                      p_w->menu_bar[0],
1999                                      XmNwidth, 1,
2000                                      XmNheight, 1,
2001                                      XmNallowShellResize, true,
2002                                      XmNoverrideRedirect, true,
2003                                      NULL);
2004
2005    // create row column in Pull-Down shell
2006    p_w->help_pull_down = XtVaCreateWidget("menu_row_column",
2007                                           xmRowColumnWidgetClass, help_popup,
2008                                           XmNrowColumnType, XmMENU_PULLDOWN,
2009                                           NULL);
2010
2011    // create HELP-label in menu bar
2012    help_label = XtVaCreateManagedWidget("menu1_top_b1",
2013                                         xmCascadeButtonWidgetClass, p_w->menu_bar[0],
2014                                         RES_CONVERT(XmNlabelString, help_button),
2015                                         RES_CONVERT(XmNmnemonic, help_mnemonic),
2016                                         XmNsubMenuId, p_w->help_pull_down, NULL);
2017    XtVaSetValues(p_w->menu_bar[0], XmNmenuHelpWidget, help_label, NULL);
2018    root->make_sensitive(help_label, AWM_ALL);
2019
2020    form1 = XtVaCreateManagedWidget("form1",
2021                                    xmFormWidgetClass,
2022                                    main_window,
2023                                    // XmNwidth, width,
2024                                    // XmNheight, height,
2025                                    XmNresizePolicy, XmRESIZE_NONE,
2026                                    // XmNx, 0,
2027                                    // XmNy, 0,
2028                                    NULL);
2029
2030    p_w->mode_area = XtVaCreateManagedWidget("mode area",
2031                                             xmDrawingAreaWidgetClass,
2032                                             form1,
2033                                             XmNresizePolicy, XmRESIZE_NONE,
2034                                             XmNwidth, 38,
2035                                             XmNheight, height,
2036                                             XmNx, 0,
2037                                             XmNy, 0,
2038                                             XmNleftOffset, 0,
2039                                             XmNtopOffset, 0,
2040                                             XmNbottomAttachment, XmATTACH_FORM,
2041                                             XmNleftAttachment, XmATTACH_POSITION,
2042                                             XmNtopAttachment, XmATTACH_POSITION,
2043                                             XmNmarginHeight, 2,
2044                                             XmNmarginWidth, 1,
2045                                             NULL);
2046
2047    separator = XtVaCreateManagedWidget("separator",
2048                                        xmSeparatorWidgetClass,
2049                                        form1,
2050                                        XmNx, 37,
2051                                        XmNshadowThickness, 4,
2052                                        XmNorientation, XmVERTICAL,
2053                                        XmNbottomAttachment, XmATTACH_FORM,
2054                                        XmNtopAttachment, XmATTACH_FORM,
2055                                        XmNleftAttachment, XmATTACH_NONE,
2056                                        XmNleftWidget, NULL,
2057                                        XmNrightAttachment, XmATTACH_NONE,
2058                                        XmNleftOffset, 70,
2059                                        XmNleftPosition, 0,
2060                                        NULL);
2061
2062    form2 = XtVaCreateManagedWidget("form2",
2063                                    xmFormWidgetClass,
2064                                    form1,
2065                                    XmNwidth, width,
2066                                    XmNheight, height,
2067                                    XmNtopOffset, 0,
2068                                    XmNbottomOffset, 0,
2069                                    XmNleftOffset, 0,
2070                                    XmNrightOffset, 0,
2071                                    XmNrightAttachment, XmATTACH_FORM,
2072                                    XmNbottomAttachment, XmATTACH_FORM,
2073                                    XmNleftAttachment, XmATTACH_WIDGET,
2074                                    XmNleftWidget, separator,
2075                                    XmNtopAttachment, XmATTACH_POSITION,
2076                                    XmNresizePolicy, XmRESIZE_NONE,
2077                                    XmNx, 0,
2078                                    XmNy, 0,
2079                                    NULL);
2080    p_w->areas[AW_INFO_AREA] =
2081        new AW_area_management(root, form2, XtVaCreateManagedWidget("info_area",
2082                                                                    xmDrawingAreaWidgetClass,
2083                                                                    form2,
2084                                                                    XmNheight, 0,
2085                                                                    XmNbottomAttachment, XmATTACH_NONE,
2086                                                                    XmNtopAttachment, XmATTACH_FORM,
2087                                                                    XmNleftAttachment, XmATTACH_FORM,
2088                                                                    XmNrightAttachment, XmATTACH_FORM,
2089                                                                    XmNmarginHeight, 2,
2090                                                                    XmNmarginWidth, 2,
2091                                                                    NULL));
2092
2093    p_w->areas[AW_BOTTOM_AREA] =
2094        new AW_area_management(root, form2, XtVaCreateManagedWidget("bottom_area",
2095                                                                    xmDrawingAreaWidgetClass,
2096                                                                    form2,
2097                                                                    XmNheight, 0,
2098                                                                    XmNbottomAttachment, XmATTACH_FORM,
2099                                                                    XmNtopAttachment, XmATTACH_NONE,
2100                                                                    XmNleftAttachment, XmATTACH_FORM,
2101                                                                    XmNrightAttachment, XmATTACH_FORM,
2102                                                                    NULL));
2103
2104    p_w->scroll_bar_horizontal = XtVaCreateManagedWidget("scroll_bar_horizontal",
2105                                                         xmScrollBarWidgetClass,
2106                                                         form2,
2107                                                         XmNheight, 15,
2108                                                         XmNminimum, 0,
2109                                                         XmNmaximum, AW_SCROLL_MAX,
2110                                                         XmNincrement, 10,
2111                                                         XmNsliderSize, AW_SCROLL_MAX,
2112                                                         XmNrightAttachment, XmATTACH_FORM,
2113                                                         XmNbottomAttachment, XmATTACH_FORM,
2114                                                         XmNbottomOffset, 0,
2115                                                         XmNleftAttachment, XmATTACH_FORM,
2116                                                         XmNtopAttachment, XmATTACH_NONE,
2117                                                         XmNorientation, XmHORIZONTAL,
2118                                                         XmNrightOffset, 18,
2119                                                         NULL);
2120
2121    p_w->scroll_bar_vertical = XtVaCreateManagedWidget("scroll_bar_vertical",
2122                                                       xmScrollBarWidgetClass,
2123                                                       form2,
2124                                                       XmNwidth, 15,
2125                                                       XmNminimum, 0,
2126                                                       XmNmaximum, AW_SCROLL_MAX,
2127                                                       XmNincrement, 10,
2128                                                       XmNsliderSize, AW_SCROLL_MAX,
2129                                                       XmNrightAttachment, XmATTACH_FORM,
2130                                                       XmNbottomAttachment, XmATTACH_WIDGET,
2131                                                       XmNbottomWidget, p_w->scroll_bar_horizontal,
2132                                                       XmNbottomOffset, 3,
2133                                                       XmNleftOffset, 3,
2134                                                       XmNrightOffset, 3,
2135                                                       XmNleftAttachment, XmATTACH_NONE,
2136                                                       XmNtopAttachment, XmATTACH_WIDGET,
2137                                                       XmNtopWidget, INFO_WIDGET,
2138                                                       NULL);
2139
2140    p_w->frame = XtVaCreateManagedWidget("draw_area",
2141                                         xmFrameWidgetClass,
2142                                         form2,
2143                                         XmNshadowType, XmSHADOW_IN,
2144                                         XmNshadowThickness, 2,
2145                                         XmNleftOffset, 3,
2146                                         XmNtopOffset, 3,
2147                                         XmNbottomOffset, 3,
2148                                         XmNrightOffset, 3,
2149                                         XmNbottomAttachment, XmATTACH_WIDGET,
2150                                         XmNbottomWidget, p_w->scroll_bar_horizontal,
2151                                         XmNtopAttachment, XmATTACH_FORM,
2152                                         XmNtopOffset, 0,
2153                                         XmNleftAttachment, XmATTACH_FORM,
2154                                         XmNrightAttachment, XmATTACH_WIDGET,
2155                                         XmNrightWidget, p_w->scroll_bar_vertical,
2156                                         NULL);
2157
2158    p_w->areas[AW_MIDDLE_AREA] =
2159        new AW_area_management(root, p_w->frame, XtVaCreateManagedWidget("draw area",
2160                                                                         xmDrawingAreaWidgetClass,
2161                                                                         p_w->frame,
2162                                                                         XmNmarginHeight, 0,
2163                                                                         XmNmarginWidth, 0,
2164                                                                         NULL));
2165
2166    XmMainWindowSetAreas(main_window, p_w->menu_bar[0], (Widget) NULL, (Widget) NULL, (Widget) NULL, form1);
2167
2168    aw_realize_widget(this);
2169
2170    create_devices();
2171    aw_create_help_entry(this);
2172    create_window_variables();
2173}
2174
2175void AW_window_menu::init(AW_root *root_in, const char *wid, const char *windowname, int width, int height) { 
2176    Widget      main_window;
2177    Widget      help_popup;
2178    Widget      help_label;
2179    Widget      separator;
2180    Widget      form1;
2181    Widget      form2;
2182    const char *help_button   = "HELP";
2183    const char *help_mnemonic = "H";
2184
2185#if defined(DUMP_MENU_LIST)
2186    initMenuListing(windowname);
2187#endif // DUMP_MENU_LIST
2188    root = root_in; // for macro
2189    window_name = strdup(windowname);
2190    window_defaults_name = GBS_string_2_key(wid);
2191
2192    int posx = 50;
2193    int posy = 50;
2194
2195    p_w->shell = aw_create_shell(this, true, true, width, height, posx, posy);
2196
2197    main_window = XtVaCreateManagedWidget("mainWindow1",
2198                                          xmMainWindowWidgetClass, p_w->shell,
2199                                          NULL);
2200
2201    p_w->menu_bar[0] = XtVaCreateManagedWidget("menu1", xmRowColumnWidgetClass,
2202                                               main_window,
2203                                               XmNrowColumnType, XmMENU_BAR,
2204                                               NULL);
2205
2206    // create shell for help-cascade
2207    help_popup = XtVaCreatePopupShell("menu_shell", xmMenuShellWidgetClass,
2208                                      p_w->menu_bar[0],
2209                                      XmNwidth, 1,
2210                                      XmNheight, 1,
2211                                      XmNallowShellResize, true,
2212                                      XmNoverrideRedirect, true,
2213                                      NULL);
2214
2215    // create row column in Pull-Down shell
2216    p_w->help_pull_down = XtVaCreateWidget("menu_row_column",
2217                                           xmRowColumnWidgetClass, help_popup,
2218                                           XmNrowColumnType, XmMENU_PULLDOWN,
2219                                           NULL);
2220
2221    // create HELP-label in menu bar
2222    help_label = XtVaCreateManagedWidget("menu1_top_b1",
2223                                         xmCascadeButtonWidgetClass, p_w->menu_bar[0],
2224                                         RES_CONVERT(XmNlabelString, help_button),
2225                                         RES_CONVERT(XmNmnemonic, help_mnemonic),
2226                                         XmNsubMenuId, p_w->help_pull_down, NULL);
2227    XtVaSetValues(p_w->menu_bar[0], XmNmenuHelpWidget, help_label, NULL);
2228    root->make_sensitive(help_label, AWM_ALL);
2229
2230    form1 = XtVaCreateManagedWidget("form1",
2231                                    xmFormWidgetClass,
2232                                    main_window,
2233                                    // XmNwidth, width,
2234                                    // XmNheight, height,
2235                                    XmNresizePolicy, XmRESIZE_NONE,
2236                                    // XmNx, 0,
2237                                    // XmNy, 0,
2238                                    NULL);
2239
2240    p_w->mode_area = XtVaCreateManagedWidget("mode area",
2241                                             xmDrawingAreaWidgetClass,
2242                                             form1,
2243                                             XmNresizePolicy, XmRESIZE_NONE,
2244                                             XmNwidth, 17,
2245                                             XmNheight, height,
2246                                             XmNx, 0,
2247                                             XmNy, 0,
2248                                             XmNleftOffset, 0,
2249                                             XmNtopOffset, 0,
2250                                             XmNbottomAttachment, XmATTACH_FORM,
2251                                             XmNleftAttachment, XmATTACH_POSITION,
2252                                             XmNtopAttachment, XmATTACH_POSITION,
2253                                             XmNmarginHeight, 2,
2254                                             XmNmarginWidth, 1,
2255                                             NULL);
2256
2257    separator = p_w->mode_area;
2258
2259    form2 = XtVaCreateManagedWidget("form2",
2260                                    xmFormWidgetClass,
2261                                    form1,
2262                                    XmNwidth, width,
2263                                    XmNheight, height,
2264                                    XmNtopOffset, 0,
2265                                    XmNbottomOffset, 0,
2266                                    XmNleftOffset, 0,
2267                                    XmNrightOffset, 0,
2268                                    XmNrightAttachment, XmATTACH_FORM,
2269                                    XmNbottomAttachment, XmATTACH_FORM,
2270                                    XmNleftAttachment, XmATTACH_WIDGET,
2271                                    XmNleftWidget, separator,
2272                                    XmNtopAttachment, XmATTACH_POSITION,
2273                                    XmNresizePolicy, XmRESIZE_NONE,
2274                                    XmNx, 0,
2275                                    XmNy, 0,
2276                                    NULL);
2277    p_w->areas[AW_INFO_AREA] =
2278        new AW_area_management(root, form2, XtVaCreateManagedWidget("info_area",
2279                                                                    xmDrawingAreaWidgetClass,
2280                                                                    form2,
2281                                                                    XmNheight, 0,
2282                                                                    XmNbottomAttachment, XmATTACH_NONE,
2283                                                                    XmNtopAttachment, XmATTACH_FORM,
2284                                                                    XmNleftAttachment, XmATTACH_FORM,
2285                                                                    XmNrightAttachment, XmATTACH_FORM,
2286                                                                    XmNmarginHeight, 2,
2287                                                                    XmNmarginWidth, 2,
2288                                                                    NULL));
2289
2290    p_w->areas[AW_BOTTOM_AREA] =
2291        new AW_area_management(root, form2, XtVaCreateManagedWidget("bottom_area",
2292                                                                    xmDrawingAreaWidgetClass,
2293                                                                    form2,
2294                                                                    XmNheight, 0,
2295                                                                    XmNbottomAttachment, XmATTACH_FORM,
2296                                                                    XmNtopAttachment, XmATTACH_NONE,
2297                                                                    XmNleftAttachment, XmATTACH_FORM,
2298                                                                    XmNrightAttachment, XmATTACH_FORM,
2299                                                                    NULL));
2300
2301    p_w->scroll_bar_horizontal = XtVaCreateManagedWidget("scroll_bar_horizontal",
2302                                                         xmScrollBarWidgetClass,
2303                                                         form2,
2304                                                         XmNheight, 15,
2305                                                         XmNminimum, 0,
2306                                                         XmNmaximum, AW_SCROLL_MAX,
2307                                                         XmNincrement, 10,
2308                                                         XmNsliderSize, AW_SCROLL_MAX,
2309                                                         XmNrightAttachment, XmATTACH_FORM,
2310                                                         XmNbottomAttachment, XmATTACH_FORM,
2311                                                         XmNbottomOffset, 0,
2312                                                         XmNleftAttachment, XmATTACH_FORM,
2313                                                         XmNtopAttachment, XmATTACH_NONE,
2314                                                         XmNorientation, XmHORIZONTAL,
2315                                                         XmNrightOffset, 18,
2316                                                         NULL);
2317
2318    p_w->scroll_bar_vertical = XtVaCreateManagedWidget("scroll_bar_vertical",
2319                                                       xmScrollBarWidgetClass,
2320                                                       form2,
2321                                                       XmNwidth, 15,
2322                                                       XmNminimum, 0,
2323                                                       XmNmaximum, AW_SCROLL_MAX,
2324                                                       XmNincrement, 10,
2325                                                       XmNsliderSize, AW_SCROLL_MAX,
2326                                                       XmNrightAttachment, XmATTACH_FORM,
2327                                                       XmNbottomAttachment, XmATTACH_WIDGET,
2328                                                       XmNbottomWidget, p_w->scroll_bar_horizontal,
2329                                                       XmNbottomOffset, 3,
2330                                                       XmNleftOffset, 3,
2331                                                       XmNrightOffset, 3,
2332                                                       XmNleftAttachment, XmATTACH_NONE,
2333                                                       XmNtopAttachment, XmATTACH_WIDGET,
2334                                                       XmNtopWidget, INFO_WIDGET,
2335                                                       NULL);
2336
2337    p_w->frame = XtVaCreateManagedWidget("draw_area",
2338                                         xmFrameWidgetClass,
2339                                         form2,
2340                                         XmNshadowType, XmSHADOW_IN,
2341                                         XmNshadowThickness, 2,
2342                                         XmNleftOffset, 3,
2343                                         XmNtopOffset, 3,
2344                                         XmNbottomOffset, 3,
2345                                         XmNrightOffset, 3,
2346                                         XmNbottomAttachment, XmATTACH_WIDGET,
2347                                         XmNbottomWidget, p_w->scroll_bar_horizontal,
2348                                         XmNtopAttachment, XmATTACH_FORM,
2349                                         XmNtopOffset, 0,
2350                                         XmNleftAttachment, XmATTACH_FORM,
2351                                         XmNrightAttachment, XmATTACH_WIDGET,
2352                                         XmNrightWidget, p_w->scroll_bar_vertical,
2353                                         NULL);
2354
2355    p_w->areas[AW_MIDDLE_AREA] =
2356        new AW_area_management(root, p_w->frame, XtVaCreateManagedWidget("draw area",
2357                                                                         xmDrawingAreaWidgetClass,
2358                                                                         p_w->frame,
2359                                                                         XmNmarginHeight, 0,
2360                                                                         XmNmarginWidth, 0,
2361                                                                         NULL));
2362
2363    XmMainWindowSetAreas(main_window, p_w->menu_bar[0], (Widget) NULL,
2364                         (Widget) NULL, (Widget) NULL, form1);
2365
2366    aw_realize_widget(this);
2367
2368    create_devices();
2369    aw_create_help_entry(this);
2370    create_window_variables();
2371}
2372
2373void AW_window_simple::init(AW_root *root_in, const char *wid, const char *windowname) {
2374    root = root_in; // for macro
2375
2376    int width  = 100;                               // this is only the minimum size!
2377    int height = 100;
2378    int posx   = 50;
2379    int posy   = 50;
2380
2381    window_name = strdup(windowname);
2382    window_defaults_name = GBS_string_2_key(wid);
2383
2384    p_w->shell = aw_create_shell(this, true, true, width, height, posx, posy);
2385
2386    // add this to disable resize or maximize in simple dialogs (avoids broken layouts)
2387    // XtVaSetValues(p_w->shell, XmNmwmFunctions, MWM_FUNC_MOVE | MWM_FUNC_CLOSE, NULL);
2388
2389    Widget form1 = XtVaCreateManagedWidget("forms", xmFormWidgetClass,
2390            p_w->shell,
2391            NULL);
2392
2393    p_w->areas[AW_INFO_AREA] = new AW_area_management(root, form1, XtVaCreateManagedWidget("info_area",
2394                    xmDrawingAreaWidgetClass,
2395                    form1,
2396                    XmNbottomAttachment, XmATTACH_FORM,
2397                    XmNtopAttachment, XmATTACH_FORM,
2398                    XmNleftAttachment, XmATTACH_FORM,
2399                    XmNrightAttachment, XmATTACH_FORM,
2400                    XmNmarginHeight, 2,
2401                    XmNmarginWidth, 2,
2402                    NULL));
2403
2404    aw_realize_widget(this);
2405    create_devices();
2406}
2407
2408void AW_window_simple_menu::init(AW_root *root_in, const char *wid, const char *windowname) { 
2409    root = root_in; // for macro
2410
2411    const char *help_button = "HELP";
2412    const char *help_mnemonic = "H";
2413    window_name = strdup(windowname);
2414    window_defaults_name = GBS_string_2_key(wid);
2415
2416    int width = 100;
2417    int height = 100;
2418    int posx = 50;
2419    int posy = 50;
2420
2421    p_w->shell = aw_create_shell(this, true, true, width, height, posx, posy);
2422
2423    Widget main_window;
2424    Widget help_popup;
2425    Widget help_label;
2426    Widget form1;
2427
2428    main_window = XtVaCreateManagedWidget("mainWindow1",
2429                                          xmMainWindowWidgetClass, p_w->shell,
2430                                          NULL);
2431
2432    p_w->menu_bar[0] = XtVaCreateManagedWidget("menu1", xmRowColumnWidgetClass,
2433                                               main_window,
2434                                               XmNrowColumnType, XmMENU_BAR,
2435                                               NULL);
2436
2437    // create shell for help-cascade
2438    help_popup = XtVaCreatePopupShell("menu_shell", xmMenuShellWidgetClass,
2439                                      p_w->menu_bar[0],
2440                                      XmNwidth, 1,
2441                                      XmNheight, 1,
2442                                      XmNallowShellResize, true,
2443                                      XmNoverrideRedirect, true,
2444                                      NULL);
2445
2446    // create row column in Pull-Down shell
2447    p_w->help_pull_down = XtVaCreateWidget("menu_row_column",
2448                                           xmRowColumnWidgetClass, help_popup,
2449                                           XmNrowColumnType, XmMENU_PULLDOWN,
2450                                           NULL);
2451
2452    // create HELP-label in menu bar
2453    help_label = XtVaCreateManagedWidget("menu1_top_b1",
2454                                         xmCascadeButtonWidgetClass, p_w->menu_bar[0],
2455                                         RES_CONVERT(XmNlabelString, help_button),
2456                                         RES_CONVERT(XmNmnemonic, help_mnemonic),
2457                                         XmNsubMenuId, p_w->help_pull_down, NULL);
2458    XtVaSetValues(p_w->menu_bar[0], XmNmenuHelpWidget, help_label, NULL);
2459    root->make_sensitive(help_label, AWM_ALL);
2460
2461    form1 = XtVaCreateManagedWidget("form1",
2462                                    xmFormWidgetClass,
2463                                    main_window,
2464                                    XmNtopOffset, 10,
2465                                    XmNresizePolicy, XmRESIZE_NONE,
2466                                    NULL);
2467
2468    p_w->areas[AW_INFO_AREA] =
2469        new AW_area_management(root, form1, XtVaCreateManagedWidget("info_area",
2470                                                                    xmDrawingAreaWidgetClass,
2471                                                                    form1,
2472                                                                    XmNbottomAttachment, XmATTACH_FORM,
2473                                                                    XmNtopAttachment, XmATTACH_FORM,
2474                                                                    XmNleftAttachment, XmATTACH_FORM,
2475                                                                    XmNrightAttachment, XmATTACH_FORM,
2476                                                                    XmNmarginHeight, 2,
2477                                                                    XmNmarginWidth, 2,
2478                                                                    NULL));
2479
2480    aw_realize_widget(this);
2481
2482    aw_create_help_entry(this);
2483    create_devices();
2484}
2485
2486void AW_window_message::init(AW_root *root_in, const char *windowname, bool allow_close) {
2487    root = root_in; // for macro
2488
2489    int width  = 100;
2490    int height = 100;
2491    int posx   = 50;
2492    int posy   = 50;
2493
2494    window_name = strdup(windowname);
2495    window_defaults_name = GBS_string_2_key(window_name);
2496
2497    // create shell for message box
2498    p_w->shell = aw_create_shell(this, true, allow_close, width, height, posx, posy);
2499
2500    // disable resize or maximize in simple dialogs (avoids broken layouts)
2501    XtVaSetValues(p_w->shell, XmNmwmFunctions, MWM_FUNC_MOVE | MWM_FUNC_CLOSE,
2502            NULL);
2503
2504    p_w->areas[AW_INFO_AREA] = new AW_area_management(root, p_w->shell, XtVaCreateManagedWidget("info_area",
2505                    xmDrawingAreaWidgetClass,
2506                    p_w->shell,
2507                    XmNheight, 0,
2508                    XmNbottomAttachment, XmATTACH_NONE,
2509                    XmNtopAttachment, XmATTACH_FORM,
2510                    XmNleftAttachment, XmATTACH_FORM,
2511                    XmNrightAttachment, XmATTACH_FORM,
2512                    NULL));
2513
2514    aw_realize_widget(this);
2515}
2516
2517void AW_window::set_info_area_height(int height) {
2518    XtVaSetValues(INFO_WIDGET, XmNheight, height, NULL);
2519    XtVaSetValues(p_w->frame, XmNtopOffset, height, NULL);
2520}
2521
2522void AW_window::set_bottom_area_height(int height) {
2523    XtVaSetValues(BOTTOM_WIDGET, XmNheight, height, NULL);
2524    XtVaSetValues(p_w->scroll_bar_horizontal, XmNbottomOffset, (int)height, NULL);
2525}
2526
2527void AW_window::set_vertical_scrollbar_top_indent(int indent) {
2528    XtVaSetValues(p_w->scroll_bar_vertical, XmNtopOffset, (int)indent, NULL);
2529    top_indent_of_vertical_scrollbar = indent;
2530}
2531
2532void AW_window::set_vertical_scrollbar_bottom_indent(int indent) {
2533    XtVaSetValues(p_w->scroll_bar_vertical, XmNbottomOffset, (int)(3+indent), NULL);
2534    bottom_indent_of_vertical_scrollbar = indent;
2535}
2536
2537void AW_root::apply_sensitivity(AW_active mask) {
2538    aw_assert(legal_mask(mask));
2539    AW_buttons_struct *list;
2540
2541    global_mask = mask;
2542    for (list = button_sens_list; list; list = list->next) {
2543        XtSetSensitive(list->button, (list->mask & mask) ? True : False);
2544    }
2545}
2546
2547void AW_window::set_focus_policy(bool follow_mouse) {
2548    int focusPolicy = follow_mouse ? XmPOINTER : XmEXPLICIT;
2549    XtVaSetValues(p_w->shell, XmNkeyboardFocusPolicy, focusPolicy, NULL);
2550}
2551
2552static long set_focus_policy(const char *, long cl_aww, void *) {
2553    AW_window *aww = (AW_window*)cl_aww;
2554    aww->set_focus_policy(aww->get_root()->focus_follows_mouse);
2555    return cl_aww;
2556}
2557void AW_root::apply_focus_policy(bool follow_mouse) {
2558    focus_follows_mouse = follow_mouse;
2559    GBS_hash_do_loop(hash_for_windows, set_focus_policy, 0);
2560}
2561
2562void AW_window::select_mode(int mode) {
2563    if (mode >= p_w->number_of_modes)
2564        return;
2565
2566    Widget oldwidget = p_w->modes_widgets[p_w->selected_mode];
2567    p_w->selected_mode = mode;
2568    Widget widget = p_w->modes_widgets[p_w->selected_mode];
2569    XtVaSetValues(oldwidget, XmNbackground, p_global->background, NULL);
2570    XtVaSetValues(widget, XmNbackground, p_global->foreground, NULL);
2571}
2572
2573static void aw_mode_callback(AW_window *aww, long mode, AW_cb_struct *cbs) {
2574    aww->select_mode((int)mode);
2575    cbs->run_callback();
2576}
2577
2578#define MODE_BUTTON_OFFSET 34
2579inline int yoffset_for_mode_button(int button_number) {
2580    return button_number*MODE_BUTTON_OFFSET + (button_number/4)*8 + 2;
2581}
2582
2583int AW_window::create_mode(const char *pixmap, const char *helpText, AW_active mask, void (*f)(AW_window*, AW_CL, AW_CL), AW_CL cd1, AW_CL cd2) {
2584    aw_assert(legal_mask(mask));
2585    Widget button;
2586
2587    TuneBackground(p_w->mode_area, TUNE_BUTTON); // set background color for mode-buttons
2588
2589    const char *path = GB_path_in_ARBLIB("pixmaps", pixmap);
2590
2591    int y = yoffset_for_mode_button(p_w->number_of_modes);
2592    button = XtVaCreateManagedWidget("", xmPushButtonWidgetClass, p_w->mode_area,
2593                                     XmNx,               0,
2594                                     XmNy,               y,
2595                                     XmNlabelType,       XmPIXMAP,
2596                                     XmNshadowThickness, 1,
2597                                     XmNbackground,      _at->background_color,
2598                                     NULL);
2599    XtVaSetValues(button, RES_CONVERT(XmNlabelPixmap, path), NULL);
2600    XtVaGetValues(button, XmNforeground, &p_global->foreground, NULL);
2601
2602    AW_cb_struct *cbs = new AW_cb_struct(this, f, cd1, cd2, 0);
2603    AW_cb_struct *cb2 = new AW_cb_struct(this, (AW_CB)aw_mode_callback, (AW_CL)p_w->number_of_modes, (AW_CL)cbs, helpText, cbs);
2604    XtAddCallback(button, XmNactivateCallback,
2605    (XtCallbackProc) AW_server_callback,
2606    (XtPointer) cb2);
2607
2608    if (!p_w->modes_f_callbacks) {
2609        p_w->modes_f_callbacks = (AW_cb_struct **)GB_calloc(sizeof(AW_cb_struct*), AW_NUMBER_OF_F_KEYS); // valgrinders : never freed because AW_window never is freed
2610    }
2611    if (!p_w->modes_widgets) {
2612        p_w->modes_widgets = (Widget *)GB_calloc(sizeof(Widget), AW_NUMBER_OF_F_KEYS);
2613    }
2614    if (p_w->number_of_modes<AW_NUMBER_OF_F_KEYS) {
2615        p_w->modes_f_callbacks[p_w->number_of_modes] = cb2;
2616        p_w->modes_widgets[p_w->number_of_modes] = button;
2617    }
2618
2619    root->make_sensitive(button, mask);
2620    p_w->number_of_modes++;
2621
2622    int ynext = yoffset_for_mode_button(p_w->number_of_modes);
2623    if (ynext> _at->max_y_size) _at->max_y_size = ynext;
2624
2625    return p_w->number_of_modes;
2626}
2627
2628// ------------------------
2629//      Hotkey Checking
2630// ------------------------
2631
2632#ifdef DEBUG
2633
2634#define MAX_DEEP_TO_TEST       10
2635#define MAX_MENU_ITEMS_TO_TEST 50
2636
2637static char *TD_menu_name = 0;
2638static char  TD_mnemonics[MAX_DEEP_TO_TEST][MAX_MENU_ITEMS_TO_TEST];
2639static int   TD_topics[MAX_DEEP_TO_TEST];
2640
2641struct SearchPossibilities : virtual Noncopyable {
2642    char *menu_topic;
2643    SearchPossibilities *next;
2644
2645    SearchPossibilities(const char* menu_topic_, SearchPossibilities *next_) {
2646        menu_topic = strdup(menu_topic_);
2647        next = next_;
2648    }
2649
2650    ~SearchPossibilities() {
2651        free(menu_topic);
2652        delete next;
2653    }
2654};
2655
2656typedef SearchPossibilities *SearchPossibilitiesPtr;
2657static SearchPossibilitiesPtr TD_poss[MAX_DEEP_TO_TEST] = { 0 };
2658
2659inline void addToPoss(int menu_deep, const char *topic_name) {
2660    TD_poss[menu_deep] = new SearchPossibilities(topic_name, TD_poss[menu_deep]);
2661}
2662
2663inline char oppositeCase(char c) {
2664    return isupper(c) ? tolower(c) : toupper(c);
2665}
2666
2667static void strcpy_overlapping(char *dest, char *src) {
2668    int src_len = strlen(src);
2669    memmove(dest, src, src_len+1);
2670}
2671
2672static const char *possible_mnemonics(int menu_deep, const char *topic_name) {
2673    int t;
2674    static char *unused;
2675
2676    freedup(unused, topic_name);
2677
2678    for (t = 0; unused[t]; ++t) {
2679        bool remove = false;
2680        if (!isalnum(unused[t])) { // remove useless chars
2681            remove = true;
2682        }
2683        else {
2684            char *dup = strchr(unused, unused[t]);
2685            if (dup && (dup-unused)<t) { // remove duplicated chars
2686                remove = true;
2687            }
2688            else {
2689                dup = strchr(unused, oppositeCase(unused[t]));
2690                if (dup && (dup-unused)<t) { // char is duplicated with opposite case
2691                    dup[0] = toupper(dup[0]); // prefer upper case
2692                    remove = true;
2693                }
2694            }
2695        }
2696        if (remove) {
2697            strcpy_overlapping(unused+t, unused+t+1);
2698            --t;
2699        }
2700    }
2701
2702    int topics = TD_topics[menu_deep];
2703    for (t = 0; t<topics; ++t) {
2704        char c = TD_mnemonics[menu_deep][t]; // upper case!
2705        char *u = strchr(unused, c);
2706        if (u)
2707            strcpy_overlapping(u, u+1); // remove char
2708        u = strchr(unused, tolower(c));
2709        if (u)
2710            strcpy_overlapping(u, u+1); // remove char
2711    }
2712
2713    return unused;
2714}
2715
2716static void printPossibilities(int menu_deep) {
2717    SearchPossibilities *sp = TD_poss[menu_deep];
2718    while (sp) {
2719        const char *poss = possible_mnemonics(menu_deep, sp->menu_topic);
2720        fprintf(stderr, "          - Possibilities for '%s': '%s'\n", sp->menu_topic, poss);
2721
2722        sp = sp->next;
2723    }
2724
2725    delete TD_poss[menu_deep];
2726    TD_poss[menu_deep] = 0;
2727}
2728
2729static int menu_deep_check = 0;
2730
2731static void test_duplicate_mnemonics(int menu_deep, const char *topic_name, const char *mnemonic) {
2732    if (mnemonic && mnemonic[0] != 0) {
2733        if (mnemonic[1]) { // longer than 1 char -> wrong
2734            fprintf(stderr, "Warning: Hotkey '%s' is too long; only 1 character allowed (%s|%s)\n", mnemonic, TD_menu_name, topic_name);
2735        }
2736        if (topic_name[0] == '#') { // graphical menu
2737            if (mnemonic[0]) {
2738                fprintf(stderr, "Warning: Hotkey '%s' is useless for graphical menu entry (%s|%s)\n", mnemonic, TD_menu_name, topic_name);
2739            }
2740        }
2741        else {
2742            if (strchr(topic_name, mnemonic[0])) {  // occurs in menu text
2743                int topics = TD_topics[menu_deep];
2744                int t;
2745                char hotkey = toupper(mnemonic[0]); // store hotkeys case-less (case does not matter when pressing the hotkey)
2746
2747                TD_mnemonics[menu_deep][topics] = hotkey;
2748
2749                for (t=0; t<topics; t++) {
2750                    if (TD_mnemonics[menu_deep][t]==hotkey) {
2751                        fprintf(stderr, "Warning: Hotkey '%c' used twice (%s|%s)\n", hotkey, TD_menu_name, topic_name);
2752                        addToPoss(menu_deep, topic_name);
2753                        break;
2754                    }
2755                }
2756
2757                TD_topics[menu_deep] = topics+1;
2758            }
2759            else {
2760                fprintf(stderr, "Warning: Hotkey '%c' is useless; does not occur in text (%s|%s)\n", mnemonic[0], TD_menu_name, topic_name);
2761                addToPoss(menu_deep, topic_name);
2762            }
2763        }
2764    }
2765#if defined(DEVEL_RALF)
2766    else {
2767        if (topic_name[0] != '#') { // not a graphical menu
2768            fprintf(stderr, "Warning: Missing hotkey for (%s|%s)\n", TD_menu_name, topic_name);
2769            addToPoss(menu_deep, topic_name);
2770        }
2771    }
2772#endif // DEVEL_RALF
2773}
2774
2775static void open_test_duplicate_mnemonics(int menu_deep, const char *sub_menu_name, const char *mnemonic) {
2776    aw_assert(menu_deep == menu_deep_check+1);
2777    menu_deep_check = menu_deep;
2778
2779    int len = strlen(TD_menu_name)+1+strlen(sub_menu_name)+1;
2780    char *buf = (char*)malloc(len);
2781
2782    memset(buf, 0, len);
2783    sprintf(buf, "%s|%s", TD_menu_name, sub_menu_name);
2784
2785    test_duplicate_mnemonics(menu_deep-1, sub_menu_name, mnemonic);
2786
2787    freeset(TD_menu_name, buf);
2788    TD_poss[menu_deep] = 0;
2789}
2790
2791static void close_test_duplicate_mnemonics(int menu_deep) {
2792    aw_assert(menu_deep == menu_deep_check);
2793    menu_deep_check = menu_deep-1;
2794
2795    printPossibilities(menu_deep);
2796    TD_topics[menu_deep] = 0;
2797
2798    aw_assert(TD_menu_name);
2799    // otherwise no menu was opened
2800
2801    char *slash = strrchr(TD_menu_name, '|');
2802    if (slash) {
2803        slash[0] = 0;
2804    }
2805    else {
2806        TD_menu_name[0] = 0;
2807    }
2808}
2809
2810static void init_duplicate_mnemonic() {
2811    int i;
2812
2813    if (TD_menu_name) close_test_duplicate_mnemonics(1); // close last menu
2814    freedup(TD_menu_name, "");
2815
2816    for (i=0; i<MAX_DEEP_TO_TEST; i++) {
2817        TD_topics[i] = 0;
2818    }
2819    aw_assert(menu_deep_check == 0);
2820}
2821static void exit_duplicate_mnemonic() {
2822    close_test_duplicate_mnemonics(1); // close last menu
2823    aw_assert(TD_menu_name);
2824    freenull(TD_menu_name);
2825    aw_assert(menu_deep_check == 0);
2826}
2827#endif
2828
2829// --------------------------------------------------------------------------------
2830
2831void AW_window::create_menu(AW_label name, const char *mnemonic, AW_active mask) {
2832    aw_assert(legal_mask(mask));
2833    p_w->menu_deep = 0;
2834#ifdef DEBUG
2835    init_duplicate_mnemonic();
2836#endif
2837#if defined(DUMP_MENU_LIST)
2838    dumpCloseAllSubMenus();
2839#endif // DUMP_MENU_LIST
2840    insert_sub_menu(name, mnemonic, mask);
2841}
2842
2843void AW_window::all_menus_created() const { // this is called by AW_window::show() (i.e. after all menus have been created)
2844#if defined(DEBUG)
2845    if (p_w->menu_deep>0) { // window had menu
2846        aw_assert(p_w->menu_deep == 1);
2847        // some unclosed sub-menus ?
2848        if (menu_deep_check == 1) { // otherwise the window is just re-shown (already has been checked!)
2849            exit_duplicate_mnemonic();
2850        }
2851    }
2852#endif // DEBUG
2853}
2854
2855void AW_window::insert_sub_menu(AW_label name, const char *mnemonic, AW_active mask) {
2856    aw_assert(legal_mask(mask));
2857    Widget shell, Label;
2858
2859    TuneBackground(p_w->menu_bar[p_w->menu_deep], TUNE_SUBMENU); // set background color for submenus
2860    // (Note: This must even be called if TUNE_SUBMENU is 0!
2861    //        Otherwise several submenus get the TUNE_MENUTOPIC color)
2862
2863#if defined(DUMP_MENU_LIST)
2864    dumpOpenSubMenu(name);
2865#endif // DUMP_MENU_LIST
2866#ifdef DEBUG
2867    open_test_duplicate_mnemonics(p_w->menu_deep+1, name, mnemonic);
2868#endif
2869
2870    // create shell for Pull-Down
2871    shell = XtVaCreatePopupShell("menu_shell", xmMenuShellWidgetClass,
2872            p_w->menu_bar[p_w->menu_deep],
2873            XmNwidth, 1,
2874            XmNheight, 1,
2875            XmNallowShellResize, true,
2876            XmNoverrideRedirect, true,
2877            NULL);
2878
2879    // create row column in Pull-Down shell
2880
2881    p_w->menu_bar[p_w->menu_deep+1] = XtVaCreateWidget("menu_row_column",
2882            xmRowColumnWidgetClass, shell,
2883            XmNrowColumnType, XmMENU_PULLDOWN,
2884            XmNtearOffModel, XmTEAR_OFF_ENABLED,
2885            NULL);
2886
2887    // create label in menu bar
2888    if (mnemonic && *mnemonic && strchr(name, mnemonic[0])) {
2889        // if mnemonic is "" -> Cannot convert string "" to type KeySym
2890        Label = XtVaCreateManagedWidget("menu1_top_b1",
2891                xmCascadeButtonWidgetClass, p_w->menu_bar[p_w->menu_deep],
2892                RES_CONVERT(XmNlabelString, name),
2893                                         RES_CONVERT(XmNmnemonic, mnemonic),
2894                                         XmNsubMenuId, p_w->menu_bar[p_w->menu_deep+1],
2895                                         XmNbackground, _at->background_color, NULL);
2896    }
2897    else {
2898        Label = XtVaCreateManagedWidget("menu1_top_b1",
2899        xmCascadeButtonWidgetClass,
2900        p_w->menu_bar[p_w->menu_deep],
2901        RES_CONVERT(XmNlabelString, name),
2902        XmNsubMenuId, p_w->menu_bar[p_w->menu_deep+1],
2903        XmNbackground, _at->background_color,
2904        NULL);
2905    }
2906
2907    if (p_w->menu_deep < AW_MAX_MENU_DEEP-1) p_w->menu_deep++;
2908
2909    root->make_sensitive(Label, mask);
2910}
2911
2912void AW_window::close_sub_menu() {
2913#ifdef DEBUG
2914    close_test_duplicate_mnemonics(p_w->menu_deep);
2915#endif
2916#if defined(DUMP_MENU_LIST)
2917    dumpCloseSubMenu();
2918#endif // DUMP_MENU_LIST
2919    if (p_w->menu_deep>0)
2920        p_w->menu_deep--;
2921}
2922
2923void AW_window::insert_menu_topic(const char *topic_id, AW_label name,
2924                                  const char *mnemonic, const char *helpText, AW_active mask,
2925                                  void (*f)(AW_window*, AW_CL, AW_CL), AW_CL cd1, AW_CL cd2)
2926{
2927    aw_assert(legal_mask(mask));
2928    Widget button;
2929
2930    if (!topic_id) topic_id = name; // hmm, due to this we cannot insert_menu_topic w/o id. Change? @@@
2931
2932    TuneBackground(p_w->menu_bar[p_w->menu_deep], TUNE_MENUTOPIC); // set background color for normal menu topics
2933
2934#if defined(DUMP_MENU_LIST)
2935    dumpMenuEntry(name);
2936#endif // DUMP_MENU_LIST
2937#ifdef DEBUG
2938    test_duplicate_mnemonics(p_w->menu_deep, name, mnemonic);
2939#endif
2940    if (mnemonic && *mnemonic && strchr(name, mnemonic[0])) {
2941        // create one sub-menu-point
2942        button = XtVaCreateManagedWidget("", xmPushButtonWidgetClass,
2943                p_w->menu_bar[p_w->menu_deep],
2944                RES_LABEL_CONVERT(name),
2945                                          RES_CONVERT(XmNmnemonic, mnemonic),
2946                                          XmNbackground, _at->background_color, NULL);
2947    }
2948    else {
2949        button = XtVaCreateManagedWidget("",
2950        xmPushButtonWidgetClass,
2951        p_w->menu_bar[p_w->menu_deep],
2952        RES_LABEL_CONVERT(name),
2953        XmNbackground, _at->background_color,
2954        NULL);
2955    }
2956
2957    AW_label_in_awar_list(this, button, name);
2958    AW_cb_struct *cbs = new AW_cb_struct(this, f, cd1, cd2, helpText);
2959    XtAddCallback(button, XmNactivateCallback,
2960                  (XtCallbackProc) AW_server_callback,
2961                  (XtPointer) cbs);
2962
2963    cbs->id = strdup(topic_id);
2964    root->define_remote_command(cbs);
2965    root->make_sensitive(button, mask);
2966}
2967
2968void AW_window::insert_help_topic(AW_label name, const char *mnemonic, const char *helpText, AW_active mask,
2969                                  void (*f)(AW_window*, AW_CL,  AW_CL), AW_CL cd1, AW_CL cd2)
2970{
2971    aw_assert(legal_mask(mask));
2972    Widget button;
2973
2974    // create one help-sub-menu-point
2975    button = XtVaCreateManagedWidget("", xmPushButtonWidgetClass,
2976            p_w->help_pull_down,
2977            RES_CONVERT(XmNlabelString, name),
2978                                      RES_CONVERT(XmNmnemonic, mnemonic), NULL);
2979    XtAddCallback(button, XmNactivateCallback,
2980    (XtCallbackProc) AW_server_callback,
2981    (XtPointer) new AW_cb_struct(this, f, cd1, cd2, helpText));
2982
2983    root->make_sensitive(button, mask);
2984}
2985
2986void AW_window::sep______________() {
2987    // create one help-sub-menu-point
2988    XtVaCreateManagedWidget("", xmSeparatorWidgetClass,
2989                            p_w->menu_bar[p_w->menu_deep],
2990                            NULL);
2991}
2992
2993AW_area_management::AW_area_management(AW_root *awr, Widget formi, Widget widget) {
2994    memset((char *)this, 0, sizeof(AW_area_management));
2995    form = formi;
2996    area = widget;
2997    XtAddEventHandler(area, EnterWindowMask, FALSE, AW_root_focusCB, (XtPointer)awr);
2998}
2999
3000AW_device_Xm *AW_area_management::get_screen_device() {
3001    if (!device) device = new AW_device_Xm(common);
3002    return device;
3003}
3004
3005AW_device_size *AW_area_management::get_size_device() {
3006    if (!size_device) size_device = new AW_device_size(common);
3007    return size_device;
3008}
3009
3010AW_device_print *AW_area_management::get_print_device() {
3011    if (!print_device) print_device = new AW_device_print(common);
3012    return print_device;
3013}
3014
3015AW_device_click *AW_area_management::get_click_device() {
3016    if (!click_device) click_device = new AW_device_click(common);
3017    return click_device;
3018}
3019
3020AW_device *AW_window::get_device(AW_area area) { // @@@ rename to get_screen_device
3021    AW_area_management *aram   = MAP_ARAM(area);
3022    AW_device_Xm       *device = NULL;
3023
3024    if (aram) device = aram->get_screen_device();
3025    return device;
3026}
3027
3028AW_device_size *AW_window::get_size_device(AW_area area) {
3029    AW_area_management *aram        = MAP_ARAM(area);
3030    AW_device_size     *size_device = NULL;
3031
3032    if (aram) {
3033        size_device = aram->get_size_device();
3034        size_device->clear();
3035        size_device->reset(); // @@@ hm
3036    }
3037    return size_device;
3038}
3039
3040AW_device_print *AW_window::get_print_device(AW_area area) {
3041    AW_area_management *aram         = MAP_ARAM(area);
3042    AW_device_print    *print_device = NULL;
3043
3044    if (aram) print_device = aram->get_print_device();
3045    return print_device;
3046}
3047
3048AW_device_click *AW_window::get_click_device(AW_area area, int mousex, int mousey,
3049                                             AW_pos max_distance_linei, AW_pos max_distance_texti, AW_pos radi) {
3050    AW_area_management *aram         = MAP_ARAM(area);
3051    AW_device_click    *click_device = NULL;
3052
3053    if (aram) {
3054        click_device = aram->get_click_device();
3055        click_device->init(mousex, mousey, max_distance_linei,
3056                           max_distance_texti, radi, AW_ALL_DEVICES);
3057    }
3058    return click_device;
3059}
3060
3061void AW_window::wm_activate() {
3062    {
3063        Boolean iconic = False;
3064        XtVaGetValues(p_w->shell, XmNiconic, &iconic, NULL);
3065
3066        if (iconic == True) {
3067            XtVaSetValues(p_w->shell, XmNiconic, False, NULL);
3068
3069            XtMapWidget(p_w->shell);
3070            XRaiseWindow(XtDisplay(p_w->shell), XtWindow(p_w->shell));
3071        }
3072    }
3073
3074    {
3075        Display *xdpy            = XtDisplay(p_w->shell);
3076        Window   window          = XtWindow(p_w->shell);
3077        Atom     netactivewindow = XInternAtom(xdpy, "_NET_ACTIVE_WINDOW", False);
3078
3079        if (netactivewindow) {
3080
3081            XClientMessageEvent ce;
3082            ce.type         = ClientMessage;
3083            ce.display      = xdpy;
3084            ce.window       = window;
3085            ce.message_type = netactivewindow;
3086            ce.format       = 32;
3087            ce.data.l[0]    = 2;
3088            ce.data.l[1]    = None;
3089            ce.data.l[2]    = Above;
3090            ce.data.l[3]    = 0;
3091            ce.data.l[4]    = 0;
3092
3093#if defined(DEBUG)
3094            Status ret =
3095#endif // DEBUG
3096                XSendEvent(xdpy, XDefaultRootWindow(xdpy),
3097                           False,
3098                           SubstructureRedirectMask | SubstructureNotifyMask,
3099                           (XEvent *) &ce);
3100
3101#if defined(DEBUG)
3102            if (!ret) { fprintf(stderr, "Failed to send _NET_ACTIVE_WINDOW to WM (XSendEvent returns %i)\n", ret); }
3103#endif // DEBUG
3104            XSync(xdpy, False);
3105        }
3106#if defined(DEBUG)
3107        else {
3108            fputs("No such atom '_NET_ACTIVE_WINDOW'\n", stderr);
3109        }
3110#endif // DEBUG
3111    }
3112}
3113
3114void AW_window::show_internal(void *cl_grab) {
3115    XtGrabKind grab = *(XtGrabKind*)cl_grab;
3116
3117    if (!window_is_shown) {
3118        all_menus_created();
3119        get_root()->window_show();
3120        window_is_shown = true;
3121    }
3122
3123    if (recalc_size_at_show != AW_KEEP_SIZE) {
3124        if (recalc_size_at_show == AW_RESIZE_DEFAULT) {
3125            window_fit();
3126        }
3127        else {
3128            aw_assert(recalc_size_at_show == AW_RESIZE_USER);
3129            // check whether user size is too small and increase to minimum window size
3130
3131            int min_width, min_height;   get_window_size(min_width, min_height);
3132            int user_width, user_height; get_size_from_awars(user_width, user_height);
3133
3134            if (user_width <min_width)  user_width  = min_width;
3135            if (user_height<min_height) user_height = min_height;
3136
3137            set_window_size(user_width, user_height);
3138        }
3139        recalc_size_at_show = AW_KEEP_SIZE;
3140    }
3141
3142    {
3143        int  posx, posy;
3144        bool setPos = false;
3145
3146        switch (recalc_pos_at_show) {
3147            case AW_REPOS_TO_MOUSE_ONCE:
3148                recalc_pos_at_show = AW_KEEP_POS;
3149                // fallthrough
3150            case AW_REPOS_TO_MOUSE: {
3151                int mx, my; if (!get_mouse_pos(mx, my)) goto FALLBACK_CENTER;
3152                int width, height; get_window_size(width, height);
3153                int wx, wy; get_window_content_pos(wx, wy);
3154
3155                if (wx || wy) {
3156                    if (p_w->knows_WM_offset()) {
3157                        wx -= p_w->WM_left_offset;
3158                        wy -= p_w->WM_top_offset;
3159
3160                        width  += p_w->WM_left_offset;
3161                        height += p_w->WM_top_offset;
3162                    }
3163
3164                    int wx2 = wx+width-1;
3165                    int wy2 = wy+height-1;
3166
3167                    if (mx<wx) { setPos = true; wx = mx; } else if (mx>wx2) { setPos = true; wx = mx-width+1; }
3168                    if (my<wy) { setPos = true; wy = my; } else if (my>wy2) { setPos = true; wy = my-height+1; }
3169
3170                    posx = wx;
3171                    posy = wy;
3172                }
3173                else {
3174                    setPos = true;
3175                    posx   = mx-width/2;
3176                    posy   = my-height/2;
3177                }
3178
3179                if (posx<0) posx = 0;
3180                if (posy<0) posy = 0;
3181
3182                break;
3183            }
3184            case AW_REPOS_TO_CENTER: {
3185                  FALLBACK_CENTER :
3186                int width, height; get_window_size(width, height);
3187                int swidth, sheight; get_screen_size(swidth, sheight);
3188
3189                setPos = true;
3190                posx   = (swidth-width)/2;
3191                posy   = (sheight-height)/4;
3192                break;
3193            }
3194
3195            case AW_KEEP_POS:
3196                break;
3197        }
3198
3199        if (setPos) store_pos_in_awars(posx, posy);
3200        else get_pos_from_awars(posx, posy);
3201
3202        set_window_frame_pos(posx, posy); // always set pos
3203    }
3204
3205    XtPopup(p_w->shell, grab);
3206    if (!expose_callback_added) {
3207        set_expose_callback(AW_INFO_AREA, (AW_CB)aw_onExpose_calc_WM_offsets, 0, 0); // @@@ should be removed after it was called once
3208        expose_callback_added = true;
3209    }
3210}
3211
3212void AW_window::show() {
3213    XtGrabKind grab = XtGrabNone;
3214    show_internal(&grab);
3215}
3216
3217void AW_window::show_grabbed() {
3218    XtGrabKind grab = XtGrabExclusive;
3219    show_internal(&grab);
3220}
3221
3222void AW_window::hide() {
3223    if (window_is_shown) {
3224        aw_update_window_geometry_awars(this);
3225        if (hide_cb) hide_cb(this);
3226        get_root()->window_hide();
3227        window_is_shown = false;
3228    }
3229    XtPopdown(p_w->shell);
3230}
3231
3232bool AW_window::is_shown() const {
3233    // return true if window is shown ( = not invisible and already created)
3234    // Note: does return TRUE!, if window is only minimized by WM
3235    return window_is_shown;
3236}
3237
3238void AW_window::hide_or_notify(const char *error) {
3239    if (error) aw_message(error);
3240    else hide();
3241}
3242
3243void AW_root::window_show() {
3244    active_windows++;
3245}
3246
3247void AW_root::window_hide() {
3248    active_windows--;
3249    if (active_windows<0) {
3250        exit(0);
3251    }
3252}
3253
3254void AW_root::main_loop() {
3255    XtAppMainLoop(p_r->context);
3256}
3257
3258void AW_root::process_events() {
3259    XtAppProcessEvent(p_r->context, XtIMAll);
3260}
3261void AW_root::process_pending_events() {
3262    XtInputMask pending = XtAppPending(p_r->context);
3263    while (pending) {
3264        XtAppProcessEvent(p_r->context, pending);
3265        pending = XtAppPending(p_r->context);
3266    }
3267}
3268
3269AW_ProcessEventType AW_root::peek_key_event(AW_window * /* aww */) {
3270    //! Returns type if key event follows, else 0
3271
3272    XEvent xevent;
3273    Boolean result = XtAppPeekEvent(p_r->context, &xevent);
3274
3275    if (!result) return NO_EVENT;
3276    if ((xevent.type != KeyPress) && (xevent.type != KeyRelease)) return NO_EVENT;
3277    return (AW_ProcessEventType)xevent.type;
3278}
3279
3280static void timed_window_title_cb(AW_root* /*aw_root*/, AW_CL cd1, AW_CL cd2) {
3281    char *title = (char *)cd1;
3282    AW_window *aw = (AW_window *)cd2;
3283
3284    aw->number_of_timed_title_changes--;
3285    if (!aw->number_of_timed_title_changes) {
3286        aw->set_window_title_intern(title);
3287    }
3288
3289    delete title;
3290}
3291void AW_window::message(char *title, int ms) {
3292    char *old_title = NULL;
3293
3294    number_of_timed_title_changes++;
3295
3296    old_title = strdup(window_name);
3297
3298    XtVaSetValues(p_w->shell, XmNtitle, title, NULL);
3299
3300    get_root()->add_timed_callback(ms, timed_window_title_cb, (AW_CL)old_title,
3301            (AW_CL)this);
3302
3303}
3304
3305void AW_window::set_window_title_intern(char *title) {
3306    XtVaSetValues(p_w->shell, XmNtitle, title, NULL);
3307}
3308
3309void AW_window::set_window_title(const char *title) {
3310    XtVaSetValues(p_w->shell, XmNtitle, title, NULL);
3311    freedup(window_name, title);
3312}
3313
3314const char *AW_window::get_window_title() {
3315    char *title;
3316
3317    XtVaGetValues(p_w->shell, XmNtitle, &title, NULL);
3318
3319    return title;
3320}
3321
3322const char *AW_window::local_id(const char *id) const {
3323    static char *last_local_id = 0;
3324    freeset(last_local_id, GBS_global_string_copy("%s/%s", get_window_id(), id));
3325    return last_local_id;
3326}
3327
3328static void AW_xfigCB_info_area(AW_window *aww, AW_xfig *xfig) {
3329
3330    AW_device *device = aww->get_device(AW_INFO_AREA);
3331    device->reset();
3332    if (aww->get_root()->color_mode == 0) { // mono colr
3333        device->clear(-1);
3334    }
3335    device->set_offset(AW::Vector(-xfig->minx, -xfig->miny));
3336    xfig->print(device);
3337}
3338
3339void AW_window::load_xfig(const char *file, bool resize) {
3340    AW_xfig *xfig;
3341
3342    if (file)   xfig = new AW_xfig(file, get_root()->font_width, get_root()->font_height);
3343    else        xfig = new AW_xfig(get_root()->font_width, get_root()->font_height); // create an empty xfig
3344
3345    xfig_data = (void*)xfig;
3346
3347    set_expose_callback(AW_INFO_AREA, (AW_CB)AW_xfigCB_info_area, (AW_CL)xfig_data, 0);
3348    xfig->create_gcs(get_device(AW_INFO_AREA), get_root()->color_mode ? 8 : 1);
3349
3350    int xsize = xfig->maxx - xfig->minx;
3351    int ysize = xfig->maxy - xfig->miny;
3352
3353    if (xsize>_at->max_x_size) _at->max_x_size = xsize;
3354    if (ysize>_at->max_y_size) _at->max_y_size = ysize;
3355
3356    if (resize) {
3357        recalc_size_atShow(AW_RESIZE_ANY);
3358        set_window_size(_at->max_x_size+1000, _at->max_y_size+1000);
3359    }
3360}
3361
3362void AW_window::draw_line(int x1, int y1, int x2, int y2, int width, bool resize) {
3363    AW_xfig *xfig = (AW_xfig*)xfig_data;
3364    aw_assert(xfig);
3365    // forgot to call load_xfig ?
3366
3367    xfig->add_line(x1, y1, x2, y2, width);
3368
3369    class x {
3370public:
3371        static inline int max(int i1, int i2) {
3372            return i1>i2 ? i1 : i2;
3373        }
3374    };
3375
3376    _at->max_x_size = x::max(_at->max_x_size, xfig->maxx - xfig->minx);
3377    _at->max_y_size = x::max(_at->max_y_size, xfig->maxy - xfig->miny);
3378
3379    if (resize) {
3380        recalc_size_atShow(AW_RESIZE_ANY);
3381        set_window_size(_at->max_x_size+1000, _at->max_y_size+1000);
3382    }
3383}
3384
3385void AW_window::_set_activate_callback(void *widget) {
3386    if (_callback && (long)_callback != 1) {
3387        if (!_callback->help_text && _at->helptext_for_next_button) {
3388            _callback->help_text = _at->helptext_for_next_button;
3389            _at->helptext_for_next_button = 0;
3390        }
3391
3392        XtAddCallback((Widget) widget, XmNactivateCallback,
3393                (XtCallbackProc) AW_server_callback, (XtPointer) _callback);
3394    }
3395    _callback = NULL;
3396}
3397
3398GB_ERROR AW_root::start_macro_recording(const char *file, const char *application_id, const char *stop_action_name) {
3399    GB_ERROR error = NULL;
3400    if (prvt->recording_macro_file) {
3401        error = "Already recording macro";
3402    }
3403    else {
3404        char *path               = NULL;
3405        if (file[0] == '/') path = strdup(file);
3406        else  path               = GBS_global_string_copy("%s/%s", GB_getenvARBMACROHOME(), file);
3407
3408        char *macro_header       = GB_read_file(GB_path_in_ARBLIB("macro.head"));
3409        if (!macro_header) error = GB_await_error();
3410        else {
3411            prvt->recording_macro_file = fopen(path, "w");
3412
3413            if (prvt->recording_macro_file) {
3414                prvt->stop_action_name            = strdup(stop_action_name);
3415                prvt->application_name_for_macros = strdup(application_id);
3416
3417                fprintf(prvt->recording_macro_file, "%s", macro_header);
3418                reassign(prvt->recording_macro_path, path);
3419            }
3420            else error = GB_IO_error("recording to", file);
3421            free(macro_header);
3422        }
3423        free(path);
3424    }
3425    return error;
3426}
3427
3428GB_ERROR AW_root::stop_macro_recording() {
3429    GB_ERROR error = NULL;
3430    if (!prvt->recording_macro_file) {
3431        error = "Not recording macro";
3432    }
3433    else {
3434        fprintf(prvt->recording_macro_file, "ARB::close($gb_main);");
3435        fclose(prvt->recording_macro_file);
3436
3437        long mode = GB_mode_of_file(prvt->recording_macro_path);
3438        error     = GB_set_mode_of_file(prvt->recording_macro_path, mode | ((mode >> 2)& 0111));
3439
3440        prvt->recording_macro_file = 0;
3441
3442        freenull(prvt->recording_macro_path);
3443        freenull(prvt->stop_action_name);
3444        freenull(prvt->application_name_for_macros);
3445    }
3446    return error;
3447}
3448
3449GB_ERROR AW_root::execute_macro(const char *file) {
3450    char *path = 0;
3451    if (file[0] == '/') {
3452        path = strdup(file);
3453    }
3454    else {
3455        path = GBS_global_string_copy("%s/%s", GB_getenvARBMACROHOME(), file);
3456    }
3457    const char *com = GBS_global_string("perl %s &", path);
3458    printf("[Action '%s']\n", com);
3459    if (system(com)) {
3460        aw_message(GBS_global_string("Calling '%s' failed", com));
3461    }
3462    free(path);
3463    return 0;
3464}
3465
3466void AW_root::define_remote_command(AW_cb_struct *cbs) {
3467    if (cbs->f == (AW_CB)AW_POPDOWN) {
3468        aw_assert(!cbs->get_cd1() && !cbs->get_cd2()); // popdown takes no parameters (please pass ", 0, 0"!)
3469    }
3470
3471    AW_cb_struct *old_cbs = (AW_cb_struct*)GBS_write_hash(prvt->action_hash, cbs->id, (long)cbs);
3472    if (old_cbs) {
3473        if (!old_cbs->is_equal(*cbs)) {                  // existing remote command replaced by different callback
3474#if defined(DEBUG)
3475            fputs(GBS_global_string("Warning: reused callback id '%s' for different callback\n", old_cbs->id), stderr);
3476#if defined(DEVEL_RALF) && 0
3477            aw_assert(0);
3478#endif // DEVEL_RALF
3479#endif // DEBUG
3480        }
3481        // do not free old_cbs, cause it's still reachable from first widget that defined this remote command
3482    }
3483}
3484
3485#if defined(DEBUG)
3486// #define DUMP_REMOTE_ACTIONS
3487#endif // DEBUG
3488
3489GB_ERROR AW_root::check_for_remote_command(AW_default gb_maind, const char *rm_base) {
3490    GBDATA *gb_main = (GBDATA *)gb_maind;
3491
3492    char *awar_action = GBS_global_string_copy("%s/action", rm_base);
3493    char *awar_value  = GBS_global_string_copy("%s/value", rm_base);
3494    char *awar_awar   = GBS_global_string_copy("%s/awar", rm_base);
3495    char *awar_result = GBS_global_string_copy("%s/result", rm_base);
3496
3497    GB_push_transaction(gb_main);
3498
3499    char *action   = GBT_readOrCreate_string(gb_main, awar_action, "");
3500    char *value    = GBT_readOrCreate_string(gb_main, awar_value, "");
3501    char *tmp_awar = GBT_readOrCreate_string(gb_main, awar_awar, "");
3502
3503    if (tmp_awar[0]) {
3504        GB_ERROR error = 0;
3505        if (strcmp(action, "AWAR_REMOTE_READ") == 0) {
3506            char *read_value = this->awar(tmp_awar)->read_as_string();
3507            GBT_write_string(gb_main, awar_value, read_value);
3508#if defined(DUMP_REMOTE_ACTIONS)
3509            printf("remote command 'AWAR_REMOTE_READ' awar='%s' value='%s'\n", tmp_awar, read_value);
3510#endif // DUMP_REMOTE_ACTIONS
3511            free(read_value);
3512            // clear action (AWAR_REMOTE_READ is just a pseudo-action) :
3513            action[0] = 0;
3514            GBT_write_string(gb_main, awar_action, "");
3515        }
3516        else if (strcmp(action, "AWAR_REMOTE_TOUCH") == 0) {
3517            this->awar(tmp_awar)->touch();
3518#if defined(DUMP_REMOTE_ACTIONS)
3519            printf("remote command 'AWAR_REMOTE_TOUCH' awar='%s'\n", tmp_awar);
3520#endif // DUMP_REMOTE_ACTIONS
3521            // clear action (AWAR_REMOTE_TOUCH is just a pseudo-action) :
3522            action[0] = 0;
3523            GBT_write_string(gb_main, awar_action, "");
3524        }
3525        else {
3526#if defined(DUMP_REMOTE_ACTIONS)
3527            printf("remote command (write awar) awar='%s' value='%s'\n", tmp_awar, value);
3528#endif // DUMP_REMOTE_ACTIONS
3529            error = this->awar(tmp_awar)->write_as_string(value);
3530        }
3531        GBT_write_string(gb_main, awar_result, error ? error : "");
3532        GBT_write_string(gb_main, awar_awar, ""); // tell perl-client call has completed (BIO::remote_awar and BIO:remote_read_awar)
3533    }
3534    GB_pop_transaction(gb_main);
3535
3536    if (action[0]) {
3537        AW_cb_struct *cbs = (AW_cb_struct *)GBS_read_hash(prvt->action_hash, action);
3538
3539#if defined(DUMP_REMOTE_ACTIONS)
3540        printf("remote command (%s) exists=%i\n", action, int(cbs != 0));
3541#endif                          // DUMP_REMOTE_ACTIONS
3542        if (cbs) {
3543            cbs->run_callback();
3544            GBT_write_string(gb_main, awar_result, "");
3545        }
3546        else {
3547            aw_message(GB_export_errorf("Unknown action '%s' in macro", action));
3548            GBT_write_string(gb_main, awar_result, GB_await_error());
3549        }
3550        GBT_write_string(gb_main, awar_action, ""); // tell perl-client call has completed (remote_action)
3551    }
3552
3553    free(tmp_awar);
3554    free(value);
3555    free(action);
3556
3557    free(awar_result);
3558    free(awar_awar);
3559    free(awar_value);
3560    free(awar_action);
3561
3562    return 0;
3563}
3564
3565void AW_window::set_background(const char *colorname, Widget parentWidget) {
3566    bool colorSet = false;
3567
3568    if (colorname) {
3569        XColor unused, color;
3570
3571        if (XAllocNamedColor(p_global->display, p_global->colormap, colorname, &color, &unused)
3572                == 0) {
3573            fprintf(stderr, "XAllocColor failed: %s\n", colorname);
3574        }
3575        else {
3576            _at->background_color = color.pixel;
3577            colorSet = true;
3578        }
3579    }
3580
3581    if (!colorSet) {
3582        XtVaGetValues(parentWidget, XmNbackground, &(_at->background_color),
3583                NULL); // fallback to background color
3584    }
3585}
3586
3587void AW_window::TuneOrSetBackground(Widget w, const char *color, int modStrength) {
3588    // Sets the background for the next created widget.
3589    //
3590    // If 'color' is specified, it may contain one of the following values:
3591    //      "+"    means: slightly increase color of parent widget 'w'
3592    //      "-"    means: slightly decrease color of parent widget 'w'
3593    //      otherwise it contains a specific color ('name' or '#RGB')
3594    //
3595    // If color is not specified, the color of the parent widget 'w' is modified
3596    // by 'modStrength' (increased if positive,  decreased if negative)
3597    //
3598    // If it's not possible to modify the color (e.g. we cannot increase 'white'),
3599    // the color will be modified in the opposite direction. For details see TuneBackground()
3600
3601    if (color) {
3602        switch (color[0]) {
3603        case '+':
3604            TuneBackground(w, TUNE_BRIGHT);
3605            break;
3606        case '-':
3607            TuneBackground(w, TUNE_DARK);
3608            break;
3609        default:
3610            set_background(color, w); // use explicit color
3611        }
3612    }
3613    else {
3614        TuneBackground(w, modStrength);
3615    }
3616}
3617
3618void AW_window::TuneBackground(Widget w, int modStrength) {
3619    // Gets the Background Color, modifies the rgb values slightly and sets new background color
3620    // Intended to give buttons a nicer 3D-look.
3621    //
3622    // possible values for modStrength:
3623    //
3624    //    0        = do not modify (i.e. set to background color of parent widget)
3625    //    1 .. 127 = increase if background is bright, decrease if background is dark
3626    //   -1 ..-127 = opposite behavior than above
3627    //  256 .. 383 = always increase
3628    // -256 ..-383 = always decrease
3629    //
3630    // if it's impossible to decrease or increase -> opposite direction is used.
3631
3632    int col[3];
3633    {
3634        Pixel bg;
3635        XtVaGetValues(w, XmNbackground, &bg, NULL);
3636
3637        XColor xc;
3638        xc.pixel = bg;
3639        XQueryColor(XtDisplay(w), p_global->colormap, &xc);
3640
3641        col[0] = xc.red >> 8; // take MSB
3642        col[1] = xc.green >> 8;
3643        col[2] = xc.blue >> 8;
3644    }
3645
3646    int mod = modStrength;
3647    int preferredDir = 0;
3648    bool invertedMod = false;
3649
3650    if (modStrength>0) {
3651        if (modStrength>255) {
3652            mod -= 256;
3653            preferredDir = 1; // increase preferred
3654        }
3655    }
3656    else {
3657        if (modStrength<-255) {
3658            mod = -modStrength-256;
3659            preferredDir = -1; // decrease preferred
3660        }
3661        else {
3662            invertedMod = true;
3663            mod = -mod;
3664        }
3665    }
3666
3667    aw_assert(mod >= 0 && mod < 128);
3668    // illegal modification
3669
3670    bool incPossible[3]; // increment possible for color
3671    bool decPossible[3]; // decrement possible for color
3672    int incs = 0; // count possible increments
3673    int decs = 0; // count possible decrements
3674
3675    for (int i = 0; i<3; ++i) {
3676        if ((incPossible[i] = ((col[i]+mod) <= 255)))
3677            incs++;
3678        if ((decPossible[i] = ((col[i]-mod) >= 0)))
3679            decs++;
3680    }
3681
3682    aw_assert(incs||decs);
3683
3684    switch (preferredDir) {
3685    case 0: // no direction preferred yet, need to decide
3686        if (invertedMod)
3687            preferredDir = decs ? -1 : 1;
3688        else
3689            preferredDir = incs ? 1 : -1;
3690        break;
3691    case 1:
3692        if (!incs)
3693            preferredDir = -1;
3694        break;
3695    case -1:
3696        if (!decs)
3697            preferredDir = 1;
3698        break;
3699    }
3700
3701    aw_assert(preferredDir == 1 || preferredDir == -1); // no direction chosen above
3702
3703    if (preferredDir == 1) {
3704        for (int i=0; i<3; ++i) col[i] += (incPossible[i] ? mod : 0);
3705    }
3706    else if (preferredDir == -1) {
3707        for (int i=0; i<3; ++i) col[i] -= (decPossible[i] ? mod : 0);
3708    }
3709
3710
3711    char hex_color[50];
3712    sprintf(hex_color, "#%2.2X%2.2X%2.2X", col[0], col[1], col[2]);
3713    aw_assert(strlen(hex_color) == 7);
3714    // otherwise some value overflowed
3715    set_background(hex_color, w);
3716}
3717
Note: See TracBrowser for help on using the repository browser.