tesseract  4.0.0-1-g2a2b
scrollview.cpp
Go to the documentation of this file.
1 // File: scrollview.cpp
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 #define NOMINMAX
22 
23 #include <algorithm>
24 #include <climits>
25 #include <cstdarg>
26 #include <cstring>
27 #include <map>
28 #include <string>
29 #include <utility>
30 #include <vector>
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 const int kSvPort = 8461;
40 const int kMaxMsgSize = 4096;
41 const int kMaxIntPairSize = 45; // Holds %d,%d, for up to 64 bit.
42 
43 #include "svutil.h"
44 
45 #include "allheaders.h"
46 
48  bool empty; // Independent indicator to allow SendMsg to call SendPolygon.
49  std::vector<int> xcoords;
50  std::vector<int> ycoords;
51 };
52 
53 // A map between the window IDs and their corresponding pointers.
54 static std::map<int, ScrollView*> svmap;
55 static SVMutex* svmap_mu;
56 // A map of all semaphores waiting for a specific event on a specific window.
57 static std::map<std::pair<ScrollView*, SVEventType>,
58  std::pair<SVSemaphore*, SVEvent*> > waiting_for_events;
59 static SVMutex* waiting_for_events_mu;
60 
62  SVEvent* any = new SVEvent;
63  any->command_id = command_id;
64  any->counter = counter;
65  any->parameter = new char[strlen(parameter) + 1];
66  strcpy(any->parameter, parameter);
67  any->type = type;
68  any->x = x;
69  any->y = y;
70  any->x_size = x_size;
71  any->y_size = y_size;
72  any->window = window;
73  return any;
74 }
75 
76 // Destructor.
77 // It is defined here, so the compiler can create a single vtable
78 // instead of weak vtables in every compilation unit.
80 
81 #ifndef GRAPHICS_DISABLED
82 void* ScrollView::MessageReceiver(void* a) {
87  int counter_event_id = 0; // ongoing counter
88  char* message = nullptr;
89  // Wait until a new message appears in the input stream_.
90  do {
91  message = ScrollView::GetStream()->Receive();
92  } while (message == nullptr);
93 
94 // This is the main loop which iterates until the server is dead (strlen = -1).
95 // It basically parses for 3 different messagetypes and then distributes the
96 // events accordingly.
97  while (1) {
98  // The new event we create.
99  SVEvent* cur = new SVEvent;
100  // The ID of the corresponding window.
101  int window_id;
102 
103  int ev_type;
104 
105  int n;
106  // Fill the new SVEvent properly.
107  sscanf(message, "%d,%d,%d,%d,%d,%d,%d,%n", &window_id, &ev_type, &cur->x,
108  &cur->y, &cur->x_size, &cur->y_size, &cur->command_id, &n);
109  char* p = (message + n);
110 
111  svmap_mu->Lock();
112  cur->window = svmap[window_id];
113 
114  if (cur->window != nullptr) {
115  cur->parameter = new char[strlen(p) + 1];
116  strcpy(cur->parameter, p);
117  if (strlen(p) > 0) { // remove the last \n
118  cur->parameter[strlen(p)] = '\0';
119  }
120  cur->type = static_cast<SVEventType>(ev_type);
121  // Correct selection coordinates so x,y is the min pt and size is +ve.
122  if (cur->x_size > 0)
123  cur->x -= cur->x_size;
124  else
125  cur->x_size = -cur->x_size;
126  if (cur->y_size > 0)
127  cur->y -= cur->y_size;
128  else
129  cur->y_size = -cur->y_size;
130  // Returned y will be the bottom-left if y is reversed.
131  if (cur->window->y_axis_is_reversed_)
132  cur->y = cur->window->TranslateYCoordinate(cur->y + cur->y_size);
133  cur->counter = counter_event_id;
134  // Increase by 2 since we will also create an SVET_ANY event from cur,
135  // which will have a counter_id of cur + 1 (and thus gets processed
136  // after cur).
137  counter_event_id += 2;
138 
139  // In case of an SVET_EXIT event, quit the whole application.
140  if (ev_type == SVET_EXIT) { ScrollView::Exit(); }
141 
142  // Place two copies of it in the table for the window.
143  cur->window->SetEvent(cur);
144 
145  // Check if any of the threads currently waiting want it.
146  std::pair<ScrollView*, SVEventType> awaiting_list(cur->window,
147  cur->type);
148  std::pair<ScrollView*, SVEventType> awaiting_list_any(cur->window,
149  SVET_ANY);
150  std::pair<ScrollView*, SVEventType> awaiting_list_any_window((ScrollView*)nullptr,
151  SVET_ANY);
152  waiting_for_events_mu->Lock();
153  if (waiting_for_events.count(awaiting_list) > 0) {
154  waiting_for_events[awaiting_list].second = cur;
155  waiting_for_events[awaiting_list].first->Signal();
156  } else if (waiting_for_events.count(awaiting_list_any) > 0) {
157  waiting_for_events[awaiting_list_any].second = cur;
158  waiting_for_events[awaiting_list_any].first->Signal();
159  } else if (waiting_for_events.count(awaiting_list_any_window) > 0) {
160  waiting_for_events[awaiting_list_any_window].second = cur;
161  waiting_for_events[awaiting_list_any_window].first->Signal();
162  } else {
163  // No one wanted it, so delete it.
164  delete cur;
165  }
166  waiting_for_events_mu->Unlock();
167  // Signal the corresponding semaphore twice (for both copies).
168  ScrollView* sv = svmap[window_id];
169  if (sv != nullptr) {
170  sv->Signal();
171  sv->Signal();
172  }
173  } else {
174  delete cur; // Applied to no window.
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 == nullptr);
182  }
183  return nullptr;
184 }
185 
186 // Table to implement the color index values in the old system.
187 static const uint8_t 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_ = 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,
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_ == nullptr) {
277  nr_created_windows_ = 0;
278  stream_ = new SVNetwork(server_name, kSvPort);
279  waiting_for_events_mu = new SVMutex();
280  svmap_mu = new SVMutex();
282  "svmain = luajava.bindClass('com.google.scrollview.ScrollView')\n");
283  SVSync::StartThread(MessageReceiver, nullptr);
284  }
285 
286  // Set up the variables on the clientside.
287  nr_created_windows_++;
288  event_handler_ = nullptr;
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] = nullptr;
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 = static_cast<ScrollView*>(a);
324  SVEvent* new_event;
325 
326  do {
327  stream_->Flush();
328  sv->semaphore_->Wait();
329  new_event = nullptr;
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] != nullptr &&
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 didn't find anything we had an old alarm and just sleep again.
344  if (new_event != nullptr) {
345  sv->event_table_[k] = nullptr;
346  sv->mutex_->Unlock();
347  if (sv->event_handler_ != nullptr) { 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 = nullptr;
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 != nullptr);
357  return nullptr;
358 }
359 #endif // GRAPHICS_DISABLED
360 
362  #ifndef GRAPHICS_DISABLED
363  svmap_mu->Lock();
364  if (svmap[window_id_] != nullptr) {
365  svmap_mu->Unlock();
366  // So the event handling thread can quit.
367  SendMsg("destroy()");
368 
370  delete sve;
371  svmap_mu->Lock();
372  svmap[window_id_] = nullptr;
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 nullptr 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  for (int i = 0; i < SVET_COUNT; i++) {
386  delete event_table_[i];
387  }
388  #endif // GRAPHICS_DISABLED
389 }
390 
391 #ifndef GRAPHICS_DISABLED
392 void ScrollView::SendMsg(const char* format, ...) {
394  if (!points_->empty)
395  SendPolygon();
396  va_list args;
397  char message[kMaxMsgSize];
398 
399  va_start(args, format); // variable list
400  vsnprintf(message, kMaxMsgSize, format, args);
401  va_end(args);
402 
403  char form[kMaxMsgSize];
404  snprintf(form, kMaxMsgSize, "w%u:%s\n", window_id_, message);
405 
406  stream_->Send(form);
407 }
408 
411 void ScrollView::SendRawMessage(const char* msg) {
412  stream_->Send(msg);
413 }
414 
417  event_handler_ = listener;
418 }
419 
420 void ScrollView::Signal() {
421  semaphore_->Signal();
422 }
423 
424 void ScrollView::SetEvent(SVEvent* svevent) {
425 // Copy event
426  SVEvent* any = svevent->copy();
427  SVEvent* specific = svevent->copy();
428  any->counter = specific->counter + 1;
429 
430 // Place both events into the queue.
431  mutex_->Lock();
432  // Delete the old objects..
433  delete event_table_[specific->type];
434  delete event_table_[SVET_ANY];
435  // ...and put the new ones in the table.
436  event_table_[specific->type] = specific;
437  event_table_[SVET_ANY] = any;
438  mutex_->Unlock();
439 }
440 
441 
446  // Initialize the waiting semaphore.
447  SVSemaphore* 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 // Block until any event on any window is received.
465 // No event is returned here!
467  // Initialize the waiting semaphore.
468  SVSemaphore* sem = new SVSemaphore();
469  std::pair<ScrollView*, SVEventType> ea((ScrollView*)nullptr, SVET_ANY);
470  waiting_for_events_mu->Lock();
471  waiting_for_events[ea] = std::pair<SVSemaphore*, SVEvent*> (sem, (SVEvent*)nullptr);
472  waiting_for_events_mu->Unlock();
473  // Wait on it.
474  stream_->Flush();
475  sem->Wait();
476  // Process the event we got woken up for (its in waiting_for_events pair).
477  waiting_for_events_mu->Lock();
478  SVEvent* ret = waiting_for_events[ea].second;
479  waiting_for_events.erase(ea);
480  waiting_for_events_mu->Unlock();
481  return ret;
482 }
483 
484 // Send the current buffered polygon (if any) and clear it.
485 void ScrollView::SendPolygon() {
486  if (!points_->empty) {
487  points_->empty = true; // Allows us to use SendMsg.
488  int length = points_->xcoords.size();
489  // length == 1 corresponds to 2 SetCursors in a row and only the
490  // last setCursor has any effect.
491  if (length == 2) {
492  // An isolated line!
493  SendMsg("drawLine(%d,%d,%d,%d)",
494  points_->xcoords[0], points_->ycoords[0],
495  points_->xcoords[1], points_->ycoords[1]);
496  } else if (length > 2) {
497  // A polyline.
498  SendMsg("createPolyline(%d)", length);
499  char coordpair[kMaxIntPairSize];
500  std::string decimal_coords;
501  for (int i = 0; i < length; ++i) {
502  snprintf(coordpair, kMaxIntPairSize, "%d,%d,",
503  points_->xcoords[i], points_->ycoords[i]);
504  decimal_coords += coordpair;
505  }
506  decimal_coords += '\n';
507  SendRawMessage(decimal_coords.c_str());
508  SendMsg("drawPolyline()");
509  }
510  points_->xcoords.clear();
511  points_->ycoords.clear();
512  }
513 }
514 
515 
516 /*******************************************************************************
517 * LUA "API" functions.
518 *******************************************************************************/
519 
520 // Sets the position from which to draw to (x,y).
521 void ScrollView::SetCursor(int x, int y) {
522  SendPolygon();
523  DrawTo(x, y);
524 }
525 
526 // Draws from the current position to (x,y) and sets the new position to it.
527 void ScrollView::DrawTo(int x, int y) {
528  points_->xcoords.push_back(x);
529  points_->ycoords.push_back(TranslateYCoordinate(y));
530  points_->empty = false;
531 }
532 
533 // Draw a line using the current pen color.
534 void ScrollView::Line(int x1, int y1, int x2, int y2) {
535  if (!points_->xcoords.empty() && x1 == points_->xcoords.back() &&
536  TranslateYCoordinate(y1) == points_->ycoords.back()) {
537  // We are already at x1, y1, so just draw to x2, y2.
538  DrawTo(x2, y2);
539  } else if (!points_->xcoords.empty() && x2 == points_->xcoords.back() &&
540  TranslateYCoordinate(y2) == points_->ycoords.back()) {
541  // We are already at x2, y2, so just draw to x1, y1.
542  DrawTo(x1, y1);
543  } else {
544  // This is a new line.
545  SetCursor(x1, y1);
546  DrawTo(x2, y2);
547  }
548 }
549 
550 // Set the visibility of the window.
551 void ScrollView::SetVisible(bool visible) {
552  if (visible) { SendMsg("setVisible(true)");
553  } else { SendMsg("setVisible(false)"); }
554 }
555 
556 // Set the alwaysOnTop flag.
558  if (b) { SendMsg("setAlwaysOnTop(true)");
559  } else { SendMsg("setAlwaysOnTop(false)"); }
560 }
561 
562 // Adds a message entry to the message box.
563 void ScrollView::AddMessage(const char* format, ...) {
564  va_list args;
565  char message[kMaxMsgSize];
566  char form[kMaxMsgSize];
567 
568  va_start(args, format); // variable list
569  vsnprintf(message, kMaxMsgSize, format, args);
570  va_end(args);
571 
572  snprintf(form, kMaxMsgSize, "w%u:%s", window_id_, message);
573 
574  char* esc = AddEscapeChars(form);
575  SendMsg("addMessage(\"%s\")", esc);
576  delete[] esc;
577 }
578 
579 // Set a messagebox.
581  SendMsg("addMessageBox()");
582 }
583 
584 // Exit the client completely (and notify the server of it).
586  SendRawMessage("svmain:exit()");
587  exit(0);
588 }
589 
590 // Clear the canvas.
592  SendMsg("clear()");
593 }
594 
595 // Set the stroke width.
596 void ScrollView::Stroke(float width) {
597  SendMsg("setStrokeWidth(%f)", width);
598 }
599 
600 // Draw a rectangle using the current pen color.
601 // The rectangle is filled with the current brush color.
602 void ScrollView::Rectangle(int x1, int y1, int x2, int y2) {
603  if (x1 == x2 && y1 == y2)
604  return; // Scrollviewer locks up.
605  SendMsg("drawRectangle(%d,%d,%d,%d)",
606  x1, TranslateYCoordinate(y1), x2, TranslateYCoordinate(y2));
607 }
608 
609 // Draw an ellipse using the current pen color.
610 // The ellipse is filled with the current brush color.
611 void ScrollView::Ellipse(int x1, int y1, int width, int height) {
612  SendMsg("drawEllipse(%d,%d,%u,%u)",
613  x1, TranslateYCoordinate(y1), width, height);
614 }
615 
616 // Set the pen color to the given RGB values.
617 void ScrollView::Pen(int red, int green, int blue) {
618  SendMsg("pen(%d,%d,%d)", red, green, blue);
619 }
620 
621 // Set the pen color to the given RGB values.
622 void ScrollView::Pen(int red, int green, int blue, int alpha) {
623  SendMsg("pen(%d,%d,%d,%d)", red, green, blue, alpha);
624 }
625 
626 // Set the brush color to the given RGB values.
627 void ScrollView::Brush(int red, int green, int blue) {
628  SendMsg("brush(%d,%d,%d)", red, green, blue);
629 }
630 
631 // Set the brush color to the given RGB values.
632 void ScrollView::Brush(int red, int green, int blue, int alpha) {
633  SendMsg("brush(%d,%d,%d,%d)", red, green, blue, alpha);
634 }
635 
636 // Set the attributes for future Text(..) calls.
637 void ScrollView::TextAttributes(const char* font, int pixel_size,
638  bool bold, bool italic, bool underlined) {
639  const char* b;
640  const char* i;
641  const char* u;
642 
643  if (bold) { b = "true";
644  } else { b = "false"; }
645  if (italic) { i = "true";
646  } else { i = "false"; }
647  if (underlined) { u = "true";
648  } else { u = "false"; }
649  SendMsg("textAttributes('%s',%u,%s,%s,%s)", font, pixel_size,
650  b, i, u);
651 }
652 
653 // Draw text at the given coordinates.
654 void ScrollView::Text(int x, int y, const char* mystring) {
655  SendMsg("drawText(%d,%d,'%s')", x, TranslateYCoordinate(y), mystring);
656 }
657 
658 // Open and draw an image given a name at (x,y).
659 void ScrollView::Image(const char* image, int x_pos, int y_pos) {
660  SendMsg("openImage('%s')", image);
661  SendMsg("drawImage('%s',%d,%d)",
662  image, x_pos, TranslateYCoordinate(y_pos));
663 }
664 
665 // Add new checkboxmenuentry to menubar.
666 void ScrollView::MenuItem(const char* parent, const char* name,
667  int cmdEvent, bool flag) {
668  if (parent == nullptr) { parent = ""; }
669  if (flag) { SendMsg("addMenuBarItem('%s','%s',%d,true)",
670  parent, name, cmdEvent);
671  } else { SendMsg("addMenuBarItem('%s','%s',%d,false)",
672  parent, name, cmdEvent); }
673 }
674 
675 // Add new menuentry to menubar.
676 void ScrollView::MenuItem(const char* parent, const char* name, int cmdEvent) {
677  if (parent == nullptr) { parent = ""; }
678  SendMsg("addMenuBarItem('%s','%s',%d)", parent, name, cmdEvent);
679 }
680 
681 // Add new submenu to menubar.
682 void ScrollView::MenuItem(const char* parent, const char* name) {
683  if (parent == nullptr) { parent = ""; }
684  SendMsg("addMenuBarItem('%s','%s')", parent, name);
685 }
686 
687 // Add new submenu to popupmenu.
688 void ScrollView::PopupItem(const char* parent, const char* name) {
689  if (parent == nullptr) { parent = ""; }
690  SendMsg("addPopupMenuItem('%s','%s')", parent, name);
691 }
692 
693 // Add new submenuentry to popupmenu.
694 void ScrollView::PopupItem(const char* parent, const char* name,
695  int cmdEvent, const char* value, const char* desc) {
696  if (parent == nullptr) { parent = ""; }
697  char* esc = AddEscapeChars(value);
698  char* esc2 = AddEscapeChars(desc);
699  SendMsg("addPopupMenuItem('%s','%s',%d,'%s','%s')", parent, name,
700  cmdEvent, esc, esc2);
701  delete[] esc;
702  delete[] esc2;
703 }
704 
705 // Send an update message for a single window.
707  SendMsg("update()");
708 }
709 
710 // Note: this is an update to all windows
712  svmap_mu->Lock();
713  for (std::map<int, ScrollView*>::iterator iter = svmap.begin();
714  iter != svmap.end(); ++iter) {
715  if (iter->second != nullptr)
716  iter->second->UpdateWindow();
717  }
718  svmap_mu->Unlock();
719 }
720 
721 // Set the pen color, using an enum value (e.g. ScrollView::ORANGE)
722 void ScrollView::Pen(Color color) {
723  Pen(table_colors[color][0], table_colors[color][1],
724  table_colors[color][2], table_colors[color][3]);
725 }
726 
727 // Set the brush color, using an enum value (e.g. ScrollView::ORANGE)
729  Brush(table_colors[color][0],
730  table_colors[color][1],
731  table_colors[color][2],
732  table_colors[color][3]);
733 }
734 
735 // Shows a modal Input Dialog which can return any kind of String
736 char* ScrollView::ShowInputDialog(const char* msg) {
737  SendMsg("showInputDialog(\"%s\")", msg);
738  SVEvent* ev;
739  // wait till an input event (all others are thrown away)
740  ev = AwaitEvent(SVET_INPUT);
741  char* p = new char[strlen(ev->parameter) + 1];
742  strcpy(p, ev->parameter);
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  std::min(x1, x2), std::min(y1, y2), std::max(x1, x2), std::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  l_uint8* data;
770  size_t size;
771  pixWriteMem(&data, &size, image, IFF_PNG);
772  int base64_len = (size + 2) / 3 * 4;
773  y_pos = TranslateYCoordinate(y_pos);
774  SendMsg("readImage(%d,%d,%d)", x_pos, y_pos, base64_len);
775  // Base64 encode the data.
776  const char kBase64Table[64] = {
777  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
778  'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
779  'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
780  'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
781  'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
782  'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
783  'w', 'x', 'y', 'z', '0', '1', '2', '3',
784  '4', '5', '6', '7', '8', '9', '+', '/',
785  };
786  char* base64 = new char[base64_len + 1];
787  memset(base64, '=', base64_len);
788  base64[base64_len] = '\0';
789  int remainder = 0;
790  int bits_left = 0;
791  int code_len = 0;
792  for (size_t i = 0; i < size; ++i) {
793  int code = (data[i] >> (bits_left + 2)) | remainder;
794  base64[code_len++] = kBase64Table[code & 63];
795  bits_left += 2;
796  remainder = data[i] << (6 - bits_left);
797  if (bits_left == 6) {
798  base64[code_len++] = kBase64Table[remainder & 63];
799  bits_left = 0;
800  remainder = 0;
801  }
802  }
803  if (bits_left > 0)
804  base64[code_len++] = kBase64Table[remainder & 63];
805  SendRawMessage(base64);
806  delete [] base64;
807  lept_free(data);
808 }
809 
810 // Escapes the ' character with a \, so it can be processed by LUA.
811 // Note: The caller will have to make sure he deletes the newly allocated item.
812 char* ScrollView::AddEscapeChars(const char* input) {
813  const char* nextptr = strchr(input, '\'');
814  const char* lastptr = input;
815  char* message = new char[kMaxMsgSize];
816  int pos = 0;
817  while (nextptr != nullptr) {
818  strncpy(message+pos, lastptr, nextptr-lastptr);
819  pos += nextptr - lastptr;
820  message[pos] = '\\';
821  pos += 1;
822  lastptr = nextptr;
823  nextptr = strchr(nextptr+1, '\'');
824  }
825  strcpy(message+pos, lastptr);
826  return message;
827 }
828 
829 // Inverse the Y axis if the coordinates are actually inversed.
831  if (!y_axis_is_reversed_) { return y;
832  } else { return y_size_ - y; }
833 }
834 
835 #endif // GRAPHICS_DISABLED
virtual void Notify(const SVEvent *sve)
Definition: scrollview.h:92
virtual ~SVEventHandler()
void DrawTo(int x, int y)
Definition: scrollview.cpp:527
void Unlock()
Unlocks on a mutex.
Definition: svutil.cpp:78
int ShowYesNoDialog(const char *msg)
Definition: scrollview.cpp:748
const int kMaxMsgSize
Definition: scrollview.cpp:40
void AddMessageBox()
Definition: scrollview.cpp:580
void TextAttributes(const char *font, int pixel_size, bool bold, bool italic, bool underlined)
Definition: scrollview.cpp:637
void Send(const char *msg)
Put a message in the messagebuffer to the server and try to send it.
Definition: svutil.cpp:209
void Stroke(float width)
Definition: scrollview.cpp:596
SVEvent * AwaitEventAnyWindow()
Definition: scrollview.cpp:466
static void StartThread(void *(*func)(void *), void *arg)
Create new thread.
Definition: svutil.cpp:87
void SetVisible(bool visible)
Definition: scrollview.cpp:551
void Lock()
Locks on a mutex.
Definition: svutil.cpp:70
void ZoomToRectangle(int x1, int y1, int x2, int y2)
Definition: scrollview.cpp:760
void Wait()
Wait on a semaphore.
Definition: svutil.cpp:198
int counter
Definition: scrollview.h:72
void MenuItem(const char *parent, const char *name)
Definition: scrollview.cpp:682
SVEventType
Definition: scrollview.h:45
int TranslateYCoordinate(int y)
Definition: scrollview.cpp:830
void Signal()
Signal a semaphore.
Definition: svutil.cpp:188
static void SendRawMessage(const char *msg)
Definition: scrollview.cpp:411
int x
Definition: scrollview.h:66
void SetCursor(int x, int y)
Definition: scrollview.cpp:521
static void Update()
Definition: scrollview.cpp:711
void SendMsg(const char *msg,...)
Send a message to the server, attaching the window id.
Definition: scrollview.cpp:393
const int kMaxIntPairSize
Definition: scrollview.cpp:41
void Ellipse(int x, int y, int width, int height)
Definition: scrollview.cpp:611
static void Exit()
Definition: scrollview.cpp:585
void PopupItem(const char *parent, const char *name)
Definition: scrollview.cpp:688
SVEvent * AwaitEvent(SVEventType type)
Definition: scrollview.cpp:445
char * Receive()
Definition: svutil.cpp:227
char * ShowInputDialog(const char *msg)
Definition: scrollview.cpp:736
SVEvent()
Definition: scrollview.h:74
int y_size
Definition: scrollview.h:69
void Text(int x, int y, const char *mystring)
Definition: scrollview.cpp:654
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
int x_size
Definition: scrollview.h:68
void AddEventHandler(SVEventHandler *listener)
Add an Event Listener to this ScrollView Window.
Definition: scrollview.cpp:416
SVEventType type
Definition: scrollview.h:64
void Image(struct Pix *image, int x_pos, int y_pos)
Definition: scrollview.cpp:768
ScrollView * window
Definition: scrollview.h:65
const int kSvPort
Definition: scrollview.cpp:39
std::vector< int > ycoords
Definition: scrollview.cpp:50
int command_id
Definition: scrollview.h:70
char * parameter
Definition: scrollview.h:71
void UpdateWindow()
Definition: scrollview.cpp:706
void Clear()
Definition: scrollview.cpp:591
std::vector< int > xcoords
Definition: scrollview.cpp:49
Definition: svutil.h:78
void Rectangle(int x1, int y1, int x2, int y2)
Definition: scrollview.cpp:602
void AlwaysOnTop(bool b)
Definition: scrollview.cpp:557
void Flush()
Flush the buffer.
Definition: svutil.cpp:216
int y
Definition: scrollview.h:67
SVEvent * copy()
Definition: scrollview.cpp:61
void Pen(Color color)
Definition: scrollview.cpp:722
void Line(int x1, int y1, int x2, int y2)
Definition: scrollview.cpp:534
void AddMessage(const char *format,...)
Definition: scrollview.cpp:563
void Brush(Color color)
Definition: scrollview.cpp:728