1 #ifdef OSX
2 	#include <sys/malloc.h>
3 	#include <CoreFoundation/CoreFoundation.h>
4 #elif BSD
5 	#include <stdlib.h>
6 #else
7 	#include <malloc.h>
8 #endif /* OSX */
9 #if !defined(OSX) && !defined(WINDOWS)
10 #include <signal.h>
11 #endif
12 #include <sys/types.h>
13 #include <string.h>
14 #ifndef _MSC_VER
15 #include <unistd.h>
16 #endif //MSVC
17 #include <ctype.h>
18 #include <SDL.h>
19 #include <SDL_thread.h>
20 #include "asc.h"
21 #include "client_serv.h"
22 #include "consolewin.h"
23 #include "context_menu.h"
24 #ifdef WINDOWS
25 #include "elloggingwrapper.h"
26 #endif
27 #include "elwindows.h"
28 #include "font.h"
29 #include "gamewin.h"
30 #include "hud.h"
31 #include "interface.h"
32 #include "list.h"
33 #include "load_gl_extensions.h"
34 #include "misc.h"
35 #include "tabs.h"
36 #include "text.h"
37 #include "translate.h"
38 #include "url.h"
39 #ifdef OPENGL_TRACE
40 #include "gl_init.h"
41 #endif
42 #include "sound.h"
43 
44 char browser_name[120];
45 static Uint32 url_win_sep = 0;
46 static float url_win_text_zoom = 1.0;
47 static const int max_url_count = 100;
48 static int clear_all_button = 101;
49 static int url_scroll_id = 0;
50 static int url_win_top_line = 0;
51 static Uint32 url_win_max_string_width = 0;
52 static int url_win_help_x = 0;
53 static int url_win_text_len_y = 0;
54 static int url_win_text_start_y = 0;
55 static int url_win_full_url_y_start = 0;
56 static int url_win_full_url_y_len = 0;
57 static int url_win_line_step = 0;
58 static Uint32 url_win_clicktime = 0;
59 static enum { URLW_EMPTY=1, URLW_CLEAR, URLW_OVER } url_win_status = URLW_EMPTY;
60 static list_node_t *url_win_clicked_url = NULL;
61 static list_node_t *url_win_hover_url = NULL;
62 
63 static int have_url_count = 0;
64 static int saved_url_count = 0;
65 
66 static list_node_t *newest_url = NULL;
67 static list_node_t *active_url = NULL;
68 static list_node_t *cm_url = NULL;
69 
70 
71 /* define the structure stored for each URL */
72 typedef struct
73 {
74 	int seen_count;		/* incremented each time a link is seen in chat */
75 	int visited;		/* true when link has been opened in the browser */
76 	char *text;			/* the actual url text */
77 } URLDATA;
78 
79 
80 /* store the current url count, used in num_new_url() */
save_url_count(void)81 void save_url_count(void)
82 {
83 	saved_url_count = have_url_count;
84 }
85 
86 
87 /* return the number of url since last save_url_count() call */
num_new_url(void)88 int num_new_url(void)
89 {
90 	return have_url_count - saved_url_count;
91 }
92 
93 
94 /* open last seen URL */
open_last_seen_url(void)95 void open_last_seen_url(void)
96 {
97 	if (!have_url_count)
98 		return;
99 	open_web_link(((URLDATA *)active_url->data)->text);
100 	((URLDATA *)active_url->data)->visited = 1;
101 }
102 
103 
104 /* free all url list memory */
destroy_url_list(void)105 void destroy_url_list(void)
106 {
107 	if (have_url_count)
108 	{
109 		/* free all the text */
110 		list_node_t *local_head = newest_url;
111 		while (local_head->next != NULL)
112 		{
113 			if (local_head->data != NULL)
114 				free(((URLDATA *)local_head->data)->text);
115 			local_head = local_head->next;
116 		}
117 
118 		/* free the list */
119 		list_destroy(newest_url);
120 		saved_url_count = have_url_count = 0;
121 		active_url = newest_url = NULL;
122 	}
123 }
124 
125 
126 /* #url command - List, clear list or open a specific URL */
url_command(const char * text,int len)127 int url_command(const char *text, int len)
128 {
129 	/* no URLs so far so display a message then exit */
130 	if (!have_url_count)
131 	{
132 		LOG_TO_CONSOLE(c_red2, urlcmd_none_str);
133 		return 1;
134 	}
135 
136 	/* get any parameter text */
137 	while(*text && !isspace(*text))
138 		text++;
139 	while(*text && isspace(*text))
140 		text++;
141 
142 	/* no parameter specified - list the URL(s) we have, oldest first */
143 	if (!strlen(text))
144 	{
145 		char *out_str = NULL;
146 		size_t out_len = 0;
147 		int irl_num = 0;
148 		int line_colour = c_grey1;
149 		list_node_t *local_head = newest_url;
150 
151 		LOG_TO_CONSOLE(c_green2, urlcmd_list_str);
152 
153 		/* go to the oldest in the list */
154 		while (local_head->next != NULL)
155 			local_head = local_head->next;
156 
157 		/* display the list ending with the newest, alternating colours */
158 		while (local_head != NULL)
159 		{
160 			size_t new_len = sizeof(char) * (strlen(((URLDATA *)local_head->data)->text) + 60);
161 			if (new_len > out_len)
162 			{
163 				if (out_str != NULL)
164 					free(out_str);
165 				out_str = (char *)malloc(new_len);
166 				out_len = new_len;
167 			}
168 			safe_snprintf(out_str, new_len, "%c %d) %s (seen %d time%s) (%s)", ((local_head==active_url) ?'>':' '),
169 				 ++irl_num, ((URLDATA *)local_head->data)->text, ((URLDATA *)local_head->data)->seen_count,
170 				 ((URLDATA *)local_head->data)->seen_count == 1?"":"s",  ((((URLDATA *)local_head->data)->visited) ?"visited":"unvisited"));
171 			LOG_TO_CONSOLE(line_colour, out_str);
172 			local_head = local_head->prev;
173 			line_colour = (line_colour==c_grey1) ?c_grey2 :c_grey1;
174 		}
175 
176 		if (out_str != NULL)
177 			free(out_str);
178 	}
179 
180 	/* if parameter is "clear" delete all entries */
181 	else if (strcmp(text, urlcmd_clear_str) == 0)
182 	{
183 		destroy_url_list();
184 	}
185 
186 	/* else assume parameter is an index, if its a valid index, open the URL */
187 	else
188 	{
189 		int open_index = atoi(text) - 1;
190 		int valid_node = 0;
191 		if (open_index >= 0)
192 		{
193 			list_node_t *local_head = newest_url;
194 			int url_num = 0;
195 			/* go to the oldest int the list */
196 			while (local_head->next != NULL)
197 				local_head = local_head->next;
198 			/* go to the specified entry */
199 			while ((local_head->prev != NULL) && (url_num < open_index))
200 			{
201 				local_head = local_head->prev;
202 				url_num++;
203 			}
204 			/* if we end up at a valid node, go for it */
205 			if ((local_head != NULL) && (url_num == open_index) && strlen(((URLDATA *)local_head->data)->text))
206 			{
207 				open_web_link(((URLDATA *)local_head->data)->text);
208 				((URLDATA *)local_head->data)->visited = 1;
209 				valid_node = 1;
210 			}
211 		}
212 		if (!valid_node)
213 			LOG_TO_CONSOLE(c_red2, urlcmd_invalid_str);
214 	}
215 
216 	return 1;
217 }
218 
219 
220 /* find and store all urls in the provided string */
find_all_url(const char * source_string,const int len)221 void find_all_url(const char *source_string, const int len)
222 {
223 	char search_for[][10] = {"http://", "https://", "ftp://", "www."};
224 	int next_start = 0;
225 
226 	while (next_start < len)
227 	{
228 		int first_found = len-next_start; /* set to max */
229 		int i;
230 
231 		/* find the first of the url start strings */
232 		for(i = 0; i < sizeof(search_for)/10; i++)
233 		{
234 			int found_at = get_string_occurance(search_for[i], source_string+next_start, len-next_start, 1);
235 			if ((found_at >= 0) && (found_at < first_found))
236 				first_found = found_at;
237 		}
238 
239 		/* if url found, store (if new) it then continue the search straight after the end */
240 		if (first_found < len-next_start)
241 		{
242 			char *new_url = NULL;
243 			char *add_start = "";
244 			size_t url_len;
245 			int url_start = next_start + first_found;
246 			int have_already = 0;
247 
248 			/* find the url end */
249 			for (next_start = url_start; next_start < len; next_start++)
250 			{
251 				char cur_char = source_string[next_start];
252 				if(!cur_char || cur_char == ' ' || cur_char == '\n' || cur_char == '<'
253 					|| cur_char == '>' || cur_char == '|' || cur_char == '"' || cur_char == '\'' || cur_char == '`'
254 					|| cur_char == ']' || cur_char == ';' || cur_char == '\\' || (cur_char&0x80) != 0)
255 					break;
256 			}
257 
258 			/* prefix www. with http:// */
259 			if (strncmp(&source_string[url_start], "www.", 4) == 0)
260 				add_start = "http://";
261 
262 			/* extract the string */
263 			url_len = strlen(add_start) + (next_start-url_start) + 1;
264 			new_url = (char *)malloc(sizeof(char)*url_len);
265 			/* could use safe_xxx() functions but I think its simpler not to here */
266 			strcpy(new_url, add_start);
267 			strncat(new_url, &source_string[url_start], next_start-url_start );
268 			new_url[url_len-1] = 0;
269 
270 			/* check the new URL is not already in the list */
271 			if (have_url_count)
272 			{
273 				list_node_t *local_head = newest_url;
274 				while (local_head != NULL)
275 				{
276 					/* if its already stored, just make existing version active */
277 					if (strcmp(((URLDATA *)local_head->data)->text, new_url) == 0)
278 					{
279 						active_url = local_head;
280 						((URLDATA *)local_head->data)->seen_count++;
281 						have_already = 1;
282 						free(new_url);
283 						break;
284 					}
285 					local_head = local_head->next;
286 				}
287 			}
288 
289 			/* if its a new url, create a new node in the url list */
290 			if (!have_already)
291 			{
292 				URLDATA *new_node = (URLDATA *)malloc(sizeof(URLDATA));
293 
294 				/* if there's a max number of url and we've reached it, remove the oldest */
295 				/* we don't need to worry if its the active_url as thats going to change */
296 				if (max_url_count && (max_url_count==have_url_count))
297 				{
298 					list_node_t *local_head = newest_url;
299 					/* go to the oldest in the list */
300 					while (local_head->next != NULL)
301 						local_head = local_head->next;
302 					free(((URLDATA *)local_head->data)->text);
303 					free(local_head->data);
304 					if (local_head==newest_url)
305 					{
306 						/* the special case is when max_url_count=1... */
307 						free(local_head);
308 						newest_url = NULL;
309 					}
310 					else
311 					{
312 						local_head = local_head->prev;
313 						free(local_head->next);
314 						local_head->next = NULL;
315 					}
316 					have_url_count--;
317 				}
318 
319 				new_node->seen_count = 1;
320 				new_node->visited = 0;
321 				new_node->text = new_url;
322 				list_push(&newest_url, new_node);
323 				active_url = newest_url;
324 				have_url_count++;
325 			}
326 
327 		} /* end if url found */
328 
329 		/* no more urls found so stop looking */
330 		else
331 			break;
332 	}
333 
334 } /* end find_all_url() */
335 
336 
337 #ifdef  WINDOWS
only_call_from_open_web_link__go_to_url(void * url)338 static int only_call_from_open_web_link__go_to_url(void * url)
339 {
340 	char browser_command[400];
341 
342 	init_thread_log("web_link");
343 
344 	// build the command line and execute it
345 	safe_snprintf (browser_command, sizeof (browser_command), "%s \"%s\"", browser_name, url),
346 	system(browser_command);	// Do not use this command on UNIX.
347 
348 	// free the memory allocated in open_web_link()
349 	free(url);
350 
351 	return 0;
352 }
353 #endif
354 
open_web_link(const char * url)355 void open_web_link(const char * url)
356 {
357 #ifdef OSX
358 	CFURLRef newurl = CFURLCreateWithString(kCFAllocatorDefault,CFStringCreateWithCStringNoCopy(NULL,url,kCFStringEncodingMacRoman, NULL),NULL);
359 	LSOpenCFURLRef(newurl,NULL);
360 	CFRelease(newurl);
361 #else
362 	// browser name can override the windows default, and if not defined in Linux, don't error
363 	if(*browser_name){
364 #ifndef WINDOWS
365 		static int have_set_signal = 0;
366 #ifdef SOUND_FORK_BUGFIX
367 		int sound_on_copy = sound_on;
368 		int music_on_copy = music_on;
369 #endif
370 
371 		/* we're not interested in the exit status of the child so
372 		   set SA_NOCLDWAIT to stop it becoming a zombie if we don't wait() */
373 		if (!have_set_signal)
374 		{
375 			struct sigaction act;
376 			memset(&act, 0, sizeof(act));
377 			act.sa_handler = SIG_DFL;
378 			act.sa_flags = SA_NOCLDWAIT;
379 			sigaction(SIGCHLD, &act, NULL);
380 			have_set_signal = 1;
381 		}
382 
383 #ifdef SOUND_FORK_BUGFIX
384 		if (sound_on_copy)
385 			toggle_sounds(&sound_on);
386 		if (music_on_copy)
387 			toggle_music(&music_on);
388 #endif
389 
390 		if (fork() == 0){
391 			execlp(browser_name, browser_name, url, NULL);
392 			// in case the exec errors
393 			_exit(1);
394 		}
395 
396 #ifdef SOUND_FORK_BUGFIX
397 		if (sound_on_copy)
398 			toggle_sounds(&sound_on);
399 		if (music_on_copy)
400 			toggle_music(&music_on);
401 #endif
402 
403 #else
404 		// make a copy of the url string as it may be freed by the caller
405 		// will be freed as the only_call_from_open_web_link__go_to_url() exits
406 		char *cp_url = malloc(strlen(url)+1);
407 		safe_strncpy(cp_url, url, strlen(url)+1);
408 
409 		// windows needs to spawn it in its own thread
410 		SDL_CreateThread(only_call_from_open_web_link__go_to_url, "BrowserThread", cp_url);
411 	} else {
412 		ShellExecute(NULL, "open", url, NULL, NULL, SW_SHOWNOACTIVATE); //this returns an int we could check for errors, but that's mainly when you use shellexecute for local files
413 #endif  //_WIN32
414 	}
415 #endif // OSX
416 }
417 
418 
419 /*  Access the caught url list and display in a scrollable window. */
display_url_handler(window_info * win)420 static int display_url_handler(window_info *win)
421 {
422 #ifdef OPENGL_TRACE
423 CHECK_GL_ERRORS();
424 #endif //OPENGL_TRACE
425 
426 	url_win_hover_url = NULL;
427 
428 	glEnable(GL_TEXTURE_2D);
429 
430 	/* check for external state change */
431 	if (!have_url_count && !url_win_status)
432 		url_win_status = URLW_EMPTY;
433 	/* if we have a status message, display it */
434 	if (url_win_status)
435 	{
436 		char *message[] = { urlcmd_none_str, urlwin_clear_str, urlwin_open_str };
437 		int y_start = (url_win_text_start_y - 0.75 * win->default_font_len_y) / 2;
438 		glColor3f(1.0f,1.0f,1.0f);
439 		draw_string_zoomed(url_win_help_x, y_start, (unsigned char *)message[url_win_status-1], 1, 0.75 * win->current_scale);
440 		url_win_status = (have_url_count) ?0 :URLW_EMPTY;
441 	}
442 
443 	/* display a page of url */
444  	if (have_url_count)
445 	{
446 		list_node_t *local_head = newest_url;
447 		int currenty = url_win_text_start_y;
448 		int start_url = 0;
449 		int num_url_displayed = 0;
450 
451 		/* don't scroll if everything will fit in the window, also catch if the list has been cleared via #url */
452 		if (((url_win_line_step * have_url_count) <= url_win_text_len_y) || (url_win_top_line > have_url_count))
453 			url_win_top_line = 0;
454 
455 		/* move to the first url to be displayed - set from the scroll bar */
456 		while (start_url < url_win_top_line && local_head->next != NULL)
457 		{
458 			local_head = local_head->next;
459 			start_url++;
460 		}
461 
462 		/* loop over the remaining URLs while there is room in the window */
463 		while (local_head != NULL)
464 		{
465 			char *thetext = ((URLDATA *)local_head->data)->text;
466 			int dsp_string_len = 0;
467 			int highlight_url = 0;
468 
469 			/* stop now if the url line will not fit into the window */
470 			if (((currenty - url_win_text_start_y) + url_win_line_step) > url_win_text_len_y)
471 				break;
472 
473 			/* highlight the active (F2) url */
474 			if (local_head == active_url)
475 				glColor3f(0.0f,1.0f,0.0f);
476 			else
477 				glColor3f(1.0f,1.0f,1.0f);
478 
479 			draw_string_zoomed_ellipsis_font(url_win_sep, currenty,
480 				(const unsigned char *)((URLDATA *)local_head->data)->text,
481 				url_win_max_string_width, 1, win->font_category, url_win_text_zoom);
482 
483 			/* step down a line, do it now as the maths for mouse over below is easier */
484 			currenty += url_win_line_step;
485 
486 			/* if the mouse is over the current line, hightlight it */
487 			if ((mouse_y >= win->cur_y + currenty - url_win_line_step) &&
488 				(mouse_y < win->cur_y + currenty) &&
489 				(mouse_x >= win->cur_x + (int)url_win_sep) &&
490 				(mouse_x - (int)url_win_sep <= win->cur_x + url_win_max_string_width))
491 			{
492 				/* remember which url we're over in case it's clicked */
493 				url_win_hover_url = local_head;
494 				highlight_url = 1;
495 			}
496 
497 			/* if a context menu is open, only hightlight the last URL hovered over before the context opened */
498 			if (cm_window_shown() != CM_INIT_VALUE)
499 			{
500 				if (cm_url == local_head)
501 					highlight_url = 1;
502 				else
503 					highlight_url = 0;
504 			}
505 			else
506 				cm_url = NULL;
507 
508 			/* if mouse over or context activated, highlight the current URL */
509 			if (highlight_url)
510 			{
511 				char *help_substring = NULL;
512 				size_t help_substring_len = 0;
513 				int dsp_start = 0;
514 				int helpline = 0;
515 				Uint32 currenttime = SDL_GetTicks();
516 				size_t full_help_len = strlen(((URLDATA *)local_head->data)->text) + 30;
517 				char *full_help_text = (char *)malloc(sizeof(char) * full_help_len);
518 				float string_width = min2i(get_string_width_zoom((const unsigned char *)((URLDATA *)local_head->data)->text,
519 					win->font_category, url_win_text_zoom), url_win_max_string_width);
520 
521 				/* display the mouse over help next time round */
522 				url_win_status = URLW_OVER;
523 
524 				/* underline the text, just clicked links are red, otherwise blue - paler when visited */
525 				if ((currenttime - url_win_clicktime < 500) && (url_win_clicked_url == url_win_hover_url))
526 					glColor3f(1.0f,0.0f,0.3f);
527 				else if (((URLDATA *)local_head->data)->visited)
528 					glColor3f(0.3f,0.5f,1.0f);
529 				else
530 					glColor3f(0.1f,0.2f,1.0f);
531 				glDisable(GL_TEXTURE_2D);
532 				glBegin(GL_LINES);
533 				glVertex2i(url_win_sep, currenty-2);
534 				glVertex2i(url_win_sep+string_width, currenty-2);
535 				glEnd();
536 				glEnable(GL_TEXTURE_2D);
537 
538 				/* write the full url as help text at the bottom of the window */
539 				safe_snprintf(full_help_text, full_help_len, "%s (seen %d time%s) (%s)",
540 					((URLDATA *)local_head->data)->text, ((URLDATA *)local_head->data)->seen_count,
541 					((URLDATA *)local_head->data)->seen_count == 1?"":"s",  ((((URLDATA *)local_head->data)->visited)?"visited":"unvisited"));
542 				thetext = full_help_text;
543 				dsp_string_len = 0;
544 				string_width = 0;
545 				glColor3f(1.0f,1.0f,1.0f);
546 				while(*thetext != '\0')
547 				{
548 					float char_width = get_char_width_zoom(*thetext++,
549 						win->font_category, win->current_scale_small);
550 					if (((string_width+char_width) > (win->len_x - 2*url_win_sep)) || (*thetext == '\0'))
551 					{
552 						if (*thetext == '\0') /* catch the last line */
553 							dsp_string_len++;
554 						if (help_substring_len < dsp_string_len)
555 						{
556 							if (help_substring != NULL)
557 								free(help_substring);
558 							help_substring = (char *)malloc(sizeof(char)*(dsp_string_len+1));
559 							help_substring_len = dsp_string_len;
560 						}
561 						strncpy(help_substring, &full_help_text[dsp_start], dsp_string_len);
562 						help_substring[dsp_string_len] = '\0';
563 						draw_string_small_zoomed(url_win_sep, url_win_full_url_y_start + url_win_sep + helpline++ * win->small_font_len_y, (const unsigned char*)help_substring, 1, win->current_scale);
564 						dsp_start += dsp_string_len;
565 						dsp_string_len = 0;
566 						string_width = 0;
567 					}
568 					dsp_string_len++;
569 					string_width += char_width;
570 				}
571 				free(help_substring);
572 				free(full_help_text);
573 
574 			} /* end if mouse over url */
575 
576 			/* count how many displayed so we can set the scroll bar properly */
577 			num_url_displayed++;
578 
579 			local_head = local_head->next;
580 		}
581 
582 		/* set the number of steps for the scroll bar */
583 		vscrollbar_set_bar_len(win->window_id, url_scroll_id, have_url_count - num_url_displayed);
584 
585 	} /* end if have url */
586 
587 	/* draw a line below the list of url, above the current url full text */
588 	glColor3fv(gui_color);
589 	glDisable(GL_TEXTURE_2D);
590 	glBegin(GL_LINES);
591 	glVertex2i(0, url_win_full_url_y_start);
592 	glVertex2i(win->len_x, url_win_full_url_y_start);
593 	glEnd();
594 	glEnable(GL_TEXTURE_2D);
595 
596 #ifdef OPENGL_TRACE
597 CHECK_GL_ERRORS();
598 #endif //OPENGL_TRACE
599 
600 	return 1;
601 
602 } /* end display_url_handler() */
603 
604 
605 /* common function to delete specified url record */
delete_current_url(list_node_t * chosen_url)606 static void delete_current_url(list_node_t *chosen_url)
607 {
608 	if (have_url_count && chosen_url != NULL)
609 	{
610 		list_node_t *prev = chosen_url->prev;
611 		list_node_t *next = chosen_url->next;
612 		if (prev != NULL)
613 			prev->next = next;
614 		if (next != NULL)
615 			next->prev = prev;
616 		if (chosen_url == newest_url)
617 			newest_url = next;
618 		if (chosen_url == active_url)
619 			active_url = prev;
620 		if (active_url == NULL)
621 			active_url = next;
622 		free(((URLDATA *)chosen_url->data)->text);
623 		free(chosen_url->data);
624 		free(chosen_url);
625 		url_win_hover_url = NULL;
626 		have_url_count--;
627 	}
628 }
629 
630 /* common function to open link of the specified url record */
open_current_url(list_node_t * chosen_url)631 static void open_current_url(list_node_t *chosen_url)
632 {
633 	if (have_url_count && chosen_url != NULL)
634 	{
635 		url_win_clicktime = SDL_GetTicks();
636 		url_win_clicked_url = chosen_url;
637 		open_web_link(((URLDATA *)chosen_url->data)->text);
638 		((URLDATA *)chosen_url->data)->visited = 1;
639 	}
640 }
641 
642 /* called just before a context menu is displayed */
context_url_pre_show_handler(window_info * win,int widget_id,int mx,int my,window_info * cm_win)643 static void context_url_pre_show_handler(window_info *win, int widget_id, int mx, int my, window_info *cm_win)
644 {
645 	// propagate opacity from parent tab window
646 	if (cm_win!= NULL && get_id_MW(MW_INFO) >-1 && get_id_MW(MW_INFO) < windows_list.num_windows)
647 		cm_win->opaque = windows_list.window[get_id_MW(MW_INFO)].opaque;
648 }
649 
650 /* called when a context menu option is selected */
context_url_handler(window_info * win,int widget_id,int mx,int my,int option)651 static int context_url_handler(window_info *win, int widget_id, int mx, int my, int option)
652 {
653 	if (cm_url)
654 	{
655 		switch (option)
656 		{
657 			case 0: open_current_url(cm_url); break;
658 			case 1:
659 				{
660 					char *theurl = ((URLDATA *)cm_url->data)->text;
661 					char *skiptext = "http://";
662 					if (theurl && strlen(theurl) > strlen(skiptext))
663 						history_grep(theurl+strlen(skiptext), strlen(theurl)-strlen(skiptext));
664 				}
665 				break;
666 			case 2: ((URLDATA *)cm_url->data)->visited = 1; break;
667 			case 3: ((URLDATA *)cm_url->data)->visited = 0; break;
668 			case 5: delete_current_url(cm_url); break;
669 			case 7: destroy_url_list(); break;
670 		}
671 		cm_url = NULL;
672 	}
673 	url_win_clicktime = SDL_GetTicks();
674 	return 1;
675 }
676 
677 /* act on scroll wheel in the main window or clicking a URL */
click_url_handler(window_info * win,int mx,int my,Uint32 flags)678 static int click_url_handler(window_info *win, int mx, int my, Uint32 flags)
679 {
680 	static size_t cm_id = CM_INIT_VALUE;
681 
682 	if (flags & ELW_WHEEL_UP)
683 		vscrollbar_scroll_up(win->window_id, url_scroll_id);
684 	else if (flags & ELW_WHEEL_DOWN)
685 		vscrollbar_scroll_down(win->window_id, url_scroll_id);
686 	else if (have_url_count && url_win_hover_url != NULL)
687 	{
688 		if (flags & KMOD_CTRL)
689 		{
690 			delete_current_url(url_win_hover_url);
691 			do_window_close_sound();
692 		}
693 		else if (flags & ELW_RIGHT_MOUSE)
694 		{
695 			cm_url = url_win_hover_url;
696 			/* create first time needed */
697 			if (!cm_valid(cm_id))
698 			{
699 				cm_id = cm_create(cm_url_menu_str, context_url_handler);
700 				cm_set_pre_show_handler(cm_id, context_url_pre_show_handler);
701 			}
702 			cm_show_direct(cm_id, -1, -1);
703 		}
704 		else
705 		{
706 			/* open the URL but block double clicks */
707 			Uint32 currentclicktime = SDL_GetTicks();
708 			if (currentclicktime < url_win_clicktime)
709 				url_win_clicktime = 0; /* just in case we're running for 49 days :) */
710 			if ((currentclicktime - url_win_clicktime > 1000) || (url_win_clicked_url != url_win_hover_url))
711 			{
712 				do_click_sound();
713 				open_current_url(url_win_hover_url);
714 			}
715 		}
716 	}
717 	url_win_top_line = vscrollbar_get_pos(win->window_id, url_scroll_id);
718 	return 0;
719 }
720 
url_win_click_clear_all(widget_list * widget,int mx,int my,Uint32 flags)721 static int url_win_click_clear_all(widget_list *widget, int mx, int my, Uint32 flags)
722 {
723 	if ((flags & ELW_WHEEL_UP) || (flags & ELW_WHEEL_DOWN))
724 		return 1;
725 	destroy_url_list();
726 	return 1;
727 }
728 
url_win_mouseover_clear_all(widget_list * widget,int mx,int my)729 static int url_win_mouseover_clear_all(widget_list *widget, int mx, int my)
730 {
731 	url_win_status = URLW_CLEAR;
732 	return 1;
733 }
734 
735 
url_win_scroll_click(widget_list * widget,int mx,int my,Uint32 flags)736 static int url_win_scroll_click(widget_list *widget, int mx, int my, Uint32 flags)
737 {
738 	url_win_top_line = vscrollbar_get_pos(widget->window_id, url_scroll_id);
739 	return 1;
740 }
741 
742 
url_win_scroll_drag(widget_list * widget,int mx,int my,Uint32 flags,int dx,int dy)743 static int url_win_scroll_drag(widget_list *widget, int mx, int my, Uint32 flags, int dx, int dy)
744 {
745 	return url_win_scroll_click(widget, mx, my, flags);
746 }
747 
748 
resize_url_handler(window_info * win,int new_width,int new_height)749 static int resize_url_handler(window_info *win, int new_width, int new_height)
750 {
751 	widget_list *widget = widget_find(win->window_id, clear_all_button);
752 
753 	url_win_text_zoom = 1.0 * win->current_scale;
754 	url_win_sep = (int)(0.5 + 5 * win->current_scale);
755 
756 	button_resize(win->window_id, clear_all_button, 0, 0, win->current_scale * 0.75);
757 	widget_move(win->window_id, clear_all_button, url_win_sep, url_win_sep);
758 
759 	url_win_help_x = widget->len_x + 2 * url_win_sep;
760 	url_win_max_string_width = win->len_x - (2*url_win_sep + win->box_size);
761 
762 	url_win_line_step = (int)(3 + get_line_height(win->font_category, url_win_text_zoom));
763 
764 	url_win_full_url_y_len = 2*url_win_sep + 4 * win->small_font_len_y;
765 
766 	url_win_text_start_y = 2*url_win_sep + widget->len_y;
767 
768 	url_win_text_len_y = url_win_line_step * (int)((win->len_y - url_win_text_start_y - url_win_full_url_y_len) / url_win_line_step);
769 	url_win_full_url_y_start = url_win_text_start_y + url_win_text_len_y;
770 
771 	widget_resize(win->window_id, url_scroll_id, win->box_size, url_win_text_len_y);
772 	widget_move(win->window_id, url_scroll_id, win->len_x - win->box_size, url_win_text_start_y);
773 
774 	return 0;
775 }
776 
777 
778 /* fill the URL window created as a tab. */
fill_url_window(int window_id)779 void fill_url_window(int window_id)
780 {
781 	set_window_custom_scale(window_id, MW_INFO);
782 	set_window_handler(window_id, ELW_HANDLER_DISPLAY, &display_url_handler );
783 	set_window_handler(window_id, ELW_HANDLER_CLICK, &click_url_handler );
784 	set_window_handler(window_id, ELW_HANDLER_RESIZE, &resize_url_handler );
785 
786 	/* create the clear all button */
787 	clear_all_button = button_add_extended (window_id, clear_all_button, NULL,
788 		0, 0, 0, 0, 0, 0, "Clear All");
789 	widget_set_OnClick(window_id, clear_all_button, url_win_click_clear_all);
790 	widget_set_OnMouseover(window_id, clear_all_button, url_win_mouseover_clear_all);
791 
792 	/* create the scroll bar */
793 	url_scroll_id = vscrollbar_add_extended(window_id, url_scroll_id, NULL,
794 		0, 0, 0, 0, 0, 1.0, 0, 1, have_url_count);
795 	widget_set_OnDrag(window_id, url_scroll_id, url_win_scroll_drag);
796 	widget_set_OnClick(window_id, url_scroll_id, url_win_scroll_click);
797 }
798 
799