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