Tesseract 3.02

- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files

- File List
- File Members

All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
scrollview.cpp
Go to the documentation of this file.
1
2 // File: scrollview.cc
3 // Description: ScrollView
4 // Author: Joern Wanke
5 // Created: Thu Nov 29 2007
6 //
7 // (C) Copyright 2007, Google Inc.
8 // Licensed under the Apache License, Version 2.0 (the "License");
9 // you may not use this file except in compliance with the License.
10 // You may obtain a copy of the License at
11 // http://www.apache.org/licenses/LICENSE-2.0
12 // Unless required by applicable law or agreed to in writing, software
13 // distributed under the License is distributed on an "AS IS" BASIS,
14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 // See the License for the specific language governing permissions and
16 // limitations under the License.
17 //
19 //
20
21 #include <stdarg.h>
22 #include <limits.h>
23 #include <string.h>
24 #include <map>
25 #include <utility>
26 #include <algorithm>
27 #include <vector>
28 #include <string>
29 #include <cstring>
30 #include <climits>
31
32 // Include automatically generated configuration file if running autoconf.
33 #ifdef HAVE_CONFIG_H
34 #include "config_auto.h"
35 #endif
36
37 #include "scrollview.h"
38
39 #ifdef _MSC_VER
40 #pragma warning(disable:4786) // Don't give stupid warnings for stl
41 #pragma warning(disable:4018) // signed/unsigned warnings
42 #pragma warning(disable:4530) // exception warnings
43 #endif
44
45 const int kSvPort = 8461;
46 const int kMaxMsgSize = 4096;
47 const int kMaxIntPairSize = 45; // Holds %d,%d, for upto 64 bit.
48
49 #include "svutil.h"
50
51 #include "allheaders.h"
52
53 struct SVPolyLineBuffer {
54 bool empty; // Independent indicator to allow SendMsg to call SendPolygon.
55 std::vector<int> xcoords;
56 std::vector<int> ycoords;
57 };
58
59 // A map between the window IDs and their corresponding pointers.
60 static std::map<int, ScrollView*> svmap;
61 static SVMutex* svmap_mu;
62 // A map of all semaphores waiting for a specific event on a specific window.
63 static std::map<std::pair<ScrollView*, SVEventType>,
64 std::pair<SVSemaphore*, SVEvent*> > waiting_for_events;
65 static SVMutex* waiting_for_events_mu;
66
67 SVEvent* SVEvent::copy() {
68 SVEvent* any = new SVEvent;
69 any->command_id = command_id;
70 any->counter = counter;
71 any->parameter = new char[strlen(parameter) + 1];
72 strncpy(any->parameter, parameter, strlen(parameter));
73 any->parameter[strlen(parameter)] = '\0';
74 any->type = type;
75 any->x = x;
76 any->y = y;
77 any->x_size = x_size;
78 any->y_size = y_size;
79 any->window = window;
80 return any;
81 }
82
83 #ifndef GRAPHICS_DISABLED
84
85
86
87
88 void* ScrollView::MessageReceiver(void* a) {
89 int counter_event_id = 0; // ongoing counter
90 char* message = NULL;
91 // Wait until a new message appears in the input stream_.
92 do {
93 message = ScrollView::GetStream()->Receive();
94 } while (message == NULL);
95
96 // This is the main loop which iterates until the server is dead (strlen = -1).
97 // It basically parses for 3 different messagetypes and then distributes the
98 // events accordingly.
99 while (strlen(message) != -1) {
100 // The new event we create.
101 SVEvent* cur = new SVEvent;
102 // The ID of the corresponding window.
103 int window_id;
104
105 int ev_type;
106
107 int n;
108 // Fill the new SVEvent properly.
109 sscanf(message, "%d,%d,%d,%d,%d,%d,%d,%n", &window_id, &ev_type, &cur->x,
110 &cur->y, &cur->x_size, &cur->y_size, &cur->command_id, &n);
111 char* p = (message + n);
112
113 svmap_mu->Lock();
114 cur->window = svmap[window_id];
115
116 if (cur->window != NULL) {
117 cur->parameter = new char[strlen(p) + 1];
118 strncpy(cur->parameter, p, strlen(p) + 1);
119 if (strlen(p) > 0) { // remove the last \n
120 cur->parameter[strlen(p)] = '\0';
121 }
122 cur->type = static_cast< SVEventType >(ev_type);
123 // Correct selection coordinates so x,y is the min pt and size is +ve.
124 if (cur->x_size > 0)
125 cur->x -= cur->x_size;
126 else
127 cur->x_size = -cur->x_size;
128 if (cur->y_size > 0)
129 cur->y -= cur->y_size;
130 else
131 cur->y_size = -cur->y_size;
132 // Returned y will be the bottom-left if y is reversed.
133 if (cur->window->y_axis_is_reversed_)
134 cur->y = cur->window->TranslateYCoordinate(cur->y + cur->y_size);
135 cur->counter = counter_event_id;
136 // Increase by 2 since we will also create an SVET_ANY event from cur,
137 // which will have a counter_id of cur + 1 (and thus gets processed
138 // after cur).
139 counter_event_id += 2;
140
141 // In case of an SVET_EXIT event, quit the whole application.
142 if (ev_type == SVET_EXIT) { ScrollView::Exit(); }
143
144 // Place two copies of it in the table for the window.
145 cur->window->SetEvent(cur);
146
147 // Check if any of the threads currently waiting want it.
148 std::pair<ScrollView*, SVEventType> awaiting_list(cur->window,
149 cur->type);
150 std::pair<ScrollView*, SVEventType> awaiting_list_any(cur->window,
151 SVET_ANY);
152 std::pair<ScrollView*, SVEventType> awaiting_list_any_window((ScrollView*)0,
153 SVET_ANY);
154 waiting_for_events_mu->Lock();
155 if (waiting_for_events.count(awaiting_list) > 0) {
156 waiting_for_events[awaiting_list].second = cur;
157 waiting_for_events[awaiting_list].first->Signal();
158 } else if (waiting_for_events.count(awaiting_list_any) > 0) {
159 waiting_for_events[awaiting_list_any].second = cur;
160 waiting_for_events[awaiting_list_any].first->Signal();
161 } else if (waiting_for_events.count(awaiting_list_any_window) > 0) {
162 waiting_for_events[awaiting_list_any_window].second = cur;
163 waiting_for_events[awaiting_list_any_window].first->Signal();
164 } else {
165 // No one wanted it, so delete it.
166 delete cur;
167 }
168 waiting_for_events_mu->Unlock();
169 // Signal the corresponding semaphore twice (for both copies).
170 ScrollView* sv = svmap[window_id];
171 if (sv != NULL) {
172 sv->Signal();
173 sv->Signal();
174 }
175 }
176 svmap_mu->Unlock();
177
178 // Wait until a new message appears in the input stream_.
179 do {
180 message = ScrollView::GetStream()->Receive();
181 } while (message == NULL);
182 }
183 return 0;
184 }
185
186 // Table to implement the color index values in the old system.
187 int table_colors[ScrollView::GREEN_YELLOW+1][4]= {
188 {0, 0, 0, 0}, // NONE (transparent)
189 {0, 0, 0, 255}, // BLACK.
190 {255, 255, 255, 255}, // WHITE.
191 {255, 0, 0, 255}, // RED.
192 {255, 255, 0, 255}, // YELLOW.
193 {0, 255, 0, 255}, // GREEN.
194 {0, 255, 255, 255}, // CYAN.
195 {0, 0, 255, 255}, // BLUE.
196 {255, 0, 255, 255}, // MAGENTA.
197 {0, 128, 255, 255}, // AQUAMARINE.
198 {0, 0, 64, 255}, // DARK_SLATE_BLUE.
199 {128, 128, 255, 255}, // LIGHT_BLUE.
200 {64, 64, 255, 255}, // MEDIUM_BLUE.
201 {0, 0, 32, 255}, // MIDNIGHT_BLUE.
202 {0, 0, 128, 255}, // NAVY_BLUE.
203 {192, 192, 255, 255}, // SKY_BLUE.
204 {64, 64, 128, 255}, // SLATE_BLUE.
205 {32, 32, 64, 255}, // STEEL_BLUE.
206 {255, 128, 128, 255}, // CORAL.
207 {128, 64, 0, 255}, // BROWN.
208 {128, 128, 0, 255}, // SANDY_BROWN.
209 {192, 192, 0, 255}, // GOLD.
210 {192, 192, 128, 255}, // GOLDENROD.
211 {0, 64, 0, 255}, // DARK_GREEN.
212 {32, 64, 0, 255}, // DARK_OLIVE_GREEN.
213 {64, 128, 0, 255}, // FOREST_GREEN.
214 {128, 255, 0, 255}, // LIME_GREEN.
215 {192, 255, 192, 255}, // PALE_GREEN.
216 {192, 255, 0, 255}, // YELLOW_GREEN.
217 {192, 192, 192, 255}, // LIGHT_GREY.
218 {64, 64, 128, 255}, // DARK_SLATE_GREY.
219 {64, 64, 64, 255}, // DIM_GREY.
220 {128, 128, 128, 255}, // GREY.
221 {64, 192, 0, 255}, // KHAKI.
222 {255, 0, 192, 255}, // MAROON.
223 {255, 128, 0, 255}, // ORANGE.
224 {255, 128, 64, 255}, // ORCHID.
225 {255, 192, 192, 255}, // PINK.
226 {128, 0, 128, 255}, // PLUM.
227 {255, 0, 64, 255}, // INDIAN_RED.
228 {255, 64, 0, 255}, // ORANGE_RED.
229 {255, 0, 192, 255}, // VIOLET_RED.
230 {255, 192, 128, 255}, // SALMON.
231 {128, 128, 0, 255}, // TAN.
232 {0, 255, 255, 255}, // TURQUOISE.
233 {0, 128, 128, 255}, // DARK_TURQUOISE.
234 {192, 0, 255, 255}, // VIOLET.
235 {128, 128, 0, 255}, // WHEAT.
236 {128, 255, 0, 255} // GREEN_YELLOW
237 };
238
239
240 /*******************************************************************************
241 * Scrollview implementation.
242 *******************************************************************************/
243
244 SVNetwork* ScrollView::stream_ = NULL;
245 int ScrollView::nr_created_windows_ = 0;
246 int ScrollView::image_index_ = 0;
247
249 ScrollView::ScrollView(const char* name, int x_pos, int y_pos, int x_size,
250 int y_size, int x_canvas_size, int y_canvas_size,
251 bool y_axis_reversed, const char* server_name) {
252 Initialize(name, x_pos, y_pos, x_size, y_size, x_canvas_size, y_canvas_size,
253 y_axis_reversed, server_name);}
254
256 ScrollView::ScrollView(const char* name, int x_pos, int y_pos, int x_size,
257 int y_size, int x_canvas_size, int y_canvas_size,
258 bool y_axis_reversed) {
259 Initialize(name, x_pos, y_pos, x_size, y_size, x_canvas_size, y_canvas_size,
260 y_axis_reversed, "localhost");
261 }
262
264 ScrollView::ScrollView(const char* name, int x_pos, int y_pos, int x_size,
265 int y_size, int x_canvas_size, int y_canvas_size) {
266 Initialize(name, x_pos, y_pos, x_size, y_size, x_canvas_size, y_canvas_size,
267 false, "localhost");
268 }
269
271 void ScrollView::Initialize(const char* name, int x_pos, int y_pos, int x_size,
272 int y_size, int x_canvas_size, int y_canvas_size,
273 bool y_axis_reversed, const char* server_name) {
274 // If this is the first ScrollView Window which gets created, there is no
275 // network connection yet and we have to set it up in a different thread.
276 if (stream_ == NULL) {
277 nr_created_windows_ = 0;
278 stream_ = new SVNetwork(server_name, kSvPort);
279 waiting_for_events_mu = new SVMutex();
280 svmap_mu = new SVMutex();
281 SendRawMessage(
282 "svmain = luajava.bindClass('com.google.scrollview.ScrollView')\n");
283 SVSync::StartThread(MessageReceiver, NULL);
284 }
285
286 // Set up the variables on the clientside.
287 nr_created_windows_++;
288 event_handler_ = NULL;
289 event_handler_ended_ = false;
290 y_axis_is_reversed_ = y_axis_reversed;
291 y_size_ = y_canvas_size;
292 window_name_ = name;
293 window_id_ = nr_created_windows_;
294 // Set up polygon buffering.
295 points_ = new SVPolyLineBuffer;
296 points_->empty = true;
297
298 svmap_mu->Lock();
299 svmap[window_id_] = this;
300 svmap_mu->Unlock();
301
302 for (int i = 0; i < SVET_COUNT; i++) {
303 event_table_[i] = NULL;
304 }
305
306 mutex_ = new SVMutex();
307 semaphore_ = new SVSemaphore();
308
309 // Set up an actual Window on the client side.
310 char message[kMaxMsgSize];
311 snprintf(message, sizeof(message),
312 "w%u = luajava.newInstance('com.google.scrollview.ui"
313 ".SVWindow','%s',%u,%u,%u,%u,%u,%u,%u)\n",
314 window_id_, window_name_, window_id_,
315 x_pos, y_pos, x_size, y_size, x_canvas_size, y_canvas_size);
316 SendRawMessage(message);
317
318 SVSync::StartThread(StartEventHandler, this);
319 }
320
322 void* ScrollView::StartEventHandler(void* a) {
323 ScrollView* sv = reinterpret_cast< ScrollView*>(a);
324 SVEvent* new_event;
325
326 do {
327 stream_->Flush();
328 sv->semaphore_->Wait();
329 new_event = NULL;
330 int serial = -1;
331 int k = -1;
332 sv->mutex_->Lock();
333 // Check every table entry if he is is valid and not already processed.
334
335 for (int i = 0; i < SVET_COUNT; i++) {
336 if (sv->event_table_[i] != NULL &&
337 (serial < 0 || sv->event_table_[i]->counter < serial)) {
338 new_event = sv->event_table_[i];
339 serial = sv->event_table_[i]->counter;
340 k = i;
341 }
342 }
343 // If we didnt find anything we had an old alarm and just sleep again.
344 if (new_event != NULL) {
345 sv->event_table_[k] = NULL;
346 sv->mutex_->Unlock();
347 if (sv->event_handler_ != NULL) { sv->event_handler_->Notify(new_event); }
348 if (new_event->type == SVET_DESTROY) {
349 // Signal the destructor that it is safe to terminate.
350 sv->event_handler_ended_ = true;
351 sv = NULL;
352 }
353 delete new_event; // Delete the pointer after it has been processed.
354 } else { sv->mutex_->Unlock(); }
355 // The thread should run as long as its associated window is alive.
356 } while (sv != NULL);
357 return 0;
358 }
359 #endif // GRAPHICS_DISABLED
360
361 ScrollView::~ScrollView() {
362 #ifndef GRAPHICS_DISABLED
363 svmap_mu->Lock();
364 if (svmap[window_id_] != NULL) {
365 svmap_mu->Unlock();
366 // So the event handling thread can quit.
367 SendMsg("destroy()");
368
369 SVEvent* sve = AwaitEvent(SVET_DESTROY);
370 delete sve;
371 svmap_mu->Lock();
372 svmap[window_id_] = NULL;
373 svmap_mu->Unlock();
374 // The event handler thread for this window *must* receive the
375 // destroy event and set its pointer to this to NULL before we allow
376 // the destructor to exit.
377 while (!event_handler_ended_)
378 Update();
379 } else {
380 svmap_mu->Unlock();
381 }
382 delete mutex_;
383 delete semaphore_;
384 delete points_;
385 #endif // GRAPHICS_DISABLED
386 }
387
388 #ifndef GRAPHICS_DISABLED
389
390 void ScrollView::SendMsg(const char* format, ...) {
391 if (!points_->empty)
392 SendPolygon();
393 va_list args;
394 char message[kMaxMsgSize];
395
396 va_start(args, format); // variable list
397 vsnprintf(message, kMaxMsgSize, format, args);
398 va_end(args);
399
400 char form[kMaxMsgSize];
401 snprintf(form, kMaxMsgSize, "w%u:%s\n", window_id_, message);
402
403 stream_->Send(form);
404 }
405
408 void ScrollView::SendRawMessage(const char* msg) {
409 stream_->Send(msg);
410 }
411
413 void ScrollView::AddEventHandler(SVEventHandler* listener) {
414 event_handler_ = listener;
415 }
416
417 void ScrollView::Signal() {
418 semaphore_->Signal();
419 }
420
421 void ScrollView::SetEvent(SVEvent* svevent) {
422 // Copy event
423 SVEvent* any = svevent->copy();
424 SVEvent* specific = svevent->copy();
425 any->counter = specific->counter + 1;
426
427 // Place both events into the queue.
428 mutex_->Lock();
429 // Delete the old objects..
430 if (event_table_[specific->type] != NULL) {
431 delete event_table_[specific->type]; }
432 if (event_table_[SVET_ANY] != NULL) {
433 delete event_table_[SVET_ANY]; }
434 // ...and put the new ones in the table.
435 event_table_[specific->type] = specific;
436 event_table_[SVET_ANY] = any;
437 mutex_->Unlock();
438 }
439
440
444 SVEvent* ScrollView::AwaitEvent(SVEventType type) {
445 // Initialize the waiting semaphore.
446 SVSemaphore* sem = new SVSemaphore();
447 std::pair<ScrollView*, SVEventType> ea(this, type);
448 waiting_for_events_mu->Lock();
449 waiting_for_events[ea] = std::pair<SVSemaphore*, SVEvent*> (sem, (SVEvent*)0);
450 waiting_for_events_mu->Unlock();
451 // Wait on it, but first flush.
452 stream_->Flush();
453 sem->Wait();
454 // Process the event we got woken up for (its in waiting_for_events pair).
455 waiting_for_events_mu->Lock();
456 SVEvent* ret = waiting_for_events[ea].second;
457 waiting_for_events.erase(ea);
458 delete sem;
459 waiting_for_events_mu->Unlock();
460 return ret;
461 }
462
463 // Block until any event on any window is received.
464 // No event is returned here!
465 SVEvent* ScrollView::AwaitEventAnyWindow() {
466 // Initialize the waiting semaphore.
467 SVSemaphore* sem = new SVSemaphore();
468 std::pair<ScrollView*, SVEventType> ea((ScrollView*)0, SVET_ANY);
469 waiting_for_events_mu->Lock();
470 waiting_for_events[ea] = std::pair<SVSemaphore*, SVEvent*> (sem, (SVEvent*)0);
471 waiting_for_events_mu->Unlock();
472 // Wait on it.
473 stream_->Flush();
474 sem->Wait();
475 // Process the event we got woken up for (its in waiting_for_events pair).
476 waiting_for_events_mu->Lock();
477 SVEvent* ret = waiting_for_events[ea].second;
478 waiting_for_events.erase(ea);
479 waiting_for_events_mu->Unlock();
480 return ret;
481 }
482
483 // Send the current buffered polygon (if any) and clear it.
484 void ScrollView::SendPolygon() {
485 if (!points_->empty) {
486 points_->empty = true; // Allows us to use SendMsg.
487 int length = points_->xcoords.size();
488 // length == 1 corresponds to 2 SetCursors in a row and only the
489 // last setCursor has any effect.
490 if (length == 2) {
491 // An isolated line!
492 SendMsg("drawLine(%d,%d,%d,%d)",
493 points_->xcoords[0], points_->ycoords[0],
494 points_->xcoords[1], points_->ycoords[1]);
495 } else if (length > 2) {
496 // A polyline.
497 SendMsg("createPolyline(%d)", length);
498 char coordpair[kMaxIntPairSize];
499 std::string decimal_coords;
500 for (int i = 0; i < length; ++i) {
501 snprintf(coordpair, kMaxIntPairSize, "%d,%d,",
502 points_->xcoords[i], points_->ycoords[i]);
503 decimal_coords += coordpair;
504 }
505 decimal_coords += '\n';
506 SendRawMessage(decimal_coords.c_str());
507 SendMsg("drawPolyline()");
508 }
509 points_->xcoords.clear();
510 points_->ycoords.clear();
511 }
512 }
513
514
515 /*******************************************************************************
516 * LUA "API" functions.
517 *******************************************************************************/
518
519 // Sets the position from which to draw to (x,y).
520 void ScrollView::SetCursor(int x, int y) {
521 SendPolygon();
522 DrawTo(x, y);
523 }
524
525 // Draws from the current position to (x,y) and sets the new position to it.
526 void ScrollView::DrawTo(int x, int y) {
527 points_->xcoords.push_back(x);
528 points_->ycoords.push_back(TranslateYCoordinate(y));
529 points_->empty = false;
530 }
531
532 // Draw a line using the current pen color.
533 void ScrollView::Line(int x1, int y1, int x2, int y2) {
534 if (!points_->xcoords.empty() && x1 == points_->xcoords.back() &&
535 TranslateYCoordinate(y1) == points_->ycoords.back()) {
536 // We are already at x1, y1, so just draw to x2, y2.
537 DrawTo(x2, y2);
538 } else if (!points_->xcoords.empty() && x2 == points_->xcoords.back() &&
539 TranslateYCoordinate(y2) == points_->ycoords.back()) {
540 // We are already at x2, y2, so just draw to x1, y1.
541 DrawTo(x1, y1);
542 } else {
543 // This is a new line.
544 SetCursor(x1, y1);
545 DrawTo(x2, y2);
546 }
547 }
548
549 // Set the visibility of the window.
550 void ScrollView::SetVisible(bool visible) {
551 if (visible) { SendMsg("setVisible(true)");
552 } else { SendMsg("setVisible(false)"); }
553 }
554
555 // Set the alwaysOnTop flag.
556 void ScrollView::AlwaysOnTop(bool b) {
557 if (b) { SendMsg("setAlwaysOnTop(true)");
558 } else { SendMsg("setAlwaysOnTop(false)"); }
559 }
560
561 // Adds a message entry to the message box.
562 void ScrollView::AddMessage(const char* format, ...) {
563 va_list args;
564 char message[kMaxMsgSize];
565 char form[kMaxMsgSize];
566
567 va_start(args, format); // variable list
568 vsnprintf(message, kMaxMsgSize, format, args);
569 va_end(args);
570
571 snprintf(form, kMaxMsgSize, "w%u:%s", window_id_, message);
572
573 char* esc = AddEscapeChars(form);
574 SendMsg("addMessage(\"%s\")", esc);
575 delete[] esc;
576 }
577
578 // Set a messagebox.
579 void ScrollView::AddMessageBox() {
580 SendMsg("addMessageBox()");
581 }
582
583 // Exit the client completely (and notify the server of it).
584 void ScrollView::Exit() {
585 SendRawMessage("svmain:exit()");
586 exit(0);
587 }
588
589 // Clear the canvas.
590 void ScrollView::Clear() {
591 SendMsg("clear()");
592 }
593
594 // Set the stroke width.
595 void ScrollView::Stroke(float width) {
596 SendMsg("setStrokeWidth(%f)", width);
597 }
598
599 // Draw a rectangle using the current pen color.
600 // The rectangle is filled with the current brush color.
601 void ScrollView::Rectangle(int x1, int y1, int x2, int y2) {
602 if (x1 == x2 && y1 == y2)
603 return; // Scrollviewer locks up.
604 SendMsg("drawRectangle(%d,%d,%d,%d)",
605 x1, TranslateYCoordinate(y1), x2, TranslateYCoordinate(y2));
606 }
607
608 // Draw an ellipse using the current pen color.
609 // The ellipse is filled with the current brush color.
610 void ScrollView::Ellipse(int x1, int y1, int width, int height) {
611 SendMsg("drawEllipse(%d,%d,%u,%u)",
612 x1, TranslateYCoordinate(y1), width, height);
613 }
614
615 // Set the pen color to the given RGB values.
616 void ScrollView::Pen(int red, int green, int blue) {
617 SendMsg("pen(%d,%d,%d)", red, green, blue);
618 }
619
620 // Set the pen color to the given RGB values.
621 void ScrollView::Pen(int red, int green, int blue, int alpha) {
622 SendMsg("pen(%d,%d,%d,%d)", red, green, blue, alpha);
623 }
624
625 // Set the brush color to the given RGB values.
626 void ScrollView::Brush(int red, int green, int blue) {
627 SendMsg("brush(%d,%d,%d)", red, green, blue);
628 }
629
630 // Set the brush color to the given RGB values.
631 void ScrollView::Brush(int red, int green, int blue, int alpha) {
632 SendMsg("brush(%d,%d,%d,%d)", red, green, blue, alpha);
633 }
634
635 // Set the attributes for future Text(..) calls.
636 void ScrollView::TextAttributes(const char* font, int pixel_size,
637 bool bold, bool italic, bool underlined) {
638 const char* b;
639 const char* i;
640 const char* u;
641
642 if (bold) { b = "true";
643 } else { b = "false"; }
644 if (italic) { i = "true";
645 } else { i = "false"; }
646 if (underlined) { u = "true";
647 } else { u = "false"; }
648 SendMsg("textAttributes('%s',%u,%s,%s,%s)", font, pixel_size,
649 b, i, u);
650 }
651
652 // Draw text at the given coordinates.
653 void ScrollView::Text(int x, int y, const char* mystring) {
654 SendMsg("drawText(%d,%d,'%s')", x, TranslateYCoordinate(y), mystring);
655 }
656
657 // Open and draw an image given a name at (x,y).
658 void ScrollView::Image(const char* image, int x_pos, int y_pos) {
659 SendMsg("openImage('%s')", image);
660 SendMsg("drawImage('%s',%d,%d)",
661 image, x_pos, TranslateYCoordinate(y_pos));
662 }
663
664 // Add new checkboxmenuentry to menubar.
665 void ScrollView::MenuItem(const char* parent, const char* name,
666 int cmdEvent, bool flag) {
667 if (parent == NULL) { parent = ""; }
668 if (flag) { SendMsg("addMenuBarItem('%s','%s',%d,true)",
669 parent, name, cmdEvent);
670 } else { SendMsg("addMenuBarItem('%s','%s',%d,false)",
671 parent, name, cmdEvent); }
672 }
673
674 // Add new menuentry to menubar.
675 void ScrollView::MenuItem(const char* parent, const char* name, int cmdEvent) {
676 if (parent == NULL) { parent = ""; }
677 SendMsg("addMenuBarItem('%s','%s',%d)", parent, name, cmdEvent);
678 }
679
680 // Add new submenu to menubar.
681 void ScrollView::MenuItem(const char* parent, const char* name) {
682 if (parent == NULL) { parent = ""; }
683 SendMsg("addMenuBarItem('%s','%s')", parent, name);
684 }
685
686 // Add new submenu to popupmenu.
687 void ScrollView::PopupItem(const char* parent, const char* name) {
688 if (parent == NULL) { parent = ""; }
689 SendMsg("addPopupMenuItem('%s','%s')", parent, name);
690 }
691
692 // Add new submenuentry to popupmenu.
693 void ScrollView::PopupItem(const char* parent, const char* name,
694 int cmdEvent, const char* value, const char* desc) {
695 if (parent == NULL) { parent = ""; }
696 char* esc = AddEscapeChars(value);
697 char* esc2 = AddEscapeChars(desc);
698 SendMsg("addPopupMenuItem('%s','%s',%d,'%s','%s')", parent, name,
699 cmdEvent, esc, esc2);
700 delete[] esc;
701 delete[] esc2;
702 }
703
704 // Send an update message for a single window.
705 void ScrollView::UpdateWindow() {
706 SendMsg("update()");
707 }
708
709 // Note: this is an update to all windows
710 void ScrollView::Update() {
711 svmap_mu->Lock();
712 for (std::map<int, ScrollView*>::iterator iter = svmap.begin();
713 iter != svmap.end(); ++iter) {
714 if (iter->second != NULL)
715 iter->second->UpdateWindow();
716 }
717 svmap_mu->Unlock();
718 }
719
720 // Set the pen color, using an enum value (e.g. ScrollView::ORANGE)
721 void ScrollView::Pen(Color color) {
722 Pen(table_colors[color][0], table_colors[color][1],
723 table_colors[color][2], table_colors[color][3]);
724 }
725
726 // Set the brush color, using an enum value (e.g. ScrollView::ORANGE)
727 void ScrollView::Brush(Color color) {
728 Brush(table_colors[color][0],
729 table_colors[color][1],
730 table_colors[color][2],
731 table_colors[color][3]);
732 }
733
734 // Shows a modal Input Dialog which can return any kind of String
735 char* ScrollView::ShowInputDialog(const char* msg) {
736 SendMsg("showInputDialog(\"%s\")", msg);
737 SVEvent* ev;
738 // wait till an input event (all others are thrown away)
739 ev = AwaitEvent(SVET_INPUT);
740 char* p = new char[strlen(ev->parameter) + 1];
741 strncpy(p, ev->parameter, strlen(ev->parameter));
742 p[strlen(ev->parameter)] = '\0';
743 delete ev;
744 return p;
745 }
746
747 // Shows a modal Yes/No Dialog which will return 'y' or 'n'
748 int ScrollView::ShowYesNoDialog(const char* msg) {
749 SendMsg("showYesNoDialog(\"%s\")", msg);
750 SVEvent* ev;
751 // Wait till an input event (all others are thrown away)
752 ev = AwaitEvent(SVET_INPUT);
753 int a = ev->parameter[0];
754 delete ev;
755 return a;
756 }
757
758 // Zoom the window to the rectangle given upper left corner and
759 // lower right corner.
760 void ScrollView::ZoomToRectangle(int x1, int y1, int x2, int y2) {
761 y1 = TranslateYCoordinate(y1);
762 y2 = TranslateYCoordinate(y2);
763 SendMsg("zoomRectangle(%d,%d,%d,%d)",
764 MIN(x1, x2), MIN(y1, y2), MAX(x1, x2), MAX(y1, y2));
765 }
766
767 // Send an image of type Pix.
768 void ScrollView::Image(struct Pix* image, int x_pos, int y_pos) {
769 int width = image->w;
770 int height = image->h;
771 l_uint32 bpp = image->d;
772 ++image_index_;
773 // PIX* do not have a unique identifier/name associated, so name them "lept".
774 SendMsg("createImage('lept%d',%d,%d,%d)", image_index_, width, height, bpp);
775
776 if (bpp == 32) {
777 Transfer32bppImage(image);
778 } else if (bpp == 8) {
779 TransferGrayImage(image);
780 } else if (bpp == 1) {
781 TransferBinaryImage(image);
782 }
783 // PIX* do not have a unique identifier/name associated, so name them "lept".
784 SendMsg("drawImage('lept%d',%d,%d)", image_index_, x_pos, y_pos);
785 }
786
787 // Sends each pixel as hex value like html, e.g. #00FF00 for green.
788 void ScrollView::Transfer32bppImage(PIX* image) {
789 int ppL = pixGetWidth(image);
790 int h = pixGetHeight(image);
791 int wpl = pixGetWpl(image);
792 int transfer_size= ppL * 7 + 2;
793 char* pixel_data = new char[transfer_size];
794 for (int y = 0; y < h; ++y) {
795 l_uint32* data = pixGetData(image) + y*wpl;
796 for (int x = 0; x < ppL; ++x, ++data) {
797 snprintf(&pixel_data[x*7], 7, "#%.2x%.2x%.2x",
798 GET_DATA_BYTE(data, COLOR_RED),
799 GET_DATA_BYTE(data, COLOR_GREEN),
800 GET_DATA_BYTE(data, COLOR_BLUE));
801 }
802 pixel_data[transfer_size - 2] = '\n';
803 pixel_data[transfer_size - 1] = '\0';
804 SendRawMessage(pixel_data);
805 }
806 delete[] pixel_data;
807 }
808
809 // Sends for each pixel either '1' or '0'.
810 void ScrollView::TransferGrayImage(PIX* image) {
811 char* pixel_data = new char[image->w * 2 + 2];
812 for (int y = 0; y < image->h; y++) {
813 l_uint32* data = pixGetData(image) + y * pixGetWpl(image);
814 for (int x = 0; x < image->w; x++) {
815 snprintf(&pixel_data[x*2], 2, "%.2x", (GET_DATA_BYTE(data, x)));
816 pixel_data[image->w * 2] = '\n';
817 pixel_data[image->w * 2 + 1] = '\0';
818 SendRawMessage(pixel_data);
819 }
820 }
821 delete [] pixel_data;
822 }
823
824 // Sends for each pixel either '1' or '0'.
825 void ScrollView::TransferBinaryImage(PIX* image) {
826 char* pixel_data = new char[image->w + 2];
827 for (int y = 0; y < image->h; y++) {
828 l_uint32* data = pixGetData(image) + y * pixGetWpl(image);
829 for (int x = 0; x < image->w; x++) {
830 if (GET_DATA_BIT(data, x))
831 pixel_data[x] = '1';
832 else
833 pixel_data[x] = '0';
834 }
835 pixel_data[image->w] = '\n';
836 pixel_data[image->w + 1] = '\0';
837 SendRawMessage(pixel_data);
838 }
839 delete [] pixel_data;
840 }
841
842 // Escapes the ' character with a \, so it can be processed by LUA.
843 // Note: The caller will have to make sure he deletes the newly allocated item.
844 char* ScrollView::AddEscapeChars(const char* input) {
845 const char* nextptr = strchr(input, '\'');
846 const char* lastptr = input;
847 char* message = new char[kMaxMsgSize];
848 int pos = 0;
849 while (nextptr != NULL) {
850 strncpy(message+pos, lastptr, nextptr-lastptr);
851 pos += nextptr - lastptr;
852 message[pos] = '\\';
853 pos += 1;
854 lastptr = nextptr;
855 nextptr = strchr(nextptr+1, '\'');
856 }
857 strncpy(message+pos, lastptr, strlen(lastptr));
858 message[pos+strlen(lastptr)] = '\0';
859 return message;
860 }
861
862 // Inverse the Y axis if the coordinates are actually inversed.
863 int ScrollView::TranslateYCoordinate(int y) {
864 if (!y_axis_is_reversed_) { return y;
865 } else { return y_size_ - y; }
866 }
867
868 #endif // GRAPHICS_DISABLED