|
@@ -60,6 +60,8 @@ public final class Styliser {
|
60
|
60
|
public static final char CODE_NICKNAME = 16;
|
61
|
61
|
/** The character used for marking up fixed pitch text. */
|
62
|
62
|
public static final char CODE_FIXED = 17;
|
|
63
|
+ /** The character used for negating control codes. */
|
|
64
|
+ public static final char CODE_NEGATE = 18;
|
63
|
65
|
/** The character used for marking up italic text. */
|
64
|
66
|
public static final char CODE_ITALIC = 29;
|
65
|
67
|
/** The character used for marking up underlined text. */
|
|
@@ -71,7 +73,7 @@ public final class Styliser {
|
71
|
73
|
/** Regexp to match characters which shouldn't be used in channel links. */
|
72
|
74
|
private static final String RESERVED_CHARS = "[^\\s" + CODE_BOLD + CODE_COLOUR
|
73
|
75
|
+ CODE_STOP + CODE_HEXCOLOUR + CODE_FIXED + CODE_ITALIC
|
74
|
|
- + CODE_UNDERLINE + CODE_CHANNEL + CODE_NICKNAME + "\"]";
|
|
76
|
+ + CODE_UNDERLINE + CODE_CHANNEL + CODE_NICKNAME + CODE_NEGATE + "\"]";
|
75
|
77
|
|
76
|
78
|
/** Defines all characters allowed in URLs per W3 specs. */
|
77
|
79
|
private static final String URL_CHARS = "[a-z0-9$\\-_@\\.&\\+!\\*\"'\\(\\),=;/#\\?:%~]";
|
|
@@ -252,6 +254,7 @@ public final class Styliser {
|
252
|
254
|
pos = checkChar(pos, input.indexOf(CODE_HYPERLINK));
|
253
|
255
|
pos = checkChar(pos, input.indexOf(CODE_NICKNAME));
|
254
|
256
|
pos = checkChar(pos, input.indexOf(CODE_CHANNEL));
|
|
257
|
+ pos = checkChar(pos, input.indexOf(CODE_NEGATE));
|
255
|
258
|
|
256
|
259
|
return input.substring(0, pos);
|
257
|
260
|
}
|
|
@@ -278,27 +281,40 @@ public final class Styliser {
|
278
|
281
|
*/
|
279
|
282
|
private static int readControlChars(final String string,
|
280
|
283
|
final SimpleAttributeSet attribs, final boolean isStart) {
|
|
284
|
+ boolean isNegated = attribs.containsAttribute("NegateControl", Boolean.TRUE);
|
|
285
|
+
|
281
|
286
|
// Bold
|
282
|
287
|
if (string.charAt(0) == CODE_BOLD) {
|
283
|
|
- toggleAttribute(attribs, StyleConstants.FontConstants.Bold);
|
|
288
|
+ if (!isNegated) {
|
|
289
|
+ toggleAttribute(attribs, StyleConstants.FontConstants.Bold);
|
|
290
|
+ }
|
|
291
|
+
|
284
|
292
|
return 1;
|
285
|
293
|
}
|
286
|
294
|
|
287
|
295
|
// Underline
|
288
|
296
|
if (string.charAt(0) == CODE_UNDERLINE) {
|
289
|
|
- toggleAttribute(attribs, StyleConstants.FontConstants.Underline);
|
|
297
|
+ if (!isNegated) {
|
|
298
|
+ toggleAttribute(attribs, StyleConstants.FontConstants.Underline);
|
|
299
|
+ }
|
|
300
|
+
|
290
|
301
|
return 1;
|
291
|
302
|
}
|
292
|
303
|
|
293
|
304
|
// Italic
|
294
|
305
|
if (string.charAt(0) == CODE_ITALIC) {
|
295
|
|
- toggleAttribute(attribs, StyleConstants.FontConstants.Italic);
|
|
306
|
+ if (!isNegated) {
|
|
307
|
+ toggleAttribute(attribs, StyleConstants.FontConstants.Italic);
|
|
308
|
+ }
|
|
309
|
+
|
296
|
310
|
return 1;
|
297
|
311
|
}
|
298
|
312
|
|
299
|
313
|
// Hyperlinks
|
300
|
314
|
if (string.charAt(0) == CODE_HYPERLINK) {
|
301
|
|
- toggleLink(attribs);
|
|
315
|
+ if (!isNegated) {
|
|
316
|
+ toggleLink(attribs);
|
|
317
|
+ }
|
302
|
318
|
|
303
|
319
|
if (attribs.getAttribute(IRCTextAttribute.HYPERLINK) == null) {
|
304
|
320
|
attribs.addAttribute(IRCTextAttribute.HYPERLINK,
|
|
@@ -335,18 +351,24 @@ public final class Styliser {
|
335
|
351
|
|
336
|
352
|
// Fixed pitch
|
337
|
353
|
if (string.charAt(0) == CODE_FIXED) {
|
338
|
|
- if (attribs.containsAttribute(StyleConstants.FontConstants.FontFamily, "monospaced")) {
|
339
|
|
- attribs.removeAttribute(StyleConstants.FontConstants.FontFamily);
|
340
|
|
- } else {
|
341
|
|
- attribs.removeAttribute(StyleConstants.FontConstants.FontFamily);
|
342
|
|
- attribs.addAttribute(StyleConstants.FontConstants.FontFamily, "monospaced");
|
|
354
|
+ if (!isNegated) {
|
|
355
|
+ if (attribs.containsAttribute(StyleConstants.FontConstants.FontFamily, "monospaced")) {
|
|
356
|
+ attribs.removeAttribute(StyleConstants.FontConstants.FontFamily);
|
|
357
|
+ } else {
|
|
358
|
+ attribs.removeAttribute(StyleConstants.FontConstants.FontFamily);
|
|
359
|
+ attribs.addAttribute(StyleConstants.FontConstants.FontFamily, "monospaced");
|
|
360
|
+ }
|
343
|
361
|
}
|
|
362
|
+
|
344
|
363
|
return 1;
|
345
|
364
|
}
|
346
|
365
|
|
347
|
366
|
// Stop formatting
|
348
|
367
|
if (string.charAt(0) == CODE_STOP) {
|
349
|
|
- resetAttributes(attribs);
|
|
368
|
+ if (!isNegated) {
|
|
369
|
+ resetAttributes(attribs);
|
|
370
|
+ }
|
|
371
|
+
|
350
|
372
|
return 1;
|
351
|
373
|
}
|
352
|
374
|
|
|
@@ -362,9 +384,12 @@ public final class Styliser {
|
362
|
384
|
count++;
|
363
|
385
|
}
|
364
|
386
|
foreground = foreground % 16;
|
365
|
|
- setForeground(attribs, String.valueOf(foreground));
|
366
|
|
- if (isStart) {
|
367
|
|
- setDefaultForeground(attribs, String.valueOf(foreground));
|
|
387
|
+
|
|
388
|
+ if (!isNegated) {
|
|
389
|
+ setForeground(attribs, String.valueOf(foreground));
|
|
390
|
+ if (isStart) {
|
|
391
|
+ setDefaultForeground(attribs, String.valueOf(foreground));
|
|
392
|
+ }
|
368
|
393
|
}
|
369
|
394
|
|
370
|
395
|
// Now background
|
|
@@ -378,12 +403,15 @@ public final class Styliser {
|
378
|
403
|
count++;
|
379
|
404
|
}
|
380
|
405
|
background = background % 16;
|
381
|
|
- setBackground(attribs, String.valueOf(background));
|
382
|
|
- if (isStart) {
|
383
|
|
- setDefaultBackground(attribs, String.valueOf(background));
|
|
406
|
+
|
|
407
|
+ if (!isNegated) {
|
|
408
|
+ setBackground(attribs, String.valueOf(background));
|
|
409
|
+ if (isStart) {
|
|
410
|
+ setDefaultBackground(attribs, String.valueOf(background));
|
|
411
|
+ }
|
384
|
412
|
}
|
385
|
413
|
}
|
386
|
|
- } else {
|
|
414
|
+ } else if (!isNegated) {
|
387
|
415
|
resetColour(attribs);
|
388
|
416
|
}
|
389
|
417
|
return count;
|
|
@@ -393,28 +421,41 @@ public final class Styliser {
|
393
|
421
|
if (string.charAt(0) == CODE_HEXCOLOUR) {
|
394
|
422
|
int count = 1;
|
395
|
423
|
if (hasHexString(string, 1)) {
|
396
|
|
- setForeground(attribs, string.substring(1, 7).toUpperCase());
|
397
|
|
- if (isStart) {
|
398
|
|
- setDefaultForeground(attribs, string.substring(1, 7).toUpperCase());
|
|
424
|
+ if (!isNegated) {
|
|
425
|
+ setForeground(attribs, string.substring(1, 7).toUpperCase());
|
|
426
|
+ if (isStart) {
|
|
427
|
+ setDefaultForeground(attribs, string.substring(1, 7).toUpperCase());
|
|
428
|
+ }
|
399
|
429
|
}
|
|
430
|
+
|
400
|
431
|
count = count + 6;
|
401
|
432
|
|
402
|
433
|
// Now for background
|
403
|
434
|
if (string.charAt(count) == ',' && hasHexString(string, count + 1)) {
|
404
|
435
|
count++;
|
405
|
|
- setBackground(attribs, string.substring(count, count + 6).toUpperCase());
|
406
|
|
- if (isStart) {
|
407
|
|
- setDefaultBackground(attribs,
|
408
|
|
- string.substring(count, count + 6).toUpperCase());
|
|
436
|
+
|
|
437
|
+ if (!isNegated) {
|
|
438
|
+ setBackground(attribs, string.substring(count, count + 6).toUpperCase());
|
|
439
|
+ if (isStart) {
|
|
440
|
+ setDefaultBackground(attribs,
|
|
441
|
+ string.substring(count, count + 6).toUpperCase());
|
|
442
|
+ }
|
409
|
443
|
}
|
|
444
|
+
|
410
|
445
|
count += 6;
|
411
|
446
|
}
|
412
|
|
- } else {
|
|
447
|
+ } else if (!isNegated) {
|
413
|
448
|
resetColour(attribs);
|
414
|
449
|
}
|
415
|
450
|
return count;
|
416
|
451
|
}
|
417
|
452
|
|
|
453
|
+ // Control code negation
|
|
454
|
+ if (string.charAt(0) == CODE_NEGATE) {
|
|
455
|
+ toggleAttribute(attribs, "NegateControl");
|
|
456
|
+ return 1;
|
|
457
|
+ }
|
|
458
|
+
|
418
|
459
|
return 0;
|
419
|
460
|
}
|
420
|
461
|
|