| 1 | #include <stdio.h> |
|---|
| 2 | #include <stdlib.h> |
|---|
| 3 | #include <string.h> |
|---|
| 4 | #include <memory.h> |
|---|
| 5 | // #include <malloc.h> |
|---|
| 6 | #include <X11/X.h> |
|---|
| 7 | #include <X11/Xlib.h> |
|---|
| 8 | |
|---|
| 9 | #include <aw_root.hxx> |
|---|
| 10 | #include "aw_device.hxx" |
|---|
| 11 | #include "aw_commn.hxx" |
|---|
| 12 | #include <aw_click.hxx> |
|---|
| 13 | |
|---|
| 14 | using namespace AW; |
|---|
| 15 | |
|---|
| 16 | // ***************************************************************************************** |
|---|
| 17 | // device_click |
|---|
| 18 | // ***************************************************************************************** |
|---|
| 19 | |
|---|
| 20 | AW_device_click::AW_device_click(AW_common *commoni):AW_device(commoni) { |
|---|
| 21 | } |
|---|
| 22 | |
|---|
| 23 | void AW_device_click::init(AW_pos mousex,AW_pos mousey, AW_pos max_distance_linei, AW_pos max_distance_texti, AW_pos radi, AW_bitset filteri) { |
|---|
| 24 | AWUSE(radi); |
|---|
| 25 | mouse_x = mousex; |
|---|
| 26 | mouse_y = mousey; |
|---|
| 27 | filter = filteri; |
|---|
| 28 | max_distance_line = max_distance_linei*max_distance_linei; |
|---|
| 29 | max_distance_text = max_distance_texti; |
|---|
| 30 | memset((char *)&opt_line,0,sizeof(opt_line)); |
|---|
| 31 | memset((char *)&opt_text,0,sizeof(opt_text)); |
|---|
| 32 | opt_line.exists = false; |
|---|
| 33 | opt_text.exists = false; |
|---|
| 34 | } |
|---|
| 35 | |
|---|
| 36 | |
|---|
| 37 | AW_DEVICE_TYPE AW_device_click::type(void) { |
|---|
| 38 | return AW_DEVICE_CLICK; |
|---|
| 39 | } |
|---|
| 40 | |
|---|
| 41 | |
|---|
| 42 | /***********************************************************************************************************************/ |
|---|
| 43 | /* line text zoomtext box *******************************************************************************************/ |
|---|
| 44 | /***********************************************************************************************************************/ |
|---|
| 45 | |
|---|
| 46 | int AW_device_click::line(int gc, AW_pos x0, AW_pos y0, AW_pos x1, AW_pos y1, AW_bitset filteri, AW_CL clientdata1, AW_CL clientdata2) { |
|---|
| 47 | AW_pos X0,Y0,X1,Y1; // Transformed pos |
|---|
| 48 | AW_pos CX0,CY0,CX1,CY1; // Clipped line |
|---|
| 49 | int drawflag; // is line visible on screen |
|---|
| 50 | AW_pos lx, ly; |
|---|
| 51 | AW_pos dx, dy; |
|---|
| 52 | AW_pos h1, h2; |
|---|
| 53 | AW_pos distance, skalar = 0; |
|---|
| 54 | bool best_line = false; // is this line the best ? |
|---|
| 55 | |
|---|
| 56 | AWUSE(gc); |
|---|
| 57 | if(!(filteri & filter)) return false; |
|---|
| 58 | |
|---|
| 59 | this->transform(x0,y0,X0,Y0); |
|---|
| 60 | this->transform(x1,y1,X1,Y1); |
|---|
| 61 | drawflag = this->clip(X0,Y0,X1,Y1,CX0,CY0,CX1,CY1); |
|---|
| 62 | |
|---|
| 63 | if (drawflag) { |
|---|
| 64 | //stimmen die Kreise um die Punkte ? |
|---|
| 65 | |
|---|
| 66 | |
|---|
| 67 | |
|---|
| 68 | // distance to the second point of the line |
|---|
| 69 | dx = mouse_x - X1; |
|---|
| 70 | dy = mouse_y - Y1; |
|---|
| 71 | distance = (dx*dx) + (dy*dy); |
|---|
| 72 | if (distance < max_distance_line) { |
|---|
| 73 | best_line = true; |
|---|
| 74 | max_distance_line = distance; |
|---|
| 75 | //add more comments |
|---|
| 76 | skalar = 0.0; |
|---|
| 77 | } |
|---|
| 78 | |
|---|
| 79 | // distance to the first point of the line |
|---|
| 80 | dx = mouse_x - X0; |
|---|
| 81 | dy = mouse_y - Y0; |
|---|
| 82 | distance = (dx*dx) + (dy*dy); |
|---|
| 83 | if (distance < max_distance_line) { |
|---|
| 84 | best_line = true; |
|---|
| 85 | max_distance_line = distance; |
|---|
| 86 | //add more comments |
|---|
| 87 | skalar = 1.0; |
|---|
| 88 | } |
|---|
| 89 | |
|---|
| 90 | lx = X1 - X0; |
|---|
| 91 | ly = Y1 - Y0; |
|---|
| 92 | h2 = (lx*lx) + (ly*ly); |
|---|
| 93 | |
|---|
| 94 | // Punkt darf nicht in der Verlaengerung der Linie liegen |
|---|
| 95 | if (h2 > 0.0000000001){ |
|---|
| 96 | skalar = (dx*lx+dy*ly)/h2; |
|---|
| 97 | if ( 0.0 <= skalar && skalar <= 1.0 ) { |
|---|
| 98 | // berechne Trefferpunkt auf Linie |
|---|
| 99 | // distance to the line |
|---|
| 100 | h1 = dx*ly - dy*lx; |
|---|
| 101 | distance = (h1*h1) / h2; |
|---|
| 102 | if (distance < max_distance_line) { |
|---|
| 103 | best_line = true; |
|---|
| 104 | max_distance_line = distance; |
|---|
| 105 | //add more comments |
|---|
| 106 | } |
|---|
| 107 | } |
|---|
| 108 | } |
|---|
| 109 | |
|---|
| 110 | if (best_line == true) { |
|---|
| 111 | aw_assert(x0 == x0); aw_assert(x1 == x1); // not NAN |
|---|
| 112 | aw_assert(y0 == y0); aw_assert(y1 == y1); |
|---|
| 113 | |
|---|
| 114 | opt_line.x0 = x0; |
|---|
| 115 | opt_line.y0 = y0; |
|---|
| 116 | opt_line.x1 = x1; |
|---|
| 117 | opt_line.y1 = y1; |
|---|
| 118 | opt_line.height = distance; |
|---|
| 119 | opt_line.length = skalar; |
|---|
| 120 | opt_line.client_data1 = clientdata1; |
|---|
| 121 | opt_line.client_data2 = clientdata2; |
|---|
| 122 | opt_line.exists = true; |
|---|
| 123 | } |
|---|
| 124 | return true; |
|---|
| 125 | } |
|---|
| 126 | return false; |
|---|
| 127 | } |
|---|
| 128 | |
|---|
| 129 | |
|---|
| 130 | int AW_device_click::text(int gc, const char *str, AW_pos x, AW_pos y, AW_pos alignment, AW_bitset filteri, AW_CL clientdata1, AW_CL clientdata2,long opt_strlen) { |
|---|
| 131 | if(filteri & filter) { |
|---|
| 132 | AW_pos X0,Y0; // Transformed pos |
|---|
| 133 | this->transform(x,y,X0,Y0); |
|---|
| 134 | |
|---|
| 135 | XFontStruct *xfs = &common->gcs[gc]->curfont; |
|---|
| 136 | |
|---|
| 137 | AW_pos Y1 = Y0+(AW_pos)(xfs->max_bounds.descent); |
|---|
| 138 | Y0 = Y0-(AW_pos)(xfs->max_bounds.ascent); |
|---|
| 139 | |
|---|
| 140 | /***************** Fast check text against top bottom clip ***************************/ |
|---|
| 141 | |
|---|
| 142 | if (this->clip_rect.t == 0) { |
|---|
| 143 | if (Y1 < this->clip_rect.t) return 0; |
|---|
| 144 | } |
|---|
| 145 | else { |
|---|
| 146 | if (Y0 < this->clip_rect.t) return 0; |
|---|
| 147 | } |
|---|
| 148 | |
|---|
| 149 | if (this->clip_rect.b == common->screen.b) { |
|---|
| 150 | if (Y0 > this->clip_rect.b) return 0; |
|---|
| 151 | } |
|---|
| 152 | else { |
|---|
| 153 | if (Y1 > this->clip_rect.b) return 0; |
|---|
| 154 | } |
|---|
| 155 | |
|---|
| 156 | /***************** vertical check mouse against textsurrounding ***************************/ |
|---|
| 157 | |
|---|
| 158 | bool exact = true; |
|---|
| 159 | double best_dist = 0; |
|---|
| 160 | |
|---|
| 161 | if (mouse_y > Y1) { // outside text |
|---|
| 162 | if (mouse_y > Y1+max_distance_text) return 0; // too far above |
|---|
| 163 | exact = false; |
|---|
| 164 | best_dist = mouse_y - Y1; |
|---|
| 165 | } |
|---|
| 166 | else if (mouse_y < Y0) { |
|---|
| 167 | if (mouse_y < Y0-max_distance_text) return 0; // too far below |
|---|
| 168 | exact = false; |
|---|
| 169 | best_dist = Y0 - mouse_y; |
|---|
| 170 | } |
|---|
| 171 | |
|---|
| 172 | /***************** align text ***************************/ |
|---|
| 173 | int len = opt_strlen ? opt_strlen : strlen(str); |
|---|
| 174 | int text_width = (int)get_string_size(gc,str,len); |
|---|
| 175 | |
|---|
| 176 | X0 = common->x_alignment(X0,text_width,alignment); |
|---|
| 177 | AW_pos X1 = X0+text_width; |
|---|
| 178 | |
|---|
| 179 | /**************** check against left right clipping areas *********/ |
|---|
| 180 | if (X1 < this->clip_rect.l) return 0; |
|---|
| 181 | if (X0 > this->clip_rect.r) return 0; |
|---|
| 182 | |
|---|
| 183 | if (mouse_x < X0) return 0; // left of text |
|---|
| 184 | if (mouse_x > X1) return 0; // right of text |
|---|
| 185 | |
|---|
| 186 | max_distance_text = best_dist; // exact hit -> distance = 0; |
|---|
| 187 | |
|---|
| 188 | int position; |
|---|
| 189 | if (xfs->max_bounds.width == xfs->min_bounds.width) { // monospaced font |
|---|
| 190 | short letter_width = xfs->max_bounds.width; |
|---|
| 191 | position = (int)((mouse_x-X0)/letter_width); |
|---|
| 192 | if (position<0) position = 0; |
|---|
| 193 | if (position>(len-1)) position = len-1; |
|---|
| 194 | } |
|---|
| 195 | else { // non-monospaced font |
|---|
| 196 | AW_GC_Xm *gcm = AW_MAP_GC(gc); |
|---|
| 197 | position = 0; |
|---|
| 198 | int tmp_offset = 0; |
|---|
| 199 | while (position<=len) { |
|---|
| 200 | tmp_offset += gcm->width_of_chars[(unsigned char)str[position]]; |
|---|
| 201 | if (mouse_x <= X0+tmp_offset) break; |
|---|
| 202 | position++; |
|---|
| 203 | } |
|---|
| 204 | } |
|---|
| 205 | |
|---|
| 206 | AW_pos dist2center = Distance(Position(mouse_x, mouse_y), |
|---|
| 207 | LineVector(X0, Y0, X0+text_width, Y1).centroid()); |
|---|
| 208 | |
|---|
| 209 | if (!opt_text.exists || // first candidate |
|---|
| 210 | (!opt_text.exactHit && exact) || // previous candidate was no exact hit |
|---|
| 211 | (dist2center<opt_text.dist2center)) // distance to text-center is smaller |
|---|
| 212 | { |
|---|
| 213 | opt_text.textArea = Rectangle(rtransform(LineVector(X0, Y0, X1, Y1))); |
|---|
| 214 | opt_text.alignment = alignment; |
|---|
| 215 | opt_text.rotation = 0; |
|---|
| 216 | opt_text.distance = max_distance_text; |
|---|
| 217 | opt_text.dist2center = dist2center; |
|---|
| 218 | opt_text.cursor = position; |
|---|
| 219 | opt_text.client_data1 = clientdata1; |
|---|
| 220 | opt_text.client_data2 = clientdata2; |
|---|
| 221 | opt_text.exists = true; |
|---|
| 222 | opt_text.exactHit = exact; |
|---|
| 223 | } |
|---|
| 224 | } |
|---|
| 225 | return 1; |
|---|
| 226 | } |
|---|
| 227 | |
|---|
| 228 | |
|---|
| 229 | void AW_device_click::get_clicked_line(class AW_clicked_line *ptr) { |
|---|
| 230 | *ptr = opt_line; |
|---|
| 231 | } |
|---|
| 232 | |
|---|
| 233 | |
|---|
| 234 | void AW_device_click::get_clicked_text(class AW_clicked_text *ptr) { |
|---|
| 235 | *ptr = opt_text; |
|---|
| 236 | } |
|---|
| 237 | |
|---|
| 238 | double AW_clicked_line::distanceTo(const AW::Position& pos) { |
|---|
| 239 | AW::LineVector cl(x0, y0, x1, y1); |
|---|
| 240 | if (cl.length() == 0) { |
|---|
| 241 | return AW::Distance(pos, cl.start()); |
|---|
| 242 | } |
|---|
| 243 | return AW::Distance(pos, cl); |
|---|
| 244 | } |
|---|
| 245 | |
|---|
| 246 | bool AW_getBestClick(const AW::Position& click, AW_clicked_line *cl, AW_clicked_text *ct, AW_CL *cd1, AW_CL *cd2) { |
|---|
| 247 | // detect the nearest item next to 'click' |
|---|
| 248 | // and return that items callback params. |
|---|
| 249 | // returns false, if nothing has been clicked |
|---|
| 250 | |
|---|
| 251 | AW_clicked_element *bestClick = 0; |
|---|
| 252 | |
|---|
| 253 | if (cl->exists) { |
|---|
| 254 | if (ct->exists) { |
|---|
| 255 | if (cl->distanceTo(click) < ct->distance) { |
|---|
| 256 | bestClick = cl; |
|---|
| 257 | } |
|---|
| 258 | else { |
|---|
| 259 | bestClick = ct; |
|---|
| 260 | } |
|---|
| 261 | } |
|---|
| 262 | else { |
|---|
| 263 | bestClick = cl; |
|---|
| 264 | } |
|---|
| 265 | } |
|---|
| 266 | else if (ct->exists) { |
|---|
| 267 | bestClick = ct; |
|---|
| 268 | } |
|---|
| 269 | |
|---|
| 270 | if (bestClick) { |
|---|
| 271 | *cd1 = bestClick->client_data1; |
|---|
| 272 | *cd2 = bestClick->client_data2; |
|---|
| 273 | } |
|---|
| 274 | else { |
|---|
| 275 | *cd1 = 0; |
|---|
| 276 | *cd2 = 0; |
|---|
| 277 | } |
|---|
| 278 | |
|---|
| 279 | return bestClick; |
|---|
| 280 | } |
|---|
| 281 | |
|---|
| 282 | |
|---|
| 283 | |
|---|