1
// =============================================================== //
3
// File : AW_root.cxx //
6
// Institute of Microbiology (Technical University Munich) //
7
// http://www.arb-home.de/ //
9
// =============================================================== //
11
#include "aw_root.hxx"
12
#include "aw_awar.hxx"
13
#include "aw_nawar.hxx"
15
#include "aw_window.hxx"
16
#include "aw_window_Xm.hxx"
17
#include "aw_status.hxx"
18
#include "aw_xkey.hxx"
20
#include <arb_handlers.h>
25
#include <X11/cursorfont.h>
27
AW_root *AW_root::SINGLETON = NULL;
29
void AW_system(AW_window *aww, const char *command, const char *auto_help_file) {
30
if (auto_help_file) AW_help_popup(aww, auto_help_file);
31
aw_message_if(GBK_system(command));
34
void AW_clock_cursor(AW_root *awr) {
35
awr->prvt->set_cursor(0, 0, awr->prvt->clock_cursor);
38
void AW_normal_cursor(AW_root *awr) {
39
awr->prvt->set_cursor(0, 0, 0);
42
void AW_help_entry_pressed(AW_window *aww) {
43
AW_root *root = aww->get_root();
44
p_global->help_active = 1;
47
void AW_root::process_events() {
48
XtAppProcessEvent(p_r->context, XtIMAll);
50
void AW_root::process_pending_events() {
51
XtInputMask pending = XtAppPending(p_r->context);
53
XtAppProcessEvent(p_r->context, pending);
54
pending = XtAppPending(p_r->context);
59
AW_ProcessEventType AW_root::peek_key_event(AW_window *) {
60
//! Returns type if key event follows, else 0
63
Boolean result = XtAppPeekEvent(p_r->context, &xevent);
65
if (!result) return NO_EVENT;
66
if ((xevent.type != KeyPress) && (xevent.type != KeyRelease)) return NO_EVENT;
67
return (AW_ProcessEventType)xevent.type;
70
AW_default AW_root::load_properties(const char *default_name) {
71
GBDATA *gb_default = GB_open(default_name, "rwcD");
75
error = GB_no_transaction(gb_default);
77
GBDATA *gb_tmp = GB_search(gb_default, "tmp", GB_CREATE_CONTAINER);
78
error = GB_set_temporary(gb_tmp);
82
error = GB_await_error();
86
const char *shown_name = strrchr(default_name, '/');
87
if (!shown_name) shown_name = default_name;
89
GBK_terminatef("Error loading properties '%s': %s", shown_name, error);
92
return (AW_default) gb_default;
95
static void destroy_AW_root() {
96
delete AW_root::SINGLETON;
97
AW_root::SINGLETON = NULL;
101
bool AW_root::is_focus_callback(AW_RCB fcb) const { // eliminated in gtk-branch
102
return focus_callback_list && focus_callback_list->contains(makeRootCallback(fcb, AW_CL(0), AW_CL(0)));
105
AW_root::AW_root(const char *propertyFile, const char *program, bool no_exit, UserActionTracker *user_tracker, int */*argc*/, char ***/*argv*/) {
106
aw_assert(!AW_root::SINGLETON); // only one instance allowed
107
AW_root::SINGLETON = this;
109
memset((char *)this, 0, sizeof(AW_root));
111
prvt = new AW_root_Motif;
113
init_variables(load_properties(propertyFile));
114
init_root(program, no_exit);
116
tracker = user_tracker;
118
atexit(destroy_AW_root); // do not call this before opening properties DB!
121
#if defined(UNIT_TESTS)
122
AW_root::AW_root(const char *propertyFile) {
123
aw_assert(!AW_root::SINGLETON); // only one instance allowed
124
AW_root::SINGLETON = this;
126
memset((char *)this, 0, sizeof(AW_root));
127
init_variables(load_properties(propertyFile));
128
atexit(destroy_AW_root); // do not call this before opening properties DB!
132
void AW_root::setUserActionTracker(UserActionTracker *user_tracker) {
133
aw_assert(user_tracker);
134
aw_assert(tracker->is_replaceable()); // there is already another tracker (program-logic-error)
137
tracker = user_tracker;
140
AW_awar *AW_root::label_is_awar(const char *label) {
141
AW_awar *awar_exists = NULL;
142
size_t off = strcspn(label, "/ ");
144
if (label[off] == '/') { // contains '/' and no space before first '/'
145
awar_exists = awar_no_error(label);
150
void AW_root::define_remote_command(AW_cb *cbs) {
151
if (cbs->contains(AW_CB(AW_POPDOWN))) {
152
aw_assert(!cbs->get_cd1() && !cbs->get_cd2()); // popdown takes no parameters (please pass ", 0, 0"!)
155
AW_cb *old_cbs = (AW_cb*)GBS_write_hash(prvt->action_hash, cbs->id, (long)cbs);
157
if (!old_cbs->is_equal(*cbs)) { // existing remote command replaced by different callback
159
fputs(GBS_global_string("Warning: reused callback id '%s' for different callback\n", old_cbs->id), stderr);
160
#if defined(DEVEL_RALF) && 1
165
// do not free old_cbs, cause it's still reachable from first widget that defined this remote command
169
AW_cb *AW_root::search_remote_command(const char *action) {
170
return (AW_cb *)GBS_read_hash(prvt->action_hash, action);
173
static long set_focus_policy(const char *, long cl_aww, void *) {
174
AW_window *aww = (AW_window*)cl_aww;
175
aww->set_focus_policy(aww->get_root()->focus_follows_mouse);
178
void AW_root::apply_focus_policy(bool follow_mouse) {
179
focus_follows_mouse = follow_mouse;
180
GBS_hash_do_loop(hash_for_windows, set_focus_policy, 0);
183
void AW_root::apply_sensitivity(AW_active mask) {
184
aw_assert(legal_mask(mask));
185
AW_buttons_struct *list;
188
for (list = button_sens_list; list; list = list->next) {
189
XtSetSensitive(list->button, (list->mask & mask) ? True : False);
200
static struct fallbacks aw_fb[] = {
201
// Name fallback awarname default value
202
{ "FontList", "window/font", "8x13bold" },
203
{ "background", "window/background", "grey" },
204
{ "foreground", "window/foreground", "Black", },
205
{ 0, "window/color_1", "red", },
206
{ 0, "window/color_2", "green", },
207
{ 0, "window/color_3", "blue", },
212
void AW_root::init_variables(AW_default database) {
213
application_database = database;
214
hash_table_for_variables = GBS_create_hash(1000, GB_MIND_CASE);
215
hash_for_windows = GBS_create_hash(100, GB_MIND_CASE);
217
for (int i=0; aw_fb[i].awar; ++i) {
218
awar_string(aw_fb[i].awar, aw_fb[i].init, application_database);
222
static void aw_message_and_dump_stderr(const char *msg) {
224
fprintf(stderr, "ARB: %s\n", msg); // print to console as well
228
static void dump_stdout(const char *msg) {
229
fprintf(stdout, "ARB: %s\n", msg);
232
static arb_status_implementation AW_status_impl = {
236
aw_status_title, // set_title
237
AW_status, // set_subtitle
238
AW_status, // set_gauge
239
AW_status, // user_abort
242
static arb_handlers aw_handlers = {
243
aw_message_and_dump_stderr,
249
void AW_root::init_root(const char *programname, bool no_exit) {
250
// initialize ARB X application
252
XFontStruct *fontstruct;
253
const int MAX_FALLBACKS = 30;
254
char *fallback_resources[MAX_FALLBACKS];
256
prvt->action_hash = GBS_create_hash(1000, GB_MIND_CASE);
258
p_r-> no_exit = no_exit;
259
program_name = strdup(programname);
262
for (i=0; aw_fb[i].fb; i++) {
263
GBDATA *gb_awar = GB_search((GBDATA*)application_database, aw_fb[i].awar, GB_FIND);
264
fallback_resources[i] = GBS_global_string_copy("*%s: %s", aw_fb[i].fb, GB_read_char_pntr(gb_awar));
266
fallback_resources[i] = 0;
267
aw_assert(i<MAX_FALLBACKS);
269
ARB_install_handlers(aw_handlers);
271
// @@@ FIXME: the next line hangs if program runs inside debugger
272
p_r->toplevel_widget = XtOpenApplication(&(p_r->context), programname,
273
NULL, 0, // XrmOptionDescRec+numOpts
277
applicationShellWidgetClass, // widget class
280
for (i=0; fallback_resources[i]; i++) free(fallback_resources[i]);
282
p_r->display = XtDisplay(p_r->toplevel_widget);
284
if (p_r->display == NULL) {
285
printf("cannot open display\n");
289
GBDATA *gbd = (GBDATA*)application_database;
290
const char *font = GB_read_char_pntr(GB_search(gbd, "window/font", GB_FIND));
291
if (!(fontstruct = XLoadQueryFont(p_r->display, font))) {
292
if (!(fontstruct = XLoadQueryFont(p_r->display, "fixed"))) {
293
printf("can not load font\n");
299
if (fontstruct->max_bounds.width == fontstruct->min_bounds.width) {
300
font_width = fontstruct->max_bounds.width;
303
font_width = (fontstruct->min_bounds.width
304
+ fontstruct->max_bounds.width) / 2;
307
font_height = fontstruct->max_bounds.ascent
308
+ fontstruct->max_bounds.descent;
309
font_ascent = fontstruct->max_bounds.ascent;
311
p_r->fontlist = XmFontListCreate(fontstruct, XmSTRING_DEFAULT_CHARSET);
313
button_sens_list = 0;
315
p_r->last_option_menu = p_r->current_option_menu = p_r->option_menu_list = NULL;
316
p_r->last_toggle_field = p_r->toggle_field_list = NULL;
317
p_r->last_selection_list = p_r->selection_list = NULL;
319
value_changed = false;
320
y_correction_for_input_labels = 5;
321
global_mask = AWM_ALL;
323
p_r->screen_depth = PlanesOfScreen(XtScreen(p_r->toplevel_widget));
324
if (p_r->screen_depth == 1) {
325
color_mode = AW_MONO_COLOR;
328
color_mode = AW_RGB_COLOR;
330
p_r->colormap = DefaultColormapOfScreen(XtScreen(p_r->toplevel_widget));
331
p_r->clock_cursor = XCreateFontCursor(XtDisplay(p_r->toplevel_widget), XC_watch);
332
p_r->question_cursor = XCreateFontCursor(XtDisplay(p_r->toplevel_widget), XC_question_arrow);
335
aw_root_init_font(XtDisplay(p_r->toplevel_widget));
336
aw_install_xkeys(XtDisplay(p_r->toplevel_widget));
340
AW_root::~AW_root() {
341
delete tracker; tracker = NULL;
343
AW_root_cblist::clear(focus_callback_list);
344
delete button_sens_list; button_sens_list = NULL;
348
aw_assert(this == AW_root::SINGLETON);
354
AW_root::SINGLETON = NULL;
358
* A list of awar names that contain color names
360
static const char *aw_awar_2_color[] = {
369
void AW_root::create_colormap() {
371
XColor xcolor_returned, xcolor_exakt;
372
GBDATA *gbd = check_properties(NULL);
373
prvt->color_table = (AW_rgb*)GB_calloc(sizeof(AW_rgb), AW_STD_COLOR_IDX_MAX);
375
// Color monitor, B&W monitor is no longer supported
376
const char **awar_2_color;
378
for (color = 0, awar_2_color = aw_awar_2_color;
380
awar_2_color++, color++)
382
const char *name_of_color = GB_read_char_pntr(GB_search(gbd, *awar_2_color, GB_FIND));
383
if (XAllocNamedColor(prvt->display, prvt->colormap, name_of_color, &xcolor_returned, &xcolor_exakt) == 0) {
384
fprintf(stderr, "XAllocColor failed: %s\n", name_of_color);
387
prvt->color_table[color] = xcolor_returned.pixel;
391
prvt->foreground = BlackPixelOfScreen(XtScreen(prvt->toplevel_widget));
392
XtVaGetValues(prvt->toplevel_widget, XmNbackground, &prvt->background, NULL);
393
// AW_WINDOW_DRAG see init_devices
397
void AW_root::window_hide(AW_window *aww) {
399
if (active_windows<0) {
402
if (current_modal_window == aww) {
403
current_modal_window = NULL;
406
void AW_root::window_show() {
410
/// begin timer stuff
412
class AW_timer_cb_struct : virtual Noncopyable {
416
AW_timer_cb_struct(AW_root *aw_root, const TimedCallback& tcb) : awr(aw_root), cb(tcb) {} // use install!
419
typedef XtTimerCallbackProc timer_callback;
421
static void install(AW_root *aw_root, const TimedCallback& tcb, unsigned ms, timer_callback tc) {
422
AW_timer_cb_struct *tcbs = new AW_timer_cb_struct(aw_root, tcb);
423
tcbs->callAfter(ms, tc);
429
unsigned callOrDelayIfDisabled() {
430
return awr->disable_callbacks
431
? 25 // delay by 25 ms
434
void callAfter(unsigned ms, timer_callback tc) {
435
XtAppAddTimeOut(awr->prvt->context, ms, tc, this);
437
void recallOrUninstall(unsigned restart, timer_callback tc) {
438
if (restart) callAfter(restart, tc);
443
static void AW_timer_callback(XtPointer aw_timer_cb_struct, XtIntervalId*) {
444
AW_timer_cb_struct *tcbs = (AW_timer_cb_struct *)aw_timer_cb_struct;
446
unsigned restart = tcbs->callOrDelayIfDisabled();
447
tcbs->recallOrUninstall(restart, AW_timer_callback);
450
static void AW_timer_callback_never_disabled(XtPointer aw_timer_cb_struct, XtIntervalId*) {
451
AW_timer_cb_struct *tcbs = (AW_timer_cb_struct *)aw_timer_cb_struct;
453
unsigned restart = tcbs->call();
454
tcbs->recallOrUninstall(restart, AW_timer_callback_never_disabled);
458
void AW_root::add_timed_callback(int ms, const TimedCallback& tcb) {
459
AW_timer_cb_struct::install(this, tcb, ms, AW_timer_callback);
461
void AW_root::add_timed_callback_never_disabled(int ms, const TimedCallback& tcb) {
462
AW_timer_cb_struct::install(this, tcb, ms, AW_timer_callback_never_disabled);
469
AW_awar *AW_root::awar_no_error(const char *var_name) {
470
return hash_table_for_variables ? (AW_awar *)GBS_read_hash(hash_table_for_variables, var_name) : NULL;
474
AW_awar *AW_root::awar(const char *var_name) {
475
AW_awar *vs = awar_no_error(var_name);
476
if (!vs) GBK_terminatef("AWAR %s not defined", var_name);
480
AW_awar *AW_root::awar_float(const char *var_name, float default_value, AW_default default_file) {
481
AW_awar *vs = awar_no_error(var_name);
483
default_file = check_properties(default_file);
484
vs = new AW_awar(AW_FLOAT, var_name, "", (double)default_value, default_file, this);
485
GBS_write_hash(hash_table_for_variables, var_name, (long)vs);
490
AW_awar *AW_root::awar_string(const char *var_name, const char *default_value, AW_default default_file) {
491
AW_awar *vs = awar_no_error(var_name);
493
default_file = check_properties(default_file);
494
vs = new AW_awar(AW_STRING, var_name, default_value, 0, default_file, this);
495
GBS_write_hash(hash_table_for_variables, var_name, (long)vs);
500
AW_awar *AW_root::awar_int(const char *var_name, long default_value, AW_default default_file) {
501
AW_awar *vs = awar_no_error(var_name);
503
default_file = check_properties(default_file);
504
vs = new AW_awar(AW_INT, var_name, (const char *)default_value, 0, default_file, this);
505
GBS_write_hash(hash_table_for_variables, var_name, (long)vs);
510
AW_awar *AW_root::awar_pointer(const char *var_name, void *default_value, AW_default default_file) {
511
AW_awar *vs = awar_no_error(var_name);
513
default_file = check_properties(default_file);
514
vs = new AW_awar(AW_POINTER, var_name, (const char *)default_value, 0.0, default_file, this);
515
GBS_write_hash(hash_table_for_variables, var_name, (long)vs);
520
static long awar_set_temp_if_is_default(const char *, long val, void *cl_gb_db) {
521
AW_awar *awar = (AW_awar*)val;
522
awar->set_temp_if_is_default((GBDATA*)cl_gb_db);
526
void AW_root::dont_save_awars_with_default_value(GBDATA *gb_db) {
527
// this should only be called
528
// - before saving properties
529
// - before saving any main application DB that may contain AWARs
531
// Bug: Awars created in main DB by clients (e.g. EDIT4) will stay temporary
532
// and will never be saved again.
534
// Note: uninstanciated AWARs will not be affected, so different applications with
535
// different AWAR subsets should be no problem.
536
// 'different applications' here e.g. also includes different calls to arb_ntree
537
// (e.g. merge-tool, importer, tree-window, ...)
539
// Problems arise when an awar with the same name is used for two different purposes
540
// or with different default values (regardless whether in properties or main-DB).
541
// But this has already been problematic before.
543
GBS_hash_do_loop(hash_table_for_variables, awar_set_temp_if_is_default, (void*)gb_db);
546
void AW_root::main_loop() {
547
XtAppMainLoop(p_r->context);
550
static long AW_unlink_awar_from_DB(const char */*key*/, long cl_awar, void *cl_gb_main) {
551
AW_awar *awar = (AW_awar*)cl_awar;
552
GBDATA *gb_main = (GBDATA*)cl_gb_main;
554
awar->unlink_from_DB(gb_main);
558
void AW_root::unlink_awars_from_DB(GBDATA *gb_main) {
559
aw_assert(GB_get_root(gb_main) == gb_main);
561
GB_transaction ta(gb_main); // needed in awar-callbacks during unlink
562
GBS_hash_do_loop(hash_table_for_variables, AW_unlink_awar_from_DB, gb_main);
565
typedef std::list<GBDATA*> DataPointers;
567
static GB_ERROR set_parents_with_only_temp_childs_temp(GBDATA *gbd, DataPointers& made_temp) {
568
GB_ERROR error = NULL;
570
if (GB_read_type(gbd) == GB_DB && !GB_is_temporary(gbd)) {
571
bool has_savable_child = false;
572
for (GBDATA *gb_child = GB_child(gbd); gb_child && !error; gb_child = GB_nextChild(gb_child)) {
573
bool is_tmp = GB_is_temporary(gb_child);
575
error = set_parents_with_only_temp_childs_temp(gb_child, made_temp);
576
if (!error) is_tmp = GB_is_temporary(gb_child); // may have changed
578
if (!is_tmp) has_savable_child = true;
581
if (!error && !has_savable_child) {
582
error = GB_set_temporary(gbd);
583
made_temp.push_back(gbd);
590
static GB_ERROR clear_temp_flags(DataPointers& made_temp) {
591
GB_ERROR error = NULL;
592
for (DataPointers::iterator mt = made_temp.begin(); mt != made_temp.end() && !error; ++mt) {
593
error = GB_clear_temporary(*mt);
598
GB_ERROR AW_root::save_properties(const char *filename) {
599
GB_ERROR error = NULL;
600
GBDATA *gb_prop = application_database;
603
error = "No properties loaded - won't save";
606
error = GB_push_transaction(gb_prop);
608
aw_update_all_window_geometry_awars(this);
609
error = GB_pop_transaction(gb_prop);
611
dont_save_awars_with_default_value(gb_prop);
613
DataPointers made_temp;
614
error = set_parents_with_only_temp_childs_temp(gb_prop, made_temp); // avoid saving empty containers
615
if (!error) error = GB_save_in_arbprop(gb_prop, filename, "a");
616
if (!error) error = clear_temp_flags(made_temp);