Tesseract  3.02
 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 
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 
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.
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();
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 
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 
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 
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 
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!
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.
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.
580  SendMsg("addMessageBox()");
581 }
582 
583 // Exit the client completely (and notify the server of it).
585  SendRawMessage("svmain:exit()");
586  exit(0);
587 }
588 
589 // Clear the canvas.
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.
706  SendMsg("update()");
707 }
708 
709 // Note: this is an update to all windows
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)
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.
864  if (!y_axis_is_reversed_) { return y;
865  } else { return y_size_ - y; }
866 }
867 
868 #endif // GRAPHICS_DISABLED