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