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 FINAL_TYPE : 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(NULp) |
---|
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 = NULp; |
---|
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 FINAL_TYPE : 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 SizedCstr& cstr, const AW::Position& pos, AW_pos alignment, AW_bitset filteri) 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 |
---|