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