| 1 | // ================================================================ // |
|---|
| 2 | // // |
|---|
| 3 | // File : aw_device_click.hxx // |
|---|
| 4 | // Purpose : Detect which graphical element is "nearby" // |
|---|
| 5 | // a given mouse position // |
|---|
| 6 | // // |
|---|
| 7 | // Institute of Microbiology (Technical University Munich) // |
|---|
| 8 | // http://www.arb-home.de/ // |
|---|
| 9 | // // |
|---|
| 10 | // ================================================================ // |
|---|
| 11 | |
|---|
| 12 | #ifndef AW_DEVICE_CLICK_HXX |
|---|
| 13 | #define AW_DEVICE_CLICK_HXX |
|---|
| 14 | |
|---|
| 15 | #ifndef AW_DEVICE_HXX |
|---|
| 16 | #include "aw_device.hxx" |
|---|
| 17 | #endif |
|---|
| 18 | |
|---|
| 19 | class AW_clicked_element { |
|---|
| 20 | AW_CL client_data1; |
|---|
| 21 | AW_CL client_data2; |
|---|
| 22 | |
|---|
| 23 | bool exists; // true if a drawn element was clicked, else false |
|---|
| 24 | int distance; // distance in pixel to nearest line/text |
|---|
| 25 | AW_pos nearest_rel_pos; // 0 = at left(upper) small-side, 1 = at right(lower) small-side of textArea or line (does not make sense for box or polygon) |
|---|
| 26 | |
|---|
| 27 | void copy_cds(const AW_click_cd *click_cd) { |
|---|
| 28 | if (click_cd) { |
|---|
| 29 | client_data1 = click_cd->get_cd1(); |
|---|
| 30 | client_data2 = click_cd->get_cd2(); |
|---|
| 31 | } |
|---|
| 32 | else { |
|---|
| 33 | client_data1 = 0; |
|---|
| 34 | client_data2 = 0; |
|---|
| 35 | } |
|---|
| 36 | } |
|---|
| 37 | void set_rel_pos(double rel) { aw_assert(rel >= 0.0 && rel <= 1.0); nearest_rel_pos = rel; } |
|---|
| 38 | |
|---|
| 39 | protected: |
|---|
| 40 | |
|---|
| 41 | AW_clicked_element() |
|---|
| 42 | : client_data1(0), |
|---|
| 43 | client_data2(0), |
|---|
| 44 | exists(false), |
|---|
| 45 | distance(-1), |
|---|
| 46 | nearest_rel_pos(0) |
|---|
| 47 | {} |
|---|
| 48 | |
|---|
| 49 | void assign(int distance_, const AW_pos& nearest_rel_pos_, const AW_click_cd *click_cd_) { |
|---|
| 50 | distance = distance_; |
|---|
| 51 | set_rel_pos(nearest_rel_pos_); |
|---|
| 52 | copy_cds(click_cd_); |
|---|
| 53 | exists = true; |
|---|
| 54 | } |
|---|
| 55 | |
|---|
| 56 | public: |
|---|
| 57 | virtual ~AW_clicked_element() {} |
|---|
| 58 | |
|---|
| 59 | AW_CL cd1() const { return client_data1; } |
|---|
| 60 | AW_CL cd2() const { return client_data2; } |
|---|
| 61 | |
|---|
| 62 | virtual AW::Position get_attach_point() const = 0; |
|---|
| 63 | virtual AW::Rectangle get_bounding_box() const = 0; |
|---|
| 64 | virtual AW_clicked_element *clone() const = 0; |
|---|
| 65 | |
|---|
| 66 | bool does_exist() const { return exists; } |
|---|
| 67 | |
|---|
| 68 | inline bool is_text() const; |
|---|
| 69 | inline bool is_line() const; |
|---|
| 70 | inline bool is_box() const; |
|---|
| 71 | inline bool is_polygon() const; |
|---|
| 72 | |
|---|
| 73 | double get_rel_pos() const { return nearest_rel_pos; } |
|---|
| 74 | int get_distance() const { return distance; } |
|---|
| 75 | AW::LineVector get_connecting_line(const AW_clicked_element& other) const { |
|---|
| 76 | //! determine LineVector between two clicked elements (e.g. for drag&drop) |
|---|
| 77 | return AW::LineVector(get_attach_point(), other.get_attach_point()); |
|---|
| 78 | } |
|---|
| 79 | |
|---|
| 80 | virtual bool operator==(const AW_clicked_element& other) const = 0; |
|---|
| 81 | virtual int indicate_selected(AW_device *d, int gc) const = 0; |
|---|
| 82 | }; |
|---|
| 83 | |
|---|
| 84 | class AW_clicked_line : public AW_clicked_element { |
|---|
| 85 | AW::LineVector line; // world coordinates of clicked line |
|---|
| 86 | public: |
|---|
| 87 | void assign(const AW::LineVector& line_, int distance_, const AW_pos& nearest_rel_pos_, const AW_click_cd *click_cd_) { |
|---|
| 88 | AW_clicked_element::assign(distance_, nearest_rel_pos_, click_cd_); |
|---|
| 89 | line = line_; |
|---|
| 90 | } |
|---|
| 91 | |
|---|
| 92 | bool operator == (const AW_clicked_element& other) const OVERRIDE { |
|---|
| 93 | const AW_clicked_line *otherLine = dynamic_cast<const AW_clicked_line*>(&other); |
|---|
| 94 | return otherLine ? nearlyEqual(get_line(), otherLine->get_line()) : false; |
|---|
| 95 | } |
|---|
| 96 | |
|---|
| 97 | AW::Position get_attach_point() const OVERRIDE { |
|---|
| 98 | double nrp = get_rel_pos(); |
|---|
| 99 | return line.start() + nrp*line.line_vector(); |
|---|
| 100 | } |
|---|
| 101 | AW::Rectangle get_bounding_box() const OVERRIDE { return AW::Rectangle(line); } |
|---|
| 102 | const AW::LineVector& get_line() const { return line; } |
|---|
| 103 | |
|---|
| 104 | int indicate_selected(AW_device *d, int gc) const OVERRIDE; |
|---|
| 105 | AW_clicked_element *clone() const OVERRIDE { return new AW_clicked_line(*this); } |
|---|
| 106 | }; |
|---|
| 107 | |
|---|
| 108 | class AW_clicked_text : public AW_clicked_element { |
|---|
| 109 | AW::Rectangle textArea; // world coordinates of clicked text |
|---|
| 110 | public: |
|---|
| 111 | void assign(AW::Rectangle textArea_, int distance_, const AW_pos& nearest_rel_pos_, const AW_click_cd *click_cd_) { |
|---|
| 112 | AW_clicked_element::assign(distance_, nearest_rel_pos_, click_cd_); |
|---|
| 113 | textArea = textArea_; |
|---|
| 114 | } |
|---|
| 115 | |
|---|
| 116 | bool operator == (const AW_clicked_element& other) const OVERRIDE { |
|---|
| 117 | const AW_clicked_text *otherText = dynamic_cast<const AW_clicked_text*>(&other); |
|---|
| 118 | return otherText ? nearlyEqual(textArea, otherText->textArea) : false; |
|---|
| 119 | } |
|---|
| 120 | |
|---|
| 121 | AW::Position get_attach_point() const OVERRIDE { return textArea.centroid(); } |
|---|
| 122 | AW::Rectangle get_bounding_box() const OVERRIDE { return textArea; } |
|---|
| 123 | |
|---|
| 124 | int indicate_selected(AW_device *d, int gc) const OVERRIDE; |
|---|
| 125 | AW_clicked_element *clone() const OVERRIDE { return new AW_clicked_text(*this); } |
|---|
| 126 | }; |
|---|
| 127 | |
|---|
| 128 | class AW_clicked_box : public AW_clicked_element { |
|---|
| 129 | AW::Rectangle box; // world coordinates of clicked box |
|---|
| 130 | public: |
|---|
| 131 | void assign(AW::Rectangle box_, int distance_, const AW_pos& nearest_rel_pos_, const AW_click_cd *click_cd_) { |
|---|
| 132 | AW_clicked_element::assign(distance_, nearest_rel_pos_, click_cd_); |
|---|
| 133 | box = box_; |
|---|
| 134 | } |
|---|
| 135 | |
|---|
| 136 | bool operator == (const AW_clicked_element& other) const OVERRIDE { |
|---|
| 137 | const AW_clicked_box *otherBox = dynamic_cast<const AW_clicked_box*>(&other); |
|---|
| 138 | return otherBox ? nearlyEqual(box, otherBox->box) : false; |
|---|
| 139 | } |
|---|
| 140 | |
|---|
| 141 | AW::Position get_attach_point() const OVERRIDE { return box.centroid(); } |
|---|
| 142 | AW::Rectangle get_bounding_box() const OVERRIDE { return box; } |
|---|
| 143 | int indicate_selected(AW_device *d, int gc) const OVERRIDE; |
|---|
| 144 | AW_clicked_element *clone() const OVERRIDE { return new AW_clicked_box(*this); } |
|---|
| 145 | }; |
|---|
| 146 | |
|---|
| 147 | class AW_clicked_polygon : public AW_clicked_element { |
|---|
| 148 | int npos; // number of corners |
|---|
| 149 | AW::Position *pos; // world coordinates of clicked polygon |
|---|
| 150 | |
|---|
| 151 | public: |
|---|
| 152 | AW_clicked_polygon() |
|---|
| 153 | : npos(0), |
|---|
| 154 | pos(NULL) |
|---|
| 155 | {} |
|---|
| 156 | AW_clicked_polygon(const AW_clicked_polygon& other) |
|---|
| 157 | : AW_clicked_element(other), |
|---|
| 158 | npos(other.npos) |
|---|
| 159 | { |
|---|
| 160 | if (other.pos) { |
|---|
| 161 | pos = new AW::Position[npos]; |
|---|
| 162 | for (int i = 0; i<npos; ++i) pos[i] = other.pos[i]; |
|---|
| 163 | } |
|---|
| 164 | else { |
|---|
| 165 | pos = NULL; |
|---|
| 166 | } |
|---|
| 167 | } |
|---|
| 168 | DECLARE_ASSIGNMENT_OPERATOR(AW_clicked_polygon); |
|---|
| 169 | ~AW_clicked_polygon() { |
|---|
| 170 | delete [] pos; |
|---|
| 171 | } |
|---|
| 172 | |
|---|
| 173 | void assign(int npos_, const AW::Position *pos_, int distance_, const AW_pos& nearest_rel_pos_, const AW_click_cd *click_cd_) { |
|---|
| 174 | if (pos) delete [] pos; |
|---|
| 175 | |
|---|
| 176 | AW_clicked_element::assign(distance_, nearest_rel_pos_, click_cd_); |
|---|
| 177 | |
|---|
| 178 | npos = npos_; |
|---|
| 179 | pos = new AW::Position[npos]; |
|---|
| 180 | for (int i = 0; i<npos; ++i) pos[i] = pos_[i]; |
|---|
| 181 | } |
|---|
| 182 | |
|---|
| 183 | bool operator == (const AW_clicked_element& other) const OVERRIDE { |
|---|
| 184 | const AW_clicked_polygon *otherPoly = dynamic_cast<const AW_clicked_polygon*>(&other); |
|---|
| 185 | if (otherPoly) { |
|---|
| 186 | if (npos == otherPoly->npos) { |
|---|
| 187 | for (int i = 0; i<npos; ++i) { |
|---|
| 188 | if (!nearlyEqual(pos[i], otherPoly->pos[i])) { |
|---|
| 189 | return false; |
|---|
| 190 | } |
|---|
| 191 | } |
|---|
| 192 | return true; |
|---|
| 193 | } |
|---|
| 194 | } |
|---|
| 195 | return false; |
|---|
| 196 | } |
|---|
| 197 | |
|---|
| 198 | AW::Position get_attach_point() const OVERRIDE { |
|---|
| 199 | AW::Position c = pos[0]; |
|---|
| 200 | for (int i = 1; i<npos; ++i) { |
|---|
| 201 | c += AW::Vector(pos[i]); |
|---|
| 202 | } |
|---|
| 203 | return AW::Position(c.xpos()/npos, c.ypos()/npos); |
|---|
| 204 | } |
|---|
| 205 | AW::Rectangle get_bounding_box() const OVERRIDE; |
|---|
| 206 | int indicate_selected(AW_device *d, int gc) const OVERRIDE; |
|---|
| 207 | AW_clicked_element *clone() const OVERRIDE { |
|---|
| 208 | return new AW_clicked_polygon(*this); |
|---|
| 209 | } |
|---|
| 210 | |
|---|
| 211 | const AW::Position *get_polygon(int& posCount) const { |
|---|
| 212 | aw_assert(does_exist()); |
|---|
| 213 | posCount = npos; |
|---|
| 214 | return pos; |
|---|
| 215 | } |
|---|
| 216 | }; |
|---|
| 217 | |
|---|
| 218 | |
|---|
| 219 | // --------------------- |
|---|
| 220 | // type checks |
|---|
| 221 | |
|---|
| 222 | inline bool AW_clicked_element::is_text() const { return dynamic_cast<const AW_clicked_text*>(this); } |
|---|
| 223 | inline bool AW_clicked_element::is_line() const { return dynamic_cast<const AW_clicked_line*>(this); } |
|---|
| 224 | inline bool AW_clicked_element::is_box() const { return dynamic_cast<const AW_clicked_box*>(this); } |
|---|
| 225 | inline bool AW_clicked_element::is_polygon() const { return dynamic_cast<const AW_clicked_polygon*>(this); } |
|---|
| 226 | |
|---|
| 227 | #define AWT_CATCH 30 // max-pixel distance to graphical element (to accept a click or command) |
|---|
| 228 | #define AWT_NO_CATCH -1 |
|---|
| 229 | |
|---|
| 230 | class AW_device_click : public AW_simple_device { |
|---|
| 231 | AW::Position mouse; |
|---|
| 232 | |
|---|
| 233 | int max_distance_line; |
|---|
| 234 | int max_distance_text; |
|---|
| 235 | |
|---|
| 236 | AW_clicked_line opt_line; |
|---|
| 237 | AW_clicked_text opt_text; |
|---|
| 238 | AW_clicked_box opt_box; |
|---|
| 239 | AW_clicked_polygon opt_polygon; |
|---|
| 240 | |
|---|
| 241 | bool line_impl(int gc, const AW::LineVector& Line, AW_bitset filteri) OVERRIDE; |
|---|
| 242 | bool text_impl(int gc, const char *str, const AW::Position& pos, AW_pos alignment, AW_bitset filteri, long opt_strlen) OVERRIDE; |
|---|
| 243 | bool box_impl(int gc, AW::FillStyle filled, const AW::Rectangle& rect, AW_bitset filteri) OVERRIDE; |
|---|
| 244 | bool polygon_impl(int gc, AW::FillStyle filled, int npos, const AW::Position *pos, AW_bitset filteri) OVERRIDE; |
|---|
| 245 | |
|---|
| 246 | // completely ignore clicks to circles and arcs |
|---|
| 247 | bool circle_impl(int, AW::FillStyle, const AW::Position&, const AW::Vector&, AW_bitset) OVERRIDE { return false; } |
|---|
| 248 | bool arc_impl(int, AW::FillStyle, const AW::Position&, const AW::Vector&, int, int, AW_bitset) OVERRIDE { return false; } |
|---|
| 249 | |
|---|
| 250 | bool invisible_impl(const AW::Position& pos, AW_bitset filteri) OVERRIDE { return generic_invisible(pos, filteri); } |
|---|
| 251 | void specific_reset() OVERRIDE {} |
|---|
| 252 | |
|---|
| 253 | public: |
|---|
| 254 | AW_device_click(AW_common *common_); |
|---|
| 255 | |
|---|
| 256 | AW_DEVICE_TYPE type() OVERRIDE; |
|---|
| 257 | |
|---|
| 258 | void init_click(const AW::Position& click, int max_distance, AW_bitset filteri); |
|---|
| 259 | |
|---|
| 260 | enum ClickPreference { PREFER_NEARER, PREFER_LINE, PREFER_TEXT }; |
|---|
| 261 | const AW_clicked_element *best_click(ClickPreference prefer = PREFER_NEARER); |
|---|
| 262 | }; |
|---|
| 263 | |
|---|
| 264 | #else |
|---|
| 265 | #error aw_device_click.hxx included twice |
|---|
| 266 | #endif // AW_DEVICE_CLICK_HXX |
|---|