1 // SciTE - Scintilla based Text Editor
2 /** @file GUIGTK.cxx
3  ** Interface to platform GUI facilities.
4  ** Split off from Scintilla's Platform.h to avoid SciTE depending on implementation of Scintilla.
5  **/
6 // Copyright 1998-2010 by Neil Hodgson <neilh@scintilla.org>
7 // The License.txt file describes the conditions under which this software may be distributed.
8 
9 #include <time.h>
10 
11 #include <string>
12 #include <chrono>
13 #include <sstream>
14 
15 #include <gtk/gtk.h>
16 
17 #include "Scintilla.h"
18 #include "ScintillaWidget.h"
19 
20 #include "GUI.h"
21 
22 namespace GUI {
23 
StringFromUTF8(const char * s)24 gui_string StringFromUTF8(const char *s) {
25 	if (s)
26 		return gui_string(s);
27 	else
28 		return gui_string("");
29 }
30 
StringFromUTF8(const std::string & s)31 gui_string StringFromUTF8(const std::string &s) {
32 	return s;
33 }
34 
UTF8FromString(const gui_string & s)35 std::string UTF8FromString(const gui_string &s) {
36 	return s;
37 }
38 
StringFromInteger(long i)39 gui_string StringFromInteger(long i) {
40 	char number[32];
41 	sprintf(number, "%0ld", i);
42 	return gui_string(number);
43 }
44 
StringFromLongLong(long long i)45 gui_string StringFromLongLong(long long i) {
46 	try {
47 		std::ostringstream strstrm;
48 		strstrm << i;
49 		return StringFromUTF8(strstrm.str());
50 	} catch (std::exception &) {
51 		// Exceptions not enabled on stream but still causes diagnostic in Coverity.
52 		// Simply swallow the failure and return the default value.
53 	}
54 	return gui_string();
55 }
56 
LowerCaseUTF8(std::string_view sv)57 std::string LowerCaseUTF8(std::string_view sv) {
58 	gchar *lower = g_utf8_strdown(sv.data(), sv.length());
59 	const std::string sLower(lower);
60 	g_free(lower);
61 	return sLower;
62 }
63 
PWidget(WindowID wid)64 static GtkWidget *PWidget(WindowID wid) {
65 	return static_cast<GtkWidget *>(wid);
66 }
67 
Destroy()68 void Window::Destroy() {
69 	if (wid)
70 		gtk_widget_destroy(GTK_WIDGET(wid));
71 	wid = 0;
72 }
73 
HasFocus()74 bool Window::HasFocus() {
75 	return gtk_widget_has_focus(GTK_WIDGET(wid));
76 }
77 
GetPosition()78 Rectangle Window::GetPosition() {
79 	// Before any size allocated pretend its 1000 wide so not scrolled
80 	Rectangle rc(0, 0, 1000, 1000);
81 	if (wid) {
82 		GtkAllocation allocation;
83 		gtk_widget_get_allocation(PWidget(wid), &allocation);
84 		rc.left = allocation.x;
85 		rc.top = allocation.y;
86 		if (allocation.width > 20) {
87 			rc.right = rc.left + allocation.width;
88 			rc.bottom = rc.top + allocation.height;
89 		}
90 	}
91 	return rc;
92 }
93 
SetPosition(Rectangle rc)94 void Window::SetPosition(Rectangle rc) {
95 	GtkAllocation alloc;
96 	alloc.x = rc.left;
97 	alloc.y = rc.top;
98 	alloc.width = rc.Width();
99 	alloc.height = rc.Height();
100 	gtk_widget_size_allocate(PWidget(wid), &alloc);
101 }
102 
GetClientPosition()103 Rectangle Window::GetClientPosition() {
104 	// On GTK, the client position is the window position
105 	return GetPosition();
106 }
107 
Show(bool show)108 void Window::Show(bool show) {
109 	if (show)
110 		gtk_widget_show(PWidget(wid));
111 }
112 
InvalidateAll()113 void Window::InvalidateAll() {
114 	if (wid) {
115 		gtk_widget_queue_draw(PWidget(wid));
116 	}
117 }
118 
SetTitle(const char * s)119 void Window::SetTitle(const char *s) {
120 	gtk_window_set_title(GTK_WINDOW(wid), s);
121 }
122 
CreatePopUp()123 void Menu::CreatePopUp() {
124 	Destroy();
125 	mid = gtk_menu_new();
126 	g_object_ref_sink(G_OBJECT(mid));
127 }
128 
Destroy()129 void Menu::Destroy() {
130 	if (mid)
131 		g_object_unref(mid);
132 	mid = 0;
133 }
134 
135 #if !GTK_CHECK_VERSION(3,22,0)
MenuPositionFunc(GtkMenu *,gint * x,gint * y,gboolean *,gpointer userData)136 static void  MenuPositionFunc(GtkMenu *, gint *x, gint *y, gboolean *, gpointer userData) {
137 	sptr_t intFromPointer = GPOINTER_TO_INT(userData);
138 	*x = intFromPointer & 0xffff;
139 	*y = intFromPointer >> 16;
140 }
141 #endif
142 
Show(Point pt G_GNUC_UNUSED,Window &)143 void Menu::Show(Point pt G_GNUC_UNUSED, Window &) {
144 	GtkMenu *widget = static_cast<GtkMenu *>(mid);
145 	gtk_widget_show_all(GTK_WIDGET(widget));
146 #if GTK_CHECK_VERSION(3,22,0)
147 	// Rely on GTK to do the right thing with positioning
148 	gtk_menu_popup_at_pointer(widget, NULL);
149 #else
150 	int screenHeight = gdk_screen_height();
151 	int screenWidth = gdk_screen_width();
152 	GtkRequisition requisition;
153 #if GTK_CHECK_VERSION(3,0,0)
154 	gtk_widget_get_preferred_size(GTK_WIDGET(widget), NULL, &requisition);
155 #else
156 	gtk_widget_size_request(GTK_WIDGET(widget), &requisition);
157 #endif
158 	if ((pt.x + requisition.width) > screenWidth) {
159 		pt.x = screenWidth - requisition.width;
160 	}
161 	if ((pt.y + requisition.height) > screenHeight) {
162 		pt.y = screenHeight - requisition.height;
163 	}
164 	gtk_menu_popup(widget, NULL, NULL, MenuPositionFunc,
165 		GINT_TO_POINTER((pt.y << 16) | pt.x), 0,
166 		gtk_get_current_event_time());
167 #endif
168 }
169 
Send(unsigned int msg,uptr_t wParam,sptr_t lParam)170 sptr_t ScintillaPrimitive::Send(unsigned int msg, uptr_t wParam, sptr_t lParam) {
171 	return scintilla_send_message(SCINTILLA(GetID()), msg, wParam, lParam);
172 }
173 
IsDBCSLeadByte(int codePage,char ch)174 bool IsDBCSLeadByte(int codePage, char ch) {
175 	// Byte ranges found in Wikipedia articles with relevant search strings in each case
176 	unsigned char uch = static_cast<unsigned char>(ch);
177 	switch (codePage) {
178 		case 932:
179 			// Shift_jis
180 			return ((uch >= 0x81) && (uch <= 0x9F)) ||
181 				((uch >= 0xE0) && (uch <= 0xEF));
182 		case 936:
183 			// GBK
184 			return (uch >= 0x81) && (uch <= 0xFE);
185 		case 950:
186 			// Big5
187 			return (uch >= 0x81) && (uch <= 0xFE);
188 		// Korean EUC-KR may be code page 949.
189 	}
190 	return false;
191 }
192 
SleepMilliseconds(int sleepTime)193 void SleepMilliseconds(int sleepTime) {
194 	g_usleep(sleepTime * 1000);
195 }
196 
197 }
198