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