source: trunk/WINDOW/AW_device.cxx

Last change on this file was 16961, checked in by westram, 7 years ago
  • partial merge from 'fix' into 'trunk'
    • refactored AW_device text output
      • reduces calls to strlen (using SizedCstr)
      • eliminated/modernized several parameters/functions (esp. in TextOverlayCallbacks)
  • adds: log:branches/fix@16939:16960
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.9 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : AW_device.cxx                                     //
4//   Purpose   :                                                   //
5//                                                                 //
6//   Institute of Microbiology (Technical University Munich)       //
7//   http://www.arb-home.de/                                       //
8//                                                                 //
9// =============================================================== //
10
11#include "aw_window.hxx"
12#include "aw_root.hxx"
13#include "aw_common_xm.hxx"
14
15#include <arb_mem.h>
16#include <arb_msg.h>
17
18#if defined(DEBUG)
19// #define SHOW_CLIP_STACK_CHANGES
20#endif // DEBUG
21
22class AW_clip_scale_stack {
23    // completely private, but accessible by AW_device
24    friend class AW_device;
25
26    AW_screen_area  clip_rect;
27    AW_font_overlap font_overlap;
28
29    AW::Vector offset;
30    AW_pos scale;
31
32    class AW_clip_scale_stack *next;
33};
34
35#if defined(SHOW_CLIP_STACK_CHANGES)
36static const char *clipstatestr(AW_device *device) {
37    static char buffer[1024];
38
39    const AW_screen_area&  clip_rect = device->get_cliprect();
40    const AW_font_overlap& fo        = device->get_font_overlap();
41    const AW::Vector&      offset    = device->get_offset();
42
43    sprintf(buffer,
44            "clip_rect={t=%i, b=%i, l=%i, r=%i} "
45            "font_overlap={t=%i, b=%i, l=%i, r=%i} "
46            "scale=%f unscale=%f "
47            "offset={x=%f y=%f}" ,
48            clip_rect.t, clip_rect.b, clip_rect.l, clip_rect.r,
49            fo.top, fo.bottom, fo.left, fo.right,
50            device->get_scale(), device->get_unscale(),
51            offset.x(), offset.y());
52
53    return buffer;
54}
55#endif // SHOW_CLIP_STACK_CHANGES
56
57const AW_screen_area& AW_device::get_area_size() const {
58    return get_common()->get_screen();
59}
60
61
62void AW_device::pop_clip_scale() {
63    if (!clip_scale_stack) {
64        aw_assert(0); // Too many pop_clip_scale on that device
65        return;
66    }
67
68#if defined(SHOW_CLIP_STACK_CHANGES)
69    char *state_before_pop = strdup(clipstatestr(this));
70#endif // SHOW_CLIP_STACK_CHANGES
71
72    AW_zoomable::reset();
73    set_offset(clip_scale_stack->offset); // needs to be called before zoom()
74    zoom(clip_scale_stack->scale);
75    set_cliprect(clip_scale_stack->clip_rect);
76    set_font_overlap(clip_scale_stack->font_overlap);
77
78    aw_assert(get_scale() == clip_scale_stack->scale);
79
80    AW_clip_scale_stack *oldstack = clip_scale_stack;
81    clip_scale_stack              = clip_scale_stack->next;
82    delete oldstack;
83
84#if defined(SHOW_CLIP_STACK_CHANGES)
85    printf(" pop_clip_scale: %s\n", state_before_pop);
86    printf("    [after pop]: %s\n\n", clipstatestr(this));
87    free(state_before_pop);
88#endif // SHOW_CLIP_STACK_CHANGES
89}
90
91void AW_device::push_clip_scale() {
92    AW_clip_scale_stack *stack = new AW_clip_scale_stack;
93
94    stack->next      = clip_scale_stack;
95    clip_scale_stack = stack;
96
97    stack->scale        = get_scale();
98    stack->offset       = get_offset();
99    stack->font_overlap = get_font_overlap();
100    stack->clip_rect    = get_cliprect();
101
102#if defined(SHOW_CLIP_STACK_CHANGES)
103    printf("push_clip_scale: %s\n", clipstatestr(this));
104#endif // SHOW_CLIP_STACK_CHANGES
105}
106
107bool AW_device::text_overlay(int gc, const SizedCstr& cstr, const AW::Position& pos, AW_pos alignment, AW_bitset filteri, TextOverlayCallback toc, AW_CL cduser) {
108    // reduces any string (or virtual string) to its actual drawn size and
109    // calls the function 'toc' with the result.
110
111    const AW_GC           *gcm  = get_common()->map_gc(gc);
112    const AW_font_limits&  font = gcm->get_font_limits();
113
114
115    bool inside_clipping_left  = true; // clipping at the left edge of the screen is different from clipping right of the left edge.
116    bool inside_clipping_right = true;
117
118    // es gibt 4 clipping Moeglichkeiten:
119    // 1. man will fuer den Fall clippen, dass man vom linken display-Rand aus druckt   => clipping rechts vom 1. Buchstaben
120    // 2. man will fuer den Fall clippen, dass man mitten im Bildschirm ist             => clipping links vom 1. Buchstaben
121    // 3. man will fuer den Fall clippen, dass man mitten im Bildschirm ist             => clipping links vom letzten Buchstaben
122    // 4. man will fuer den Fall clippen, dass man bis zum rechten display-Rand druckt  => clipping rechts vom letzten Buchstaben
123
124    if (!(filter & filteri)) return 0;
125
126    const AW_screen_area& screen   = get_common()->get_screen();
127    const AW_screen_area& clipRect = get_cliprect();
128
129    if (allow_left_font_overlap() || screen.l == clipRect.l) inside_clipping_left = false;
130    if (allow_right_font_overlap() || clipRect.r == screen.r) inside_clipping_right = false;
131
132    AW::Position tpos(transform(pos));
133    {
134        const AW_pos Y0 = tpos.ypos();
135        if (allow_top_font_overlap() || clipRect.t == 0) { // check clip border inside screen
136            if (Y0+font.descent < clipRect.t) return 0; // draw outside screen
137        }
138        else {
139            if (Y0-font.ascent < clipRect.t) return 0;  // don't cross the clip border
140        }
141
142        if (allow_bottom_font_overlap() || clipRect.b == screen.b) {   // check clip border inside screen
143            if (Y0-font.ascent > clipRect.b) return 0;  // draw outside screen
144        }
145        else {
146            if (Y0+font.descent> clipRect.b) return 0;  // don't cross the clip border
147        }
148    }
149
150    long drawLen = cstr.get_length();
151    int  drawOff = 0;
152
153    const char * const textBuf = cstr;
154#if defined(ASSERTION_USED)
155    const long textBufLen = cstr.get_length();
156#endif
157
158    {
159        int xi;
160        {
161            AW_pos X0 = tpos.xpos();
162            if (alignment) {
163                AW_pos width = get_string_size(gc, cstr);
164                X0 = X0-alignment*width;
165            }
166            if (X0 > clipRect.r) return 0; // right of screen
167
168            xi = AW_INT(X0);
169        }
170
171        // now clip left side
172        {
173            int l = (int)clipRect.l;
174            if (xi + drawLen*font.width < l) return 0; // left of screen
175
176            if (xi < l) {
177                if (font.is_monospaced()) {
178                    int h = (l - xi)/font.width;
179                    if (inside_clipping_left) {
180                        if ((l-xi)%font.width  >0) h += 1;
181                    }
182                    if (h >= drawLen) return 0;
183                    drawOff  = h;
184                    xi      += h*font.width;
185                    drawLen -= h;
186
187                    if (drawLen < 0) return 0;
188                    aw_assert(textBufLen >= drawLen);
189                }
190                else { // proportional font
191                    int c = 0;
192                    int h;
193                    for (h=0; xi < l; h++) {
194                        if (!(c = textBuf[h])) return 0;
195                        xi += gcm->get_width_of_char(c);
196                    }
197                    if (!inside_clipping_left) {
198                        h-=1;
199                        xi -= gcm->get_width_of_char(c);
200                    }
201                    drawOff  = h;
202                    drawLen -= h;
203
204                    if (drawLen < 0) return 0;
205                    aw_assert(textBufLen >= drawLen);
206                }
207            }
208        }
209
210        // now clip right side
211        {
212            int r = (int)clipRect.r - xi;
213            if (font.is_monospaced()) {
214                int h = r / font.width;
215                if (h < drawLen) {
216                    if (inside_clipping_right) {
217                        drawLen = h;
218                    }
219                    else {
220                        drawLen = h+1;
221                    }
222                }
223
224                if (drawLen < 0) return 0;
225                aw_assert(textBufLen >= drawLen);
226            }
227            else { // proportional font
228                int h;
229                for (h = drawOff; r >= 0 && drawLen > 0;  h++, drawLen--) { // was drawLen >= 0
230                    r -= gcm->get_width_of_char(textBuf[h]);
231                }
232                drawLen = h - drawOff;
233                if (r <= 0 && inside_clipping_right && drawLen > 0) {
234                    drawLen -= 1;
235                }
236
237                if (drawLen < 0) return 0;
238                aw_assert(textBufLen >= drawLen);
239            }
240        }
241
242        tpos = AW::Position(xi, tpos.ypos()); // store corrected X-position in tpos
243    }
244
245    aw_assert(drawLen >= 0 && textBufLen >= drawLen);
246    aw_assert(textBufLen >= (drawOff+drawLen));
247
248    return toc(this, gc, textBuf, drawOff, drawLen, rtransform(tpos), cduser);
249}
250
251bool AW_device::generic_polygon(int gc, int npos, const AW::Position *pos, AW_bitset filteri) {
252    bool drawflag = false;
253    if (filteri & filter) {
254        int p = npos-1;
255        for (int n = 0; n<npos; ++n) {
256            drawflag |= line(gc, pos[p], pos[n], filteri);
257            p = n;
258        }
259    }
260    return drawflag;
261}
262
263void AW_device::move_region(AW_pos /* src_x */, AW_pos /* src_y */, AW_pos /* width */, AW_pos /* height */,
264                            AW_pos /* dest_x */, AW_pos /* dest_y */) {
265    // empty default
266}
267
268void AW_device::flush() {
269    // empty default
270}
271
272static const AW_screen_area& get_universe() {
273    // "unrestricted" area
274    const int UMIN = INT_MIN/10;
275    const int UMAX = INT_MAX/10;
276    static AW_screen_area universe = { UMIN, UMAX, UMIN, UMAX };
277    return universe;
278}
279
280void AW_device::reset() {
281    while (clip_scale_stack) {
282        pop_clip_scale();
283    }
284    if (type() == AW_DEVICE_SIZE) {
285        set_cliprect(get_universe());
286    }
287    else {
288        set_cliprect(get_area_size());
289    }
290    AW_zoomable::reset();
291    specific_reset();
292}
293
294bool AW_device::generic_invisible(const AW::Position& pos, AW_bitset filteri) {
295    return (filter & filteri) ? !is_outside_clip(transform(pos)) : false;
296}
297
298const AW_screen_area& AW_device::get_common_screen(const AW_common *common_) {
299    return common_->get_screen();
300}
301
302bool AW_device::generic_box(int gc, const AW::Rectangle& rect, AW_bitset filteri) {
303    int drawflag = 0;
304    if (filteri & filter) {
305        drawflag |= line_impl(gc, rect.upper_edge(), filteri);
306        drawflag |= line_impl(gc, rect.lower_edge(), filteri);
307        drawflag |= line_impl(gc, rect.left_edge(),  filteri);
308        drawflag |= line_impl(gc, rect.right_edge(), filteri);
309    }
310    return drawflag;
311}
312
313void AW_device::clear(AW_bitset) {
314    // nothing to do
315}
316
317void AW_device::clear_part(const AW::Rectangle&, AW_bitset) {
318    // nothing to do
319}
320
321void AW_device::set_filter(AW_bitset filteri) {
322    filter = filteri;
323}
324
325void AW_device::fast() {}
326void AW_device::slow() {}
327bool AW_device::ready_to_draw(int gc) {
328    return get_common()->gc_mapable(gc);
329}
330
331void AW_zoomable::reset() {
332    unscale = scale   = 1.0;
333    offset  = AW::Vector(0, 0);
334}
335
336void AW_zoomable::zoom(AW_pos val) {
337    scale   *= val;
338    unscale  = 1.0/scale;
339}
340
341// -----------------
342//      AW_GC_Xm
343
344const int GC_DEFAULT_LINE_STYLE = LineSolid;
345const int GC_DEFAULT_CAP_STYLE  = CapProjecting;
346const int GC_JOIN_STYLE         = JoinMiter;
347
348AW_GC_Xm::AW_GC_Xm(AW_common *common_)
349    : AW_GC(common_)
350{
351    XGCValues val;
352
353    val.line_width = GC_DEFAULT_LINE_WIDTH;
354    val.line_style = GC_DEFAULT_LINE_STYLE;
355    val.cap_style  = GC_DEFAULT_CAP_STYLE;
356    val.join_style = GC_JOIN_STYLE;
357
358    unsigned long value_mask = GCLineWidth|GCLineStyle|GCCapStyle|GCJoinStyle;
359
360    gc = XCreateGC(get_common()->get_display(), get_common()->get_window_id(), value_mask, &val);
361    wm_set_function(get_function());
362}
363AW_GC_Xm::~AW_GC_Xm() {
364    if (gc) XFreeGC(get_common()->get_display(), gc);
365}
366void AW_GC_Xm::wm_set_lineattributes(short lwidth, AW_linestyle lstyle) {
367    Display            *display = get_common()->get_display();
368    aw_assert(lwidth>0);
369
370    switch (lstyle) {
371        case AW_SOLID:
372            XSetLineAttributes(display, gc, lwidth, LineSolid, GC_DEFAULT_CAP_STYLE, GC_JOIN_STYLE);
373            break;
374
375        case AW_DOTTED:
376        case AW_DASHED: {
377            static char dashes[] = { 5, 2 };
378            static char dots[]   = { 1, 1 };
379            XSetDashes(display, gc, 0, lstyle == AW_DOTTED ? dots : dashes, 2);
380            XSetLineAttributes(display, gc, lwidth, LineOnOffDash, CapButt, GC_JOIN_STYLE);
381            break;
382        }
383    }
384}
385void AW_GC_Xm::wm_set_function(AW_function mode) {
386    switch (mode) {
387        case AW_XOR:
388            XSetFunction(get_common()->get_display(), gc, GXxor);
389            break;
390        case AW_COPY:
391            XSetFunction(get_common()->get_display(), gc, GXcopy);
392            break;
393    }
394}
395void AW_GC_Xm::wm_set_foreground_color(AW_rgb col) {
396    XSetForeground(get_common()->get_display(), gc, col);
397}
398
399const AW_font_limits& AW_stylable::get_font_limits(int gc, char c) const {
400    return get_common()->get_font_limits(gc, c);
401}
402
403int AW_GC::get_string_size(long len) const {
404    // calculate display size of 'len' characters of a monospaced font
405    aw_assert(font_limits.is_monospaced());
406    return len * font_limits.width;
407}
408
409int AW_GC::get_string_size(const SizedCstr& cstr) const {
410    // calculate display size of 'cstr'
411
412    aw_assert(bool(cstr)); // use other flavor!
413    if (font_limits.is_monospaced()) {
414        return get_string_size(cstr.get_length());
415    }
416
417    int         width = 0;
418    const char *str   = cstr;
419    size_t      len   = cstr.get_length();
420
421    for (size_t i = 0; i<len; ++i) {
422        aw_assert(str[i]); // otherwise cstr is invalid
423        width += width_of_chars[safeCharIndex(str[i])];
424    }
425
426    return width;
427}
428
429AW_GC *AW_common_Xm::create_gc() {
430    return new AW_GC_Xm(this);
431}
432
433void AW_GC_set::add_gc(int gi, AW_GC *agc) {
434    if (gi >= count) {
435        int new_count = gi+10;
436        ARB_recalloc(gcs, count, new_count);
437        count = new_count;
438    }
439    if (gcs[gi]) delete gcs[gi];
440    gcs[gi] = agc;
441}
442
443int AW_stylable::get_string_size(int gc, long textlen) const {
444    return get_common()->map_gc(gc)->get_string_size(textlen);
445}
446int AW_stylable::get_string_size(int gc, const SizedCstr& cstr) const {
447    return get_common()->map_gc(gc)->get_string_size(cstr);
448}
449void AW_stylable::new_gc(int gc) { get_common()->new_gc(gc); }
450void AW_stylable::set_grey_level(int gc, AW_grey_level grey_level) {
451    // <0 = don't fill, 0.0 = white, 1.0 = black
452    get_common()->map_mod_gc(gc)->set_grey_level(grey_level);
453}
454AW_grey_level AW_stylable::get_grey_level(int gc) {
455    return get_common()->map_gc(gc)->get_grey_level();
456}
457
458void AW_stylable::set_font(int gc, AW_font font_nr, int size, int *found_size) {
459    // if found_size != 0 -> return value for used font size
460    get_common()->map_mod_gc(gc)->set_font(font_nr, size, found_size);
461}
462int AW_stylable::get_available_fontsizes(int gc, AW_font font_nr, int *available_sizes) {
463    return get_common()->map_gc(gc)->get_available_fontsizes(font_nr, available_sizes);
464}
465void AW_stylable::set_line_attributes(int gc, short width, AW_linestyle style) {
466    get_common()->map_mod_gc(gc)->set_line_attributes(width, style);
467}
468void AW_stylable::set_function(int gc, AW_function function) {
469    get_common()->map_mod_gc(gc)->set_function(function);
470}
471void AW_stylable::set_foreground_color(int gc, AW_color_idx color) {
472    get_common()->map_mod_gc(gc)->set_fg_color(get_common()->get_color(color));
473}
474void AW_stylable::establish_default(int gc) {
475    get_common()->map_mod_gc(gc)->establish_default();
476}
477void AW_stylable::reset_style() {
478    get_common()->reset_style();
479}
480
481static void AW_get_common_extends_cb(AW_window *, AW_common_Xm *common) {
482    Window        root;
483    unsigned int  width, height;
484    unsigned int  depth, borderwidth; // unused
485    int           x_offset, y_offset; // unused
486
487    XGetGeometry(common->get_display(), common->get_window_id(),
488                 &root,
489                 &x_offset,
490                 &y_offset,
491                 &width,
492                 &height,
493                 &borderwidth,  // border width
494                 &depth);       // depth of display
495
496    common->set_screen_size(width, height);
497}
498
499void AW_common_Xm::install_common_extends_cb(AW_window *aww, AW_area area) {
500    aww->set_resize_callback(area, makeWindowCallback(AW_get_common_extends_cb, this));
501    AW_get_common_extends_cb(aww, this);
502}
503
Note: See TracBrowser for help on using the repository browser.