1 /* Bluefish HTML Editor
2  * html2.c - menu/toolbar callbacks, inserting functions, and other cool stuff
3  * otherwise html.c is getting so long ;-)
4  *
5  * Copyright (C) 1999-2012 Olivier Sessink
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 /* #define DEBUG */
22 
23 #include <gtk/gtk.h>
24 #include <string.h>            /* strlen() */
25 #include <stdlib.h>            /* strtol() */
26 
27 #include "htmlbar.h"
28 #include "htmlbar_stock_icons.h"
29 #include "html2.h"
30 #include "cap.h"
31 #include "../bf_lib.h"         /* string_is_color(), strip_any_whitespace()*/
32 #include "../dialog_utils.h"
33 #include "../document.h"
34 #include "../gtk_easy.h"
35 #include "../stringlist.h"
36 
glist_with_html_tags(gint use_html5)37 static GList *glist_with_html_tags(gint use_html5) {
38 	GList *tmplist = NULL;
39 
40 	tmplist = g_list_prepend(tmplist, "var");
41 	tmplist = g_list_prepend(tmplist, "ul");
42 	tmplist = g_list_prepend(tmplist, "tr");
43 	tmplist = g_list_prepend(tmplist, "title");
44 	tmplist = g_list_prepend(tmplist, "thead");
45 	tmplist = g_list_prepend(tmplist, "th");
46 	tmplist = g_list_prepend(tmplist, "tfoot");
47 	tmplist = g_list_prepend(tmplist, "textarea");
48 	tmplist = g_list_prepend(tmplist, "td");
49 	tmplist = g_list_prepend(tmplist, "tbody");
50 	tmplist = g_list_prepend(tmplist, "table");
51 	tmplist = g_list_prepend(tmplist, "sup");
52 	tmplist = g_list_prepend(tmplist, "sub");
53 	tmplist = g_list_prepend(tmplist, "style");
54 	tmplist = g_list_prepend(tmplist, "strong");
55 	tmplist = g_list_prepend(tmplist, "span");
56 	tmplist = g_list_prepend(tmplist, "small");
57 	tmplist = g_list_prepend(tmplist, "select");
58 	tmplist = g_list_prepend(tmplist, "script");
59 	tmplist = g_list_prepend(tmplist, "samp");
60 	tmplist = g_list_prepend(tmplist, "q");
61 	tmplist = g_list_prepend(tmplist, "pre");
62 	tmplist = g_list_prepend(tmplist, "param");
63 	tmplist = g_list_prepend(tmplist, "p");
64 	tmplist = g_list_prepend(tmplist, "option");
65 	tmplist = g_list_prepend(tmplist, "optgroup");
66 	tmplist = g_list_prepend(tmplist, "ol");
67 	tmplist = g_list_prepend(tmplist, "object");
68 	tmplist = g_list_prepend(tmplist, "noscript");
69 	tmplist = g_list_prepend(tmplist, "meta");
70 	tmplist = g_list_prepend(tmplist, "map");
71 	tmplist = g_list_prepend(tmplist, "link");
72 	tmplist = g_list_prepend(tmplist, "li");
73 	tmplist = g_list_prepend(tmplist, "legend");
74 	tmplist = g_list_prepend(tmplist, "label");
75 	tmplist = g_list_prepend(tmplist, "kbd");
76 	tmplist = g_list_prepend(tmplist, "ins");
77 	tmplist = g_list_prepend(tmplist, "input");
78 	tmplist = g_list_prepend(tmplist, "img");
79 	tmplist = g_list_prepend(tmplist, "iframe");
80 	tmplist = g_list_prepend(tmplist, "i");
81 	tmplist = g_list_prepend(tmplist, "html");
82 	tmplist = g_list_prepend(tmplist, "hr");
83 	tmplist = g_list_prepend(tmplist, "head");
84 	tmplist = g_list_prepend(tmplist, "h6");
85 	tmplist = g_list_prepend(tmplist, "h5");
86 	tmplist = g_list_prepend(tmplist, "h4");
87 	tmplist = g_list_prepend(tmplist, "h3");
88 	tmplist = g_list_prepend(tmplist, "h2");
89 	tmplist = g_list_prepend(tmplist, "h1");
90 	tmplist = g_list_prepend(tmplist, "form");
91 	tmplist = g_list_prepend(tmplist, "fieldset");
92 	tmplist = g_list_prepend(tmplist, "em");
93 	tmplist = g_list_prepend(tmplist, "dt");
94 	tmplist = g_list_prepend(tmplist, "dl");
95 	tmplist = g_list_prepend(tmplist, "div");
96 	tmplist = g_list_prepend(tmplist, "dfn");
97 	tmplist = g_list_prepend(tmplist, "del");
98 	tmplist = g_list_prepend(tmplist, "dd");
99 	tmplist = g_list_prepend(tmplist, "colgroup");
100 	tmplist = g_list_prepend(tmplist, "col");
101 	tmplist = g_list_prepend(tmplist, "code");
102 	tmplist = g_list_prepend(tmplist, "cite");
103 	tmplist = g_list_prepend(tmplist, "caption");
104 	tmplist = g_list_prepend(tmplist, "button");
105 	tmplist = g_list_prepend(tmplist, "br");
106 	tmplist = g_list_prepend(tmplist, "body");
107 	tmplist = g_list_prepend(tmplist, "blockquote");
108 	tmplist = g_list_prepend(tmplist, "bdo");
109 	tmplist = g_list_prepend(tmplist, "base");
110 	tmplist = g_list_prepend(tmplist, "b");
111 	tmplist = g_list_prepend(tmplist, "area");
112 	tmplist = g_list_prepend(tmplist, "address");
113 	tmplist = g_list_prepend(tmplist, "abbr");
114 	tmplist = g_list_prepend(tmplist, "a");
115 	tmplist = g_list_prepend(tmplist, "");
116 	if (use_html5) {
117 		tmplist = g_list_prepend(tmplist, "wbr");
118 		tmplist = g_list_prepend(tmplist, "video");
119 		tmplist = g_list_prepend(tmplist, "u");
120 		tmplist = g_list_prepend(tmplist, "track");
121 		tmplist = g_list_prepend(tmplist, "time");
122 		tmplist = g_list_prepend(tmplist, "summary");
123 		tmplist = g_list_prepend(tmplist, "source");
124 		tmplist = g_list_prepend(tmplist, "section");
125 		tmplist = g_list_prepend(tmplist, "s");
126 		tmplist = g_list_prepend(tmplist, "ruby");
127 		tmplist = g_list_prepend(tmplist, "rt");
128 		tmplist = g_list_prepend(tmplist, "rp");
129 		tmplist = g_list_prepend(tmplist, "progress");
130 		tmplist = g_list_prepend(tmplist, "output");
131 		tmplist = g_list_prepend(tmplist, "nav");
132 		tmplist = g_list_prepend(tmplist, "meter");
133 		tmplist = g_list_prepend(tmplist, "menu");
134 		tmplist = g_list_prepend(tmplist, "mark");
135 		tmplist = g_list_prepend(tmplist, "keygen");
136 		tmplist = g_list_prepend(tmplist, "hgroup");
137 		tmplist = g_list_prepend(tmplist, "header");
138 		tmplist = g_list_prepend(tmplist, "footer");
139 		tmplist = g_list_prepend(tmplist, "figure");
140 		tmplist = g_list_prepend(tmplist, "figcaption");
141 		tmplist = g_list_prepend(tmplist, "embed");
142 		tmplist = g_list_prepend(tmplist, "details");
143 		tmplist = g_list_prepend(tmplist, "datalist");
144 		tmplist = g_list_prepend(tmplist, "command");
145 		tmplist = g_list_prepend(tmplist, "canvas");
146 		tmplist = g_list_prepend(tmplist, "bdi");
147 		tmplist = g_list_prepend(tmplist, "audio");
148 		tmplist = g_list_prepend(tmplist, "aside");
149 		tmplist = g_list_prepend(tmplist, "article");
150 	}
151 	else {
152 		tmplist = g_list_prepend(tmplist, "tt");
153 		tmplist = g_list_prepend(tmplist, "noframes");
154 		tmplist = g_list_prepend(tmplist, "frameset");
155 		tmplist = g_list_prepend(tmplist, "frame");
156 		tmplist = g_list_prepend(tmplist, "big");
157 		tmplist = g_list_prepend(tmplist, "acronym");
158 	}
159 	return tmplist;
160 }
161 
162 typedef enum {entry, textbox, wizard} Tdest_type;
163 typedef enum {onestyle, multistyle} Tcs3_style;
164 typedef enum {but_none, but_file, but_style, but_color} Textra_button;
165 typedef struct {
166 	Tdest_type dest_type;
167 	GtkWidget *entry;
168 	Tdocument *doc;
169 	gint doc_start;
170 	gint doc_end;
171 } Tcs3_destination;
172 
173 typedef struct {
174 	GtkWidget *win;
175 	Tcs3_destination dest;
176 	Tcs3_style styletype;
177 	/*GtkWidget *clist;*/
178 	GtkListStore *lstore;
179 	GtkWidget *lview;
180 	gint selected_row;
181 	gboolean grab;
182 	GtkWidget *selector;
183 	GtkWidget *html5;
184 	GtkWidget *property;
185 	GtkWidget *value;
186 	GtkWidget *rule_add_but;
187 	GtkWidget *rule_update_but;
188 	GtkWidget *extra_but;
189 } Tcs3_diag;
190 
191 typedef struct {
192 	GtkWidget *win;
193 	GtkWidget *selector;
194 	GtkWidget *html5;
195 	GtkWidget *property;
196 	GtkWidget *value;
197 	GtkWidget *extra_but;
198 	gint whichrow;
199 	Tcs3_diag *diag;
200 } Tcs3_pd_diag;
201 
202 typedef struct {
203 	gchar *property; /* the name of the property */
204 	gchar **possibilities; /* a list of possibilities */
205 	gint force_pos; 	/* force the possibility to be one of the options in possibilities */
206 	Textra_button buttype;
207 } Tcs3_arr;
208 
209 static gchar *cs3_colors[] = {"aqua", "black", "blue", "fuchsia", "gray", "green", "lime", "maroon", "navy", "olive", "orange", "purple", "red", "silver", "teal", "white", "yellow", NULL};
210 static gchar *cs3_repeat[] = {"repeat", "repeat-x", "repeat-y", "no-repeat", NULL};
211 static gchar *cs3_fonts[] = {"arial, helvetica, sans-serif", "roman, 'times new roman', times, serif", "courier, fixed, monospace", "western, fantasy",  "Zapf-Chancery, cursive", "serif", "sans-serif", "cursive", "fantasy", "monospace", NULL};
212 //static gchar *cs3_font_size_adjustments[] = {"0.58", "none", "inherit", NULL};
213 //static gchar *cs3_font_stretches[] = {"normal","wider","narrower","ultra-condensed","extra-condensed","condensed","semi-condensed","semi-expanded","expanded","extra-expanded","ultra-expanded", NULL};
214 static gchar *cs3_font_styles[] = {"normal", "italic", "oblique", NULL};
215 static gchar *cs3_font_variants[] = {"normal", "small-caps", NULL};
216 static gchar *cs3_font_weights[] = {"normal", "bold", "bolder", "lighter", "100", "200", "300", "400", "500", "600", "700", "800", "900",  NULL};
217 static gchar *cs3_background_attachments[] = {"scroll", "fixed", NULL};
218 static gchar *cs3_background_positions[] = {"top", "center", "bottom", "left", "center", "right", "10% 10%", NULL}; /* more 2 add */
219 static gchar *cs3_text_decorations[] = {"none", "underline", "overline", "line-through", "blink", NULL};
220 static gchar *cs3_text_transforms[] = {"none", "capitalize", "uppercase", "lowercase", NULL};
221 static gchar *cs3_text_aligns[] = {"left", "right", "center", "justify", NULL};
222 static gchar *cs3_border_styles[] = {"none", "dotted", "dashed", "solid", "double", "groove", "ridge", "inset", "outset", NULL};
223 static gchar *cs3_floats[] = {"left", "right", "none", NULL};
224 static gchar *cs3_positions[] = {"static", "relative", "absolute", "fixed", NULL};
225 static gchar *cs3_clears[] = {"left", "right", "none", "both", NULL};
226 static gchar *cs3_displays[] = {"block", "inline", "list-item", "run-in", "compact", "marker", "table", "inline-table", "table-row-group", "table-header-group", "table-footer-group", "table-row", "table-column-group", "table-column", "table-cell", "table-caption", "none",NULL};
227 static gchar *cs3_whitespaces[] = {"normal", "pre", "nowrap", NULL};
228 static gchar *cs3_list_style_types[] = {"disc", "circle", "square", "decimal", "lower-roman", "upper-roman", "lower-alpha", "upper-alpha", "none", NULL};
229 static gchar *cs3_list_style_positions[] = {"inside", "outside", NULL};
230 static gchar *cs3_zindices[] = {"auto", NULL};
231 static gchar *cs3_directions[] = {"ltr", "rtl", NULL};
232 static gchar *cs3_unicodebidis[] = {"normal", "embed", "bidi-override", NULL};
233 static gchar *cs3_vertical_aligns[] = {"baseline", "sub", "super", "top", "text-top", "middle", "bottom", "text-bottom", NULL};
234 static gchar *cs3_overflows[] = {"visible", "hidden", "scroll", "auto", NULL};
235 static gchar *cs3_visibilities[] = {"visible", "hidden", "collapse", NULL};
236 static gchar *cs3_none[] = {"none", NULL};
237 static gchar *cs3_caption_sides[] = {"top", "bottom", "left", "right", NULL};
238 static gchar *cs3_table_layouts[] = {"auto", "fixed", NULL};
239 static gchar *cs3_border_collapses[] = {"collapse", "separate", NULL};
240 static gchar *cs3_empty_cells[] = {"show", "hide", NULL};
241 static gchar *cs3_content[] = {"normal", "none", "url()", "counter()", "counters()", "attr()",
242  "open-quote","close-quote", "no-open-quote", "no-close-quote", "inherit", NULL};
243 static gchar *cs3_cursor[] = {"url()", "auto", "crosshair", "default", "help", "move",
244  "pointer ", "progress", "text", "wait", "e-resize", "ne-resize", "nw-resize", "n-resize",
245   "se-resize", "sw-resize", "s-resize", "w-resize", NULL};
246 static gchar *cs3_width[] = {"thin", "medium", "thick", NULL};
247 
248 static Tcs3_arr cs3_arr[] = {
249 {"font-family", cs3_fonts, 0, but_none},
250 //{"font-stretch", cs3_font_stretches, 0, but_none},
251 // None of the major browsers support the font-stretch property.
252 {"font-style", cs3_font_styles, 1, but_none},
253 {"font-variant", cs3_font_variants, 1, but_none},
254 {"font-weight", cs3_font_weights, 1, but_none},
255 {"font-size", NULL, 0, but_none},
256 // {"font-size-adjust", cs3_font_size_adjustments, 0, but_none},
257 // Gecko only
258 {"font", NULL, 0, but_none},
259 {"color", cs3_colors, 0, but_color},
260 {"background-color", cs3_colors, 0, but_color},
261 {"background-image", NULL, 0, but_file},//	<uri> | none | inherit  url("images/bg.png")
262 {"background-repeat", cs3_repeat, 0, but_none},
263 {"background-attachment",cs3_background_attachments , 1, but_none},
264 {"background-position",cs3_background_positions , 0, but_none},
265 {"background", NULL, 0, but_none},
266 {"word-spacing", NULL, 0, but_none},
267 {"letter-spacing", NULL, 0, but_none},
268 {"text-decoration", cs3_text_decorations, 1, but_none},
269 {"text-transform", cs3_text_transforms, 1, but_none},
270 {"text-align", cs3_text_aligns, 1, but_none},
271 {"text-indent", NULL, 0, but_none},
272 {"text-shadow", cs3_none, 0, but_none},
273 {"line-height", NULL, 0, but_none},
274 {"margin-top", NULL, 0, but_none},
275 {"margin-right", NULL, 0, but_none},
276 {"margin-bottom", NULL, 0, but_none},
277 {"margin-left", NULL, 0, but_none},
278 {"margin", NULL, 0, but_none},
279 {"padding-top", NULL, 0, but_none},
280 {"padding-right", NULL, 0, but_none},
281 {"padding-bottom", NULL, 0, but_none},
282 {"padding-left", NULL, 0, but_none},
283 {"padding", NULL, 0, but_none},
284 {"border-top-width", NULL, 0, but_none},
285 {"border-right-width", NULL, 0, but_none},
286 {"border-bottom-width", NULL, 0, but_none},
287 {"border-left-width", NULL, 0, but_none},
288 {"border-width", NULL, 0, but_none},
289 {"border-top-color", cs3_colors, 0, but_color},
290 {"border-right-color", cs3_colors, 0, but_color},
291 {"border-bottom-color", cs3_colors, 0, but_color},
292 {"border-left-color", cs3_colors, 0, but_color},
293 {"border-color", cs3_colors, 0, but_color},
294 {"border-top-style", cs3_border_styles, 1, but_none},
295 {"border-right-style", cs3_border_styles, 1, but_none},
296 {"border-bottom-style", cs3_border_styles, 1, but_none},
297 {"border-left-style", cs3_border_styles, 1, but_none},
298 {"border-style", cs3_border_styles, 1, but_none},
299 {"border-top", NULL, 0, but_none},
300 {"border-right", NULL, 0, but_none},
301 {"border-bottom", NULL, 0, but_none},
302 {"border-left", NULL, 0, but_none},
303 {"border", NULL, 0, but_none},
304 {"width", NULL, 0, but_none},
305 {"height", NULL, 0, but_none},
306 {"float", cs3_floats, 1, but_none},
307 {"position", cs3_positions, 1, but_none},
308 {"top", NULL, 0, but_none},
309 {"right", NULL, 0, but_none},
310 {"bottom", NULL, 0, but_none},
311 {"left", NULL, 0, but_none},
312 {"clear", cs3_clears, 1, but_none},
313 {"display", cs3_displays, 1, but_none},
314 {"direction", cs3_directions, 1, but_none},
315 {"unicode-bidi", cs3_unicodebidis, 1, but_none},
316 {"z-index", cs3_zindices, 0, but_none},
317 {"min-width", NULL, 0, but_none},
318 {"max-width", NULL, 0, but_none},
319 {"min-height", NULL, 0, but_none},
320 {"max-height", NULL, 0, but_none},
321 {"line-height", NULL, 0, but_none},
322 {"white-space", cs3_whitespaces, 1, but_none},
323 {"list-style", NULL, 0, but_none},
324 {"list-style-type", cs3_list_style_types, 1, but_none},
325 {"list-style-image", NULL, 0, but_none},
326 {"list-style-position", cs3_list_style_positions, 1, but_none},
327 {"vertical-align", cs3_vertical_aligns, 0, but_none},
328 {"overflow", cs3_overflows, 0, but_none},
329 {"clip", cs3_zindices, 0, but_none},
330 {"visibility", cs3_visibilities, 0, but_none},
331 {"caption-side", cs3_caption_sides, 1, but_none},
332 {"table-layout", cs3_table_layouts, 1, but_none},
333 {"border-collapse", cs3_border_collapses, 1, but_none},
334 {"border-spacing", NULL, 0, but_none},
335 {"empty-cells", cs3_empty_cells, 1, but_none},
336 {"content", cs3_content, 0, but_none},
337 {"counter-increment", NULL, 0, but_none},
338 {"counter-reset", NULL, 0, but_none},
339 {"cursor", cs3_cursor, 0, but_none},
340 {"opacity", NULL, 0, but_none},
341 {"outline", NULL, 0, but_none},
342 {"outline-color", cs3_colors, 0, but_color},
343 {"outline-style", cs3_border_styles, 0, but_none},
344 {"outline-width", cs3_width, 0, but_none},
345 {"quotes", NULL, 0, but_none},
346 {NULL, NULL, 0, but_none}
347 };
348 
349 
cs3_arr_from_property(gchar * prop)350 static Tcs3_arr *cs3_arr_from_property(gchar *prop) {
351 	Tcs3_arr tmp;
352 	gint count=0;
353 
354 	if (!prop) {
355 		return NULL;
356 	}
357 	tmp = cs3_arr[count];
358 	while (tmp.property) {
359 		if (strcmp(tmp.property, prop) ==0) {
360 			return &cs3_arr[count];
361 		}
362 		count++;
363 		tmp = cs3_arr[count];
364 	}
365 	return NULL;
366 }
367 
368 /* the list should be freed, the contents of the list should NOT */
pointer_arr2glist(gchar ** arr)369 static GList *pointer_arr2glist(gchar **arr) {
370 	gchar **tmp;
371 	GList *retlist = NULL;
372 
373 	if (!arr) {
374 		return NULL;
375 	}
376 	tmp = arr;
377 	while (*tmp) {
378 		retlist = g_list_append(retlist, *tmp);
379 		tmp++;
380 	}
381 	return retlist;
382 }
383 
cs3d_destroy_lcb(GtkWidget * widget,Tcs3_diag * diag)384 static void cs3d_destroy_lcb(GtkWidget * widget, Tcs3_diag *diag) {
385 	window_destroy(diag->win);
386 	g_slice_free(Tcs3_diag, diag);
387 }
388 
cs3d_cancel_clicked_lcb(GtkWidget * widget,Tcs3_diag * diag)389 static void cs3d_cancel_clicked_lcb(GtkWidget * widget, Tcs3_diag *diag) {
390 	cs3d_destroy_lcb(NULL, diag);
391 }
392 
cs3d_ok_clicked_lcb(GtkWidget * widget,Tcs3_diag * diag)393 static void cs3d_ok_clicked_lcb(GtkWidget * widget, Tcs3_diag *diag) {
394 	Tcs3_destination dest = diag->dest;
395 	GtkTreeIter iter;
396 	GString *retstr;
397 	retstr = g_string_new("");
398 
399 	if (diag->styletype == onestyle) {
400 		gboolean retval = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(diag->lstore), &iter);
401 		while (retval) {
402 			gchar *text[3];
403 			gtk_tree_model_get(GTK_TREE_MODEL(diag->lstore), &iter, 1, &text[1], 2, &text[2], -1);
404 			retstr = g_string_append(retstr, text[1]);
405 			retstr = g_string_append(retstr, ": ");
406 			retstr = g_string_append(retstr, text[2]);
407 			retstr = g_string_append(retstr, "; ");
408 			retval = gtk_tree_model_iter_next(GTK_TREE_MODEL(diag->lstore), &iter);
409 		}
410 	} else { /* multistyle */
411 		gchar *prev_selector = NULL;
412 		gboolean retval = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(diag->lstore), &iter);
413 		while (retval) {
414 			gchar *text[3];
415 			gtk_tree_model_get(GTK_TREE_MODEL(diag->lstore), &iter, 0, &text[0], 1, &text[1], 2, &text[2], -1);
416 			if (!prev_selector) {
417 				prev_selector = g_strdup(text[0]);
418 				retstr = g_string_append(retstr, text[0]);
419 				retstr = g_string_append(retstr, " {\n");
420 			} else if (g_strcmp0(prev_selector, text[0]) != 0 ) {
421 				retstr = g_string_append(retstr, "}\n");
422 				retstr = g_string_append(retstr, text[0]);
423 				retstr = g_string_append(retstr, " {\n");
424 				g_free(prev_selector);
425 				prev_selector = g_strdup(text[0]);
426 			}
427 			retstr = g_string_append(retstr, "\t");
428 			retstr = g_string_append(retstr, text[1]);
429 			retstr = g_string_append(retstr, ": ");
430 			retstr = g_string_append(retstr, text[2]);
431 			retstr = g_string_append(retstr, ";\n");
432 			retval = gtk_tree_model_iter_next(GTK_TREE_MODEL(diag->lstore), &iter);
433 		}
434 		g_free(prev_selector);
435 		/* only append '}\n' in case there is content */
436 		if (retstr->len > 0) {
437 			retstr = g_string_append(retstr, "}\n");
438 		}
439 	}
440 
441 	if (retstr->len > 0) {
442 		if (dest.dest_type == entry) {
443 			DEBUG_MSG("cs3d_ok_clicked_lcb, entry? %p\n", dest.entry);
444 			if (dest.entry && GTK_IS_WIDGET(dest.entry)) {
445 				DEBUG_MSG("cs3d_ok_clicked_lcb, entry!\n");
446 				gtk_entry_set_text(GTK_ENTRY(dest.entry), retstr->str);
447 			}
448 		} else if (dest.dest_type == textbox) {
449 			if (dest.doc) {
450 				DEBUG_MSG("cs3d_ok_clicked_lcb, textbox!\n");
451 				if (dest.doc_start != -1 || dest.doc_end != -1) {
452 					doc_replace_text(dest.doc, retstr->str, dest.doc_start, dest.doc_end);
453 				} else {
454 					doc_insert_two_strings(dest.doc, retstr->str, NULL);
455 				}
456 			}
457 		} else if (dest.dest_type == wizard) {
458 			GtkTextBuffer *buf;
459 			buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(dest.entry));
460 			gtk_text_buffer_set_text(buf, retstr->str, -1);
461 		} else {
462 #ifdef DEVELOPMENT
463 			g_print("cs3d_ok_clicked_lcb, an unknown dest type, this should never happen!\n");
464 #endif
465 		}
466 	}
467 	g_string_free(retstr, TRUE);
468 	cs3d_destroy_lcb(NULL, diag);
469 }
470 
cs3d_selection_changed_cb(GtkTreeSelection * selection,gpointer data)471 static void cs3d_selection_changed_cb(GtkTreeSelection *selection, gpointer data)
472 {
473 	Tcs3_diag *diag = data;
474 	GtkTreeIter iter;
475 	GtkTreeModel *model;
476 	gchar *text[3] = {NULL, NULL, NULL};
477 	if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
478 		gint *indices;
479 		GtkTreePath *path;
480 		gtk_tree_model_get(model, &iter, 0, &text[0], 1, &text[1], 2, &text[2], -1);
481 
482 		/* now set the diag->selected_row !!!!!!!!!!!!!!!!!!! */
483 		path = gtk_tree_model_get_path(model, &iter);
484 		indices = gtk_tree_path_get_indices(path);
485 		DEBUG_MSG("cs3d_selection_changed_cb, selected row=%d\n",indices[0]);
486 		diag->selected_row = indices[0];
487 		gtk_tree_path_free(path);
488 	}
489 	if (diag->styletype == multistyle) {
490 		gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(diag->selector))), text[0]?text[0]:"");
491 	}
492 	gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(diag->property))), text[1]?text[1]:"");
493 	gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(diag->value))), text[2]?text[2]:"");
494 
495 
496 }
497 
add_to_row(Tcs3_diag * diag,gint whichrow)498 static void add_to_row(Tcs3_diag *diag, gint whichrow) {
499 	gchar *text[3] = {NULL, NULL, NULL};
500 	gboolean correct=TRUE;
501 	gint count=1;
502 	if (diag->styletype == multistyle) {
503 		text[0] = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(diag->selector))), 0, -1);
504 		count=0;
505 	}
506 	text[1] = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(diag->property))), 0, -1);
507 	text[2] = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(diag->value))), 0, -1);
508 
509 	while (count < 3) {
510 		if (strlen(text[count]) == 0) {
511 			DEBUG_MSG("add_to_row, %s:%s:%s: %d %s is incorrect, will not add\n",text[0],text[1], text[2], count, text[count]);
512 			correct = FALSE;
513 			break;
514 		}
515 		count++;
516 	}
517 	if (correct) {
518 		GtkTreeIter iter;
519 		if (whichrow == -1) {
520 			gtk_list_store_append(GTK_LIST_STORE(diag->lstore),&iter);
521 		} else {
522 			gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(diag->lstore),&iter,NULL,whichrow);
523 		}
524 		gtk_list_store_set(GTK_LIST_STORE(diag->lstore), &iter, 0, text[0], 1, text[1], 2, text[2], -1);
525 	}
526 	g_free(text[0]);
527 	g_free(text[1]);
528 	g_free(text[2]);
529 }
530 
cs3d_add_clicked_lcb(GtkWidget * widget,Tcs3_diag * diag)531 static void cs3d_add_clicked_lcb(GtkWidget * widget, Tcs3_diag *diag) {
532 	add_to_row(diag, -1);
533 }
534 
cs3d_del_clicked_lcb(GtkWidget * widget,Tcs3_diag * diag)535 static void cs3d_del_clicked_lcb(GtkWidget * widget, Tcs3_diag *diag) {
536 	if (diag->selected_row != -1) {
537 		GtkTreeIter iter;
538 		gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(diag->lstore),&iter,NULL,diag->selected_row);
539 		gtk_list_store_remove(GTK_LIST_STORE(diag->lstore),&iter);
540 		/*gtk_clist_remove(GTK_CLIST(diag->clist), diag->selected_row);*/
541 		diag->selected_row = -1;
542 	}
543 }
544 
cs3d_update_clicked_lcb(GtkWidget * widget,Tcs3_diag * diag)545 static void cs3d_update_clicked_lcb(GtkWidget * widget, Tcs3_diag *diag) {
546 	if (diag->selected_row != -1) {
547 		DEBUG_MSG("cs3d_update_clicked_lcb, updating row=%d\n", diag->selected_row);
548 		add_to_row(diag, diag->selected_row);
549 	}
550 }
551 
cs3d_html5_clicked_lcb(GtkWidget * widget,Tcs3_diag * diag)552 static void cs3d_html5_clicked_lcb(GtkWidget * widget, Tcs3_diag *diag) {
553 	GList *tmplist, *list;
554 	list = glist_with_html_tags(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(diag->html5)));
555 #if GTK_CHECK_VERSION(3,0,0)
556 			gtk_combo_box_text_remove_all(GTK_COMBO_BOX_TEXT(diag->selector));
557 #else
558 			GtkListStore *store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(diag->selector)));
559   			gtk_list_store_clear(store);
560 #endif
561 	for (tmplist=g_list_first(list);tmplist;tmplist=g_list_next(tmplist)) {
562 		if (tmplist->data) {
563 			gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(diag->selector),tmplist->data);
564 		}
565 	}
566 	gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(diag->selector))), "");
567 	g_list_free(list);
568 	g_list_free(tmplist);
569 	tmplist = NULL;
570 }
571 
cs3d_prop_activate_lcb(GtkWidget * widget,Tcs3_diag * diag)572 static void cs3d_prop_activate_lcb(GtkWidget * widget, Tcs3_diag *diag) {
573 	Tcs3_arr *tmp;
574 	gchar *tmpstr;
575 
576 	gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(diag->value))), "");
577 #if GTK_CHECK_VERSION(3,0,0)
578 			gtk_combo_box_text_remove_all(GTK_COMBO_BOX_TEXT(diag->value));
579 #else
580 			GtkListStore *store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(diag->value)));
581   			gtk_list_store_clear(store);
582 #endif
583 	tmpstr = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(diag->property))), 0, -1);
584 	tmp = cs3_arr_from_property(tmpstr);
585 	g_free(tmpstr);
586 	if (tmp) {
587 		GList *list, *tmplist;
588 		list = pointer_arr2glist(tmp->possibilities);
589 		if (list) {
590 			gchar *tmpstr2=NULL;
591 			if (!tmp->force_pos) {
592 					tmpstr2 = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(diag->value))), 0, -1);
593 			}
594 
595 			for (tmplist=g_list_first(list);tmplist;tmplist=g_list_next(tmplist)) {
596 				if (tmplist->data) {
597 					gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(diag->value),tmplist->data);
598 				}
599 			}
600 			g_list_free(list);
601 			if (tmpstr2) {
602 				gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(diag->value))), tmpstr2);
603 				g_free(tmpstr2);
604 			}
605 			switch(tmp->buttype) {
606 			case but_color:
607 				gtk_widget_set_sensitive(diag->extra_but, TRUE);
608 			break;
609 			default:
610 				gtk_widget_set_sensitive(diag->extra_but, FALSE);
611 			break;
612 			}
613 		} else {
614 				gtk_widget_set_sensitive(diag->extra_but, FALSE);
615 		}
616 		gtk_editable_set_editable(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(diag->value))), !tmp->force_pos);
617 	} else {
618 		gtk_widget_set_sensitive(diag->extra_but, FALSE);
619 	}
620 }
621 
css_diag(Tcs3_destination dest,Tcs3_style style,GtkWidget * transient_win,gboolean grab)622 static Tcs3_diag *css_diag(Tcs3_destination dest, Tcs3_style style, GtkWidget *transient_win, gboolean grab) {
623 
624 	Tcs3_diag *diag;
625 	GtkWidget *scrolwin, *table, *but, *vbox, *hbox, *vbox2;
626 	GList *tmplist = NULL;
627 	Tcs3_arr tmp;
628 	gint count=0;
629 	GtkTreeSelection *selection;
630 	GtkCellRenderer *renderer;
631 	GtkTreeViewColumn *column;
632 
633 	diag = g_slice_new(Tcs3_diag);
634 	diag->win = window_full2(_("Cascading Style Sheet Builder"), GTK_WIN_POS_CENTER_ON_PARENT,
635 			12, G_CALLBACK(cs3d_destroy_lcb), diag, TRUE, transient_win);
636 	gtk_window_set_role(GTK_WINDOW(diag->win), "css");
637 	diag->dest = dest;
638 	diag->styletype = style;
639 	diag->grab = grab;
640 	diag->selected_row = -1;
641 
642 	vbox = gtk_vbox_new(FALSE, 0);
643 	gtk_container_add(GTK_CONTAINER(diag->win),vbox);
644 
645 	table = gtk_table_new(3, 6, TRUE);
646 	gtk_table_set_row_spacings(GTK_TABLE(table), 12);
647 	gtk_table_set_col_spacings(GTK_TABLE(table), 12);
648 	gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
649 	tmplist = NULL;
650 
651 	if (diag->styletype == multistyle) {
652 		tmplist = glist_with_html_tags(0);
653 		diag->selector = combobox_with_popdown("", tmplist,1);
654 		dialog_mnemonic_label_in_table(_("_Selector(s):"), diag->selector, table, 0, 1, 0, 1);
655 		gtk_table_attach_defaults(GTK_TABLE(table), diag->selector, 1 ,5 , 0, 1);
656 		gtk_combo_box_set_wrap_width(GTK_COMBO_BOX(diag->selector), 5);
657 		gtk_combo_box_set_add_tearoffs(GTK_COMBO_BOX(diag->selector), 1);
658 
659 		diag->html5 = gtk_check_button_new_with_mnemonic("_html 5");
660 		gtk_table_attach_defaults(GTK_TABLE(table), diag->html5, 5,6, 0,1);
661 		g_signal_connect(diag->html5, "clicked", G_CALLBACK(cs3d_html5_clicked_lcb), diag);
662 
663 		g_list_free(tmplist);
664 		tmplist = NULL;
665 	}
666 
667 	tmp = cs3_arr[count];
668 	while (tmp.property) {
669 		tmplist = g_list_append(tmplist, tmp.property);
670 		count++;
671 		tmp = cs3_arr[count];
672 	}
673 	diag->property = combobox_with_popdown("", tmplist,1);
674 	g_list_free(tmplist);
675 	tmplist = NULL;
676 	g_signal_connect(gtk_bin_get_child(GTK_BIN(diag->property)), "activate", G_CALLBACK(cs3d_prop_activate_lcb), diag);
677 	g_signal_connect(gtk_bin_get_child(GTK_BIN(diag->property)), "changed", G_CALLBACK(cs3d_prop_activate_lcb), diag);
678 
679 	diag->value = combobox_with_popdown("", tmplist,1);
680 	dialog_mnemonic_label_in_table(_("_Property:"), diag->property, table, 0, 1, 1, 2);
681 	gtk_table_attach_defaults(GTK_TABLE(table), diag->property, 1, 5, 1, 2);
682 	gtk_combo_box_set_wrap_width(GTK_COMBO_BOX(diag->property), 4);
683 	gtk_combo_box_set_add_tearoffs(GTK_COMBO_BOX(diag->property), 1);
684 	dialog_mnemonic_label_in_table(_("_Value:"), diag->value, table, 0, 1, 2, 3);
685 	gtk_table_attach_defaults(GTK_TABLE(table), diag->value, 1, 4, 2, 3);
686 
687 
688 	gtk_widget_realize(diag->win);
689 
690 
691 	diag->extra_but = color_but_new(GTK_WIDGET(gtk_bin_get_child(GTK_BIN(diag->value))), diag->win);
692 	gtk_table_attach(GTK_TABLE(table), diag->extra_but, 4, 5, 2, 3, GTK_EXPAND, GTK_EXPAND, 0, 0);
693 
694 	/* the list widget and the buttons are in a horizontal box */
695 	hbox = gtk_hbox_new(FALSE, 12);
696 	gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 18);
697 
698 	diag->lstore = gtk_list_store_new(3, G_TYPE_STRING,G_TYPE_STRING,G_TYPE_STRING);
699 	gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(diag->lstore),0,GTK_SORT_ASCENDING);
700 	diag->lview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(diag->lstore));
701 	g_object_unref(G_OBJECT(diag->lstore));
702 	if (diag->styletype == multistyle) {
703 		renderer = gtk_cell_renderer_text_new();
704 		column = gtk_tree_view_column_new_with_attributes (_("Selector(s)"),renderer,"text", 0,NULL);
705 		gtk_tree_view_append_column(GTK_TREE_VIEW(diag->lview), column);
706 	}
707 	renderer = gtk_cell_renderer_text_new();
708 	column = gtk_tree_view_column_new_with_attributes (_("Property"),renderer,"text", 1,NULL);
709 	gtk_tree_view_append_column(GTK_TREE_VIEW(diag->lview), column);
710 	renderer = gtk_cell_renderer_text_new();
711 	column = gtk_tree_view_column_new_with_attributes (_("Value"),renderer,"text", 2,NULL);
712 	gtk_tree_view_append_column(GTK_TREE_VIEW(diag->lview), column);
713 	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(diag->lview));
714 	gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
715 	g_signal_connect(G_OBJECT(selection), "changed",G_CALLBACK(cs3d_selection_changed_cb),diag);
716 	scrolwin = gtk_scrolled_window_new(NULL, NULL);
717 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolwin), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
718 	gtk_widget_set_size_request(scrolwin, 400, 300);
719 	gtk_box_pack_start(GTK_BOX(hbox), scrolwin, TRUE, TRUE, 0);
720 	gtk_container_add(GTK_CONTAINER(scrolwin), diag->lview);
721 	/*if (diag->styletype == multistyle) {
722 		gchar *titles[] = {_("Selector"), _("Property"), _("Value"), NULL};
723 		diag->clist = gtk_clist_new_with_titles(3, titles);
724 	} else {
725 		gchar *titles[] = {_("Property"), _("Value"), NULL};
726 		diag->clist = gtk_clist_new_with_titles(2, titles);
727 	}
728 	gtk_clist_set_sort_column(GTK_CLIST(diag->clist), 0);
729 	gtk_clist_set_auto_sort(GTK_CLIST(diag->clist), TRUE);
730 
731 	g_signal_connect(diag->clist, "select_row", G_CALLBACK(cs3d_select_row_lcb), diag);
732 	g_signal_connect(diag->clist, "unselect_row", G_CALLBACK(cs3d_unselect_row_lcb), diag);
733 	gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolwin), diag->lview);
734 	*/
735 
736 	vbox2 = gtk_vbox_new(FALSE, 6);
737 	gtk_box_pack_start(GTK_BOX(hbox), vbox2, FALSE, FALSE, 0);
738 
739 	but = gtk_button_new_with_mnemonic(_(" _Add "));
740 	g_signal_connect(but, "clicked", G_CALLBACK(cs3d_add_clicked_lcb), diag);
741 	gtk_box_pack_start(GTK_BOX(vbox2), but, FALSE, FALSE, 0);
742 
743 	but = gtk_button_new_with_mnemonic(_(" _Update "));
744 	g_signal_connect(but, "clicked", G_CALLBACK(cs3d_update_clicked_lcb), diag);
745 	gtk_box_pack_start(GTK_BOX(vbox2), but, FALSE, FALSE, 0);
746 
747 	but = gtk_button_new_with_mnemonic(_(" _Delete "));
748 	g_signal_connect(but, "clicked", G_CALLBACK(cs3d_del_clicked_lcb), diag);
749 	gtk_box_pack_start(GTK_BOX(vbox2), but, FALSE, FALSE, 0);
750 
751 	/* the ok and cancel button are in a horizontal box below */
752 	hbox = gtk_hbox_new(FALSE, 0);
753 	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 12);
754 #if GTK_CHECK_VERSION(3,0,0)
755 	hbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
756 #else
757 	hbox = gtk_hbutton_box_new();
758 #endif
759 	gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
760 	gtk_box_set_spacing(GTK_BOX(hbox), 12);
761 
762 	but = bf_stock_cancel_button(G_CALLBACK(cs3d_cancel_clicked_lcb), diag);
763 	gtk_box_pack_start(GTK_BOX(hbox), but, FALSE, FALSE, 0);
764 	but = bf_stock_ok_button(G_CALLBACK(cs3d_ok_clicked_lcb), diag);
765 	gtk_box_pack_start(GTK_BOX(hbox), but, FALSE, FALSE, 0);
766 	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
767 
768 	gtk_widget_show_all(diag->win);
769 
770 	cs3d_prop_activate_lcb(NULL, diag);
771 
772 	if (diag->grab) {
773 		gtk_grab_add(diag->win);
774 	}
775 	return diag;
776 }
777 
778 typedef enum {property, value, end} Tcs3_prevtype_onestyle;
779 typedef enum {mselector, mproperty, mvalue, mend, mselectorend} Tcs3_prevtype_multistyle;
780 
781 /* static void css_parse(Tcs3_diag *diag, gchar *data)
782  * the buffer 'data' should contain a css stylesheet
783  * the comments are removed from this buffer by this function
784  * so if you don't want this, make a copy of the stylesheet
785  * before you pass it on to this function
786  */
css_parse(Tcs3_diag * diag,gchar * data)787 static void css_parse(Tcs3_diag *diag, gchar *data) {
788 	gchar *tmp = data;
789 	gint count = 0, parsable_css = 1;
790 
791 	if (!data) {
792 		DEBUG_MSG("css_parse, no data!\n");
793 		return;
794 	}
795 
796 	if (diag->styletype == onestyle) {
797 		gint prev_end = 0;
798 		Tcs3_prevtype_onestyle prevtype = end;
799 		gchar *text[3] = {NULL, NULL, NULL};
800 
801 		DEBUG_MSG("css_parse, onestyle\n");
802 		while (tmp[count] && parsable_css) {
803 			switch (tmp[count]) {
804 			case ':':
805 				if (prevtype == end) {
806 					text[0] = g_strndup(&tmp[prev_end], count - prev_end);
807 					strip_any_whitespace(text[0]);
808 					prev_end = count+1;
809 					prevtype = property;
810 				}
811 			break;
812 			case ';':
813 				if (prevtype == property) {
814 					text[1] = g_strndup(&tmp[prev_end], count - prev_end);
815 					strip_any_whitespace(text[1]);
816 					prev_end = count+1;
817 					prevtype = value;
818 				}
819 			break;
820 			case '*':
821 				if ((count > 0)&& (tmp[count-1] == '/')) {
822 					gint count2 = count, cont=1, movelen;
823 					DEBUG_MSG("css_parse, found the start of a comment on %d\n", count);
824 					while (tmp[++count2] && cont) {
825 						if (tmp[count2] == '/' && tmp[count2-1] == '*') {
826 							cont=0;
827 						}
828 					}
829 					DEBUG_MSG("css_parse, before move, tmp='%s'\n", tmp);
830 					movelen = strlen(&tmp[count2]);
831 					memmove(&tmp[count-1], &tmp[count2], movelen);
832 					tmp[count-1 + movelen] = '\0';
833 					DEBUG_MSG("css_parse, after move, tmp='%s'\n", tmp);
834 				}
835 			break;
836 			case '<':
837 			case '>':
838 				parsable_css = 0;
839 				DEBUG_MSG("css_parse, this is not a valid stylesheet\n");
840 			break;
841 
842 			default:
843 				/* do nothing */
844 			break;
845 			}
846 			if (prevtype == value) {
847 				GtkTreeIter iter;
848 				gtk_list_store_append(diag->lstore, &iter);
849 				gtk_list_store_set(diag->lstore,&iter,1, text[0],2,text[1],-1);
850 				/*gtk_clist_append(GTK_CLIST(diag->clist), text);*/
851 				g_free(text[0]);
852 				g_free(text[1]);
853 				prevtype = end;
854 			}
855 			count++;
856 		}
857 
858 
859 	} else { /* multistyle */
860 		gint prev_end = 0, i;
861 		Tcs3_prevtype_multistyle prevtype = mselectorend;
862 		gchar *text[4] = {NULL, NULL, NULL, NULL};
863 
864 		DEBUG_MSG("css_parse, multistylestyle\n");
865 
866 		while (tmp[count] && parsable_css) {
867 			switch (tmp[count]) {
868 			case '}': /* found end of block */
869 				if (prevtype != mselectorend) {
870 					prevtype = mselectorend;
871 					prev_end = count+1;
872 					g_free(text[0]);
873 					text[0]=NULL;
874 				}
875 			break;
876 			case '{': /* found selector */
877 				if (prevtype == mselectorend) {
878 					text[0] = g_strndup(&tmp[prev_end], count - prev_end);
879 					strip_any_whitespace(text[0]);
880 					prev_end = count+1;
881 					prevtype = mselector;
882 				}
883 			break;
884 			case ':':
885 				if (prevtype == mselector || prevtype == mend) {
886 					text[1] = g_strndup(&tmp[prev_end], count - prev_end);
887 					strip_any_whitespace(text[1]);
888 					prev_end = count+1;
889 					prevtype = mproperty;
890 				}
891 			break;
892 			case ';':
893 				if (prevtype == mproperty) {
894 					text[2] = g_strndup(&tmp[prev_end], count - prev_end);
895 					strip_any_whitespace(text[2]);
896 					prev_end = count+1;
897 					prevtype = mvalue;
898 				}
899 			break;
900 			case '*':
901 				if ((count > 0)&& (tmp[count-1] == '/')) {
902 					gint count2 = count, cont=1, movelen;
903 					DEBUG_MSG("css_parse, found the start of a comment on %d\n", count);
904 					while (tmp[++count2] && cont) {
905 						if (tmp[count2] == '/' && tmp[count2-1] == '*') {
906 							cont=0;
907 						}
908 					}
909 					DEBUG_MSG("css_parse, before move, tmp='%s'\n", tmp);
910 					movelen = strlen(&tmp[count2]);
911 					memmove(&tmp[count-1], &tmp[count2], movelen);
912 					tmp[count-1 + movelen] = '\0';
913 					DEBUG_MSG("css_parse, after move, tmp='%s'\n", tmp);
914 				}
915 			break;
916 			case '<':
917 			case '>':
918 				parsable_css = 0;
919 				DEBUG_MSG("css_parse, this is not a valid stylesheet\n");
920 			break;
921 			default:
922 				/* do nothing */
923 			break;
924 			}
925 			if (prevtype == mvalue) {
926 				GtkTreeIter iter;
927 				gtk_list_store_append(diag->lstore, &iter);
928 				gtk_list_store_set(diag->lstore,&iter,0,text[0],1, text[1],2,text[2],-1);
929 
930 				/*gtk_clist_append(GTK_CLIST(diag->clist), text);*/
931 				g_free(text[1]);
932 				text[1] = NULL;
933 				g_free(text[2]);
934 				text[2] = NULL;
935 				prevtype = mend;
936 			}
937 			count++;
938 		}
939 		for (i=0;i<4;i++) {
940 			if (text[i]){
941 				g_free(text[i]);
942 			}
943 		}
944 	}
945 	/*gtk_clist_sort(GTK_CLIST(diag->clist));*/
946 	DEBUG_MSG("css_parse, finished\n");
947 }
948 
new_css_dialog(GtkWidget * wid,Tbfwin * bfwin)949 void new_css_dialog(GtkWidget *wid, Tbfwin *bfwin) {
950 	Tcs3_destination dest;
951 	Tcs3_diag *diag;
952 	gint sel_start, sel_end;
953 	gboolean has_selection;
954 
955 	dest.dest_type = textbox;
956 	dest.entry = NULL;
957 	dest.doc = bfwin->current_document;
958 	if (!doc_get_selection(dest.doc, &sel_start, &sel_end)) {
959 		dest.doc_start = -1;
960 		dest.doc_end = -1;
961 		has_selection = FALSE;
962 	} else {
963 		has_selection = TRUE;
964 		dest.doc_start = sel_start;
965 		dest.doc_end = sel_end;
966 	}
967 	if (dest.doc_start > dest.doc_end) {
968 		gint swap = dest.doc_start;
969 		dest.doc_start = dest.doc_end;
970 		dest.doc_end = swap;
971 	}
972 
973 	diag = css_diag(dest, multistyle, bfwin->main_window, FALSE);
974 	if (has_selection) {
975 		gchar *data;
976 
977 		data = doc_get_chars(dest.doc, sel_start, sel_end);
978 		DEBUG_MSG("new_css_dialog, data=%p\n", data);
979 		css_parse(diag, data);
980 		g_free(data);
981 	}
982 }
983 
style_but_clicked_lcb(GtkWidget * widget,GtkWidget * which_entry)984 static void style_but_clicked_lcb(GtkWidget * widget, GtkWidget * which_entry)
985 {
986 	Tcs3_destination dest;
987 	Tcs3_diag *diag;
988 	gchar *data;
989 	GtkWidget *win;
990 
991 	dest.dest_type = entry;
992 	dest.entry = which_entry;
993 	dest.doc = NULL;
994 
995 	win = gtk_widget_get_toplevel(which_entry);
996 	diag = css_diag(dest, onestyle, win, TRUE);
997 	data = gtk_editable_get_chars(GTK_EDITABLE(which_entry), 0, -1);
998 	css_parse(diag, data);
999 	g_free(data);
1000 }
1001 
style_but_new(GtkWidget * which_entry,GtkWidget * win)1002 GtkWidget *style_but_new(GtkWidget * which_entry, GtkWidget * win)
1003 {
1004 	GtkWidget *style_but, *hbox;
1005 
1006 	style_but = gtk_button_new();
1007 	hbox = gtk_hbox_new(FALSE, 0);
1008 	gtk_box_pack_start(GTK_BOX(hbox), gtk_image_new_from_stock(BF_STOCK_CSS_SMALL, GTK_ICON_SIZE_BUTTON),FALSE, FALSE, 3);
1009 	gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new_with_mnemonic(_("_Style...")), TRUE, TRUE, 3);
1010 	gtk_container_add(GTK_CONTAINER(style_but), hbox);
1011 	g_signal_connect(style_but, "clicked", G_CALLBACK(style_but_clicked_lcb), which_entry);
1012 	gtk_widget_show_all(style_but);
1013 	return style_but;
1014 }
1015 
style_but_for_wizard_clicked_lcb(GtkWidget * widget,GtkWidget * textview)1016 static void style_but_for_wizard_clicked_lcb(GtkWidget * widget, GtkWidget * textview)
1017 {
1018 	Tcs3_destination dest;
1019 	Tcs3_diag *diag;
1020 	gchar *data;
1021 	GtkWidget *win;
1022 
1023 	dest.dest_type = wizard;
1024 	dest.entry = textview;
1025 	dest.doc = NULL;
1026 
1027 	win = gtk_widget_get_toplevel(textview);
1028 	diag = css_diag(dest, multistyle, win, TRUE);
1029 	{
1030 		GtkTextBuffer *buf;
1031 		GtkTextIter itstart, itend;
1032 		buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview));
1033 		gtk_text_buffer_get_bounds(buf,&itstart,&itend);
1034 		data = gtk_text_buffer_get_text(buf, &itstart, &itend, FALSE);
1035 		css_parse(diag, data);
1036 		g_free(data);
1037 	}
1038 }
1039 
style_but_new_for_wizard(GtkWidget * textview)1040 GtkWidget *style_but_new_for_wizard(GtkWidget * textview) {
1041 	GtkWidget *style_but, *hbox;
1042 
1043 	style_but = gtk_button_new();
1044 	hbox = gtk_hbox_new(FALSE, 0);
1045 	gtk_box_pack_start(GTK_BOX(hbox), gtk_image_new_from_stock(BF_STOCK_CSS_SMALL, GTK_ICON_SIZE_BUTTON),FALSE, FALSE, 6);
1046 	gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new_with_mnemonic(_("_Style...")), TRUE, TRUE, 6);
1047 	gtk_container_add(GTK_CONTAINER(style_but), hbox);
1048 	g_signal_connect(style_but, "clicked", G_CALLBACK(style_but_for_wizard_clicked_lcb), textview);
1049 	gtk_widget_show_all(style_but);
1050 	return style_but;
1051 }
1052 
1053 /*****************/
1054 /* colorbut code */
1055 /*****************/
1056 
1057 typedef struct {
1058 	GtkWidget *win;
1059 	GtkWidget *hexentry;
1060 	GtkWidget *websafe;
1061 	GtkWidget *csel;
1062 	gint hex_changed_id;
1063 	gint csel_changed_id;
1064 	/* the dialog is dual functional: it can be called directly from the text, and it can be called by return_color
1065 	if called by return_color it is in the modal state */
1066 	gint is_modal;
1067 	/* for the modal state */
1068 	gchar *returnval;
1069 
1070 	/* for the non-modal state */
1071 	gint startpos;
1072 	gint endpos;
1073 	Tbfwin *bfwin;
1074 } Tcolsel;
1075 
colsel_destroy_lcb(GtkWidget * widget,Tcolsel * csd)1076 static void colsel_destroy_lcb(GtkWidget *widget, Tcolsel *csd) {
1077 	DEBUG_MSG("colsel_destroy_lcb, started for csd=%p\n",csd);
1078 	g_free(csd->returnval);
1079 	g_free(csd);
1080 }
1081 
colsel_ok_clicked_lcb(GtkWidget * widget,Tcolsel * csd)1082 static void colsel_ok_clicked_lcb(GtkWidget *widget, Tcolsel *csd) {
1083 	gchar *tmpstr;
1084 	GdkColor gcolor;
1085 	/* only on a OK click we do the setcolor thing */
1086 	gtk_color_selection_get_current_color(GTK_COLOR_SELECTION(csd->csel), &gcolor);
1087 
1088 	tmpstr = gdk_color_to_hexstring(&gcolor, FALSE);
1089 	if (csd->bfwin) {
1090 		csd->bfwin->session->colorlist = add_to_stringlist(csd->bfwin->session->colorlist, tmpstr);
1091 	}
1092 
1093 	if (csd->is_modal) {
1094 		g_free(csd->returnval);
1095 		csd->returnval = tmpstr;
1096 		gtk_main_quit();
1097 	} else {
1098 		if (string_is_color(tmpstr)) {
1099 			if (csd->startpos || csd->endpos) {
1100 				DEBUG_MSG("colsel_ok_clicked_lcb, replacing with %s\n", tmpstr);
1101 				doc_replace_text(csd->bfwin->current_document, tmpstr, csd->startpos, csd->endpos);
1102 			} else {
1103 				DEBUG_MSG("colsel_ok_clicked_lcb, inserting %s\n", tmpstr);
1104 				doc_insert_two_strings(csd->bfwin->current_document,tmpstr, NULL);
1105 			}
1106 		}
1107 		g_free(tmpstr);
1108 		window_destroy(csd->win);
1109 	}
1110 }
1111 
colsel_cancel_clicked_lcb(GtkWidget * widget,Tcolsel * csd)1112 static void colsel_cancel_clicked_lcb(GtkWidget *widget, Tcolsel *csd) {
1113 	window_destroy(csd->win);
1114 }
1115 
colsel_dialog(Tbfwin * bfwin,const gchar * setcolor,gint modal,gint startpos,gint endpos)1116 static Tcolsel *colsel_dialog(Tbfwin *bfwin,const gchar *setcolor, gint modal, gint startpos, gint endpos) {
1117 	Tcolsel *csd;
1118 	GtkWidget *vbox, *hbox, *but;
1119 	GdkColor gcolor;
1120 	const gchar *this_color=setcolor;
1121 	csd = g_new0(Tcolsel, 1);
1122 	/* warning: bfwin might be NULL if this dialog is started by return_color() */
1123 	csd->bfwin = bfwin;
1124 	csd->is_modal = modal;
1125 	csd->startpos = startpos;
1126 	csd->endpos = endpos;
1127 	csd->returnval = setcolor ? g_strdup(setcolor) : g_strdup("");
1128 
1129 	DEBUG_MSG("colsel_dialog, malloced at %p, setcolor=%s\n", csd, setcolor);
1130 	csd->win = window_full2(_("Bluefish: Select color"), GTK_WIN_POS_CENTER, 12, G_CALLBACK(colsel_destroy_lcb), csd, TRUE, bfwin?bfwin->main_window:NULL);
1131 	vbox = gtk_vbox_new(FALSE, 0);
1132 	gtk_container_add(GTK_CONTAINER(csd->win), vbox);
1133 	csd->csel = gtk_color_selection_new();
1134 	gtk_color_selection_set_has_opacity_control(GTK_COLOR_SELECTION(csd->csel), FALSE);
1135 	if (this_color) {
1136 		if (gdk_color_parse(this_color, &gcolor)) {
1137 			gtk_color_selection_set_current_color(GTK_COLOR_SELECTION(csd->csel), &gcolor);
1138 		} else {
1139 			this_color=NULL;
1140 		}
1141 	}
1142 	gtk_color_selection_set_has_palette(GTK_COLOR_SELECTION(csd->csel), TRUE);
1143 	gtk_box_pack_start(GTK_BOX(vbox), csd->csel, TRUE, TRUE, 0);
1144 
1145 	hbox = gtk_hbox_new(FALSE, 0);
1146 	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 12);
1147 #if GTK_CHECK_VERSION(3,0,0)
1148 	hbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
1149 #else
1150 	hbox = gtk_hbutton_box_new();
1151 #endif
1152 	gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
1153 	gtk_box_set_spacing(GTK_BOX(hbox), 12);
1154 	gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
1155 
1156 	but = bf_stock_cancel_button(G_CALLBACK(colsel_cancel_clicked_lcb), csd);
1157 	gtk_box_pack_start(GTK_BOX(hbox), but, TRUE, TRUE, 0);
1158 	but = bf_stock_ok_button(G_CALLBACK(colsel_ok_clicked_lcb), csd);
1159 	gtk_window_set_default(GTK_WINDOW(csd->win), but);
1160 	gtk_box_pack_start(GTK_BOX(hbox), but, TRUE, TRUE, 0);
1161 
1162 	if (bfwin && bfwin->session->colorlist) {
1163 		GtkSettings* gtksettings;
1164 		/* Note that this function can only be called when the GtkWidget is attached to a toplevel, since the settings object is specific to a particular GdkScreen.  */
1165 		gtksettings = gtk_widget_get_settings(GTK_WIDGET(csd->csel));
1166 		if (gtksettings) {
1167 			gchar *strings;
1168 			DEBUG_MSG("pallette list=%d\n",g_list_length(bfwin->session->colorlist));
1169 			bfwin->session->colorlist = limit_stringlist(bfwin->session->colorlist, 20, TRUE);
1170 			DEBUG_MSG("pallette list=%d\n",g_list_length(bfwin->session->colorlist));
1171 			strings = stringlist_to_string(bfwin->session->colorlist, ":");
1172 			strings[strlen(strings)-1] = '\0';
1173 			/* this property may contain max 20 colors, otherwise gtk will crash */
1174 			g_object_set(G_OBJECT(gtksettings), "gtk-color-palette", strings, NULL);
1175 			g_free(strings);
1176 		}
1177 /*		DEBUG_MSG("colsel_dialog, setting palette from %s\n", strings);
1178 		if (gtk_color_selection_palette_from_string(strings, &gcolorarr, &num)) {
1179 			DEBUG_MSG("num=%d, gcolorarr=%p\n",num,gcolorarr);
1180 		} else {
1181 			DEBUG_MSG("hmm, failed to parse our string :(\n");
1182 		} */
1183 	}
1184 
1185 	gtk_widget_show_all(csd->win);
1186 
1187 	DEBUG_MSG("colsel_dialog, finished\n");
1188 	return csd;
1189 }
1190 
edit_color_dialog(Tdocument * doc,gchar * color,gint startpos,gint endpos)1191 void edit_color_dialog(Tdocument *doc, gchar *color, gint startpos, gint endpos) {
1192 	colsel_dialog(doc->bfwin, color, FALSE, startpos, endpos);
1193 }
1194 
sel_colour_cb(GtkWidget * widget,Tbfwin * bfwin)1195 void sel_colour_cb(GtkWidget *widget, Tbfwin *bfwin) {
1196 	gchar *tmpstr=NULL;
1197 	gint startpos=0;
1198 	gint endpos=0;
1199 
1200 	if (doc_get_selection(bfwin->current_document,&startpos , &endpos)) {
1201 		DEBUG_MSG("sel_colour_cb, selection found\n");
1202 		if (startpos > endpos) {
1203 			gint tmpint;
1204 			tmpint = startpos;
1205 			startpos = endpos;
1206 			endpos = tmpint;
1207 		}
1208 		if ((endpos - startpos) == 7) {
1209 			tmpstr = doc_get_chars(bfwin->current_document,startpos,endpos);
1210 			if (!string_is_color(tmpstr)) {
1211 				startpos = endpos = 0;
1212 			}
1213 			DEBUG_MSG("sel_colour_cb, tmpstr = %s (startpos=%d, endpos=%d)\n", tmpstr, startpos, endpos);
1214 		} else {
1215 			DEBUG_MSG("sel_colour_cb, startpos=%d, endpos=%d\n", startpos, endpos);
1216 			startpos = endpos = 0;
1217 		}
1218 	} else {
1219 		DEBUG_MSG("sel_colour_cb, NO selection found\n");
1220 	}
1221 
1222 	colsel_dialog(bfwin, tmpstr, 0, startpos, endpos);
1223 	if (tmpstr)
1224 		g_free(tmpstr);
1225 }
1226 
return_color(gchar * start_value)1227 gchar *return_color(gchar *start_value) {
1228 	Tcolsel *csd;
1229 	gchar *return_text;
1230 
1231 	csd = colsel_dialog(NULL,start_value, 1, 0, 0);
1232 	DEBUG_MSG("return color, started\n");
1233 	gtk_grab_add(csd->win);
1234 	gtk_main();
1235 
1236 	return_text = g_strdup(csd->returnval);
1237 	window_destroy(csd->win);
1238 	DEBUG_MSG("return_color, the new gtk_main stopped, return_text=%s, %p\n", return_text, return_text);
1239 	return return_text;
1240 }
1241 
color_but_clicked(GtkWidget * widget,GtkWidget * entry)1242 static void color_but_clicked(GtkWidget * widget, GtkWidget * entry)
1243 {
1244 	gchar *tmpstring, *tmpstr2;
1245 
1246 	DEBUG_MSG("color_but_clicked, before return_color\n");
1247 	tmpstr2 = gtk_editable_get_chars(GTK_EDITABLE(entry),0 ,-1);
1248 	tmpstring = return_color(tmpstr2);
1249 	DEBUG_MSG("color_but_clicked, return_color=%s\n", tmpstring);
1250 	gtk_entry_set_text(GTK_ENTRY(entry), tmpstring);
1251 	g_free(tmpstring);
1252 	g_free(tmpstr2);
1253 
1254 }
1255 
color_but_new(GtkWidget * which_entry,GtkWidget * win)1256 GtkWidget *color_but_new(GtkWidget * which_entry, GtkWidget * win)
1257 {
1258 	GtkWidget *color_but;
1259 
1260 	color_but = gtk_button_new_from_stock(GTK_STOCK_SELECT_COLOR);
1261 	g_signal_connect(color_but, "clicked", G_CALLBACK(color_but_clicked), which_entry);
1262 	gtk_widget_show(color_but);
1263 	return color_but;
1264 }
1265