23 package com.realtime.crossfire.jxclient.skin.io;
154 import java.awt.Color;
155 import java.awt.Component;
156 import java.awt.Container;
157 import java.awt.Font;
158 import java.awt.Image;
159 import java.awt.image.BufferedImage;
160 import java.io.IOException;
161 import java.io.InputStream;
162 import java.io.InputStreamReader;
163 import java.io.LineNumberReader;
164 import java.nio.charset.StandardCharsets;
165 import java.util.ArrayList;
166 import java.util.Collection;
167 import java.util.Comparator;
168 import java.util.List;
169 import java.util.TreeSet;
170 import javax.swing.GroupLayout;
171 import javax.swing.GroupLayout.Group;
172 import javax.swing.GroupLayout.SequentialGroup;
173 import javax.swing.SwingConstants;
174 import org.jetbrains.annotations.NotNull;
175 import org.jetbrains.annotations.Nullable;
424 private final List<ActivatableGUIElement>
focusOrder =
new ArrayList<>();
445 final Comparable<String> n1 = o1.getName();
446 final String n2 = o2.getName();
447 return n1.compareTo(n2);
481 public JXCSkinLoader(@NotNull
final Model model, @NotNull
final InventoryView inventoryView, @NotNull
final FloorView floorView, @NotNull
final SpellsView spellView, @NotNull
final SpellSkillView spellSkillsView, @NotNull
final FacesManager facesManager, @NotNull
final MapUpdaterState mapUpdaterState, @NotNull
final KeyBindings defaultKeyBindings, @NotNull
final OptionManager optionManager,
final int defaultTileSize, @NotNull
final KeybindingsManager keybindingsManager, @NotNull
final QuestsView questView, @NotNull
final CommandHistoryFactory commandHistoryFactory, @NotNull
final KnowledgeView knowledgeView, @NotNull
final KnowledgeTypeView knowledgeTypeView,
final boolean avoidCopyArea, @NotNull
final GuiManager guiManager, @NotNull
final GuiFactory guiFactory, @NotNull
final PendingDirections pendingDirections, @NotNull
final String
soundsCredits,
final float fontScaleFactor) {
527 public JXCSkin load(@NotNull
final JXCSkinSource skinSource, @NotNull
final CrossfireServerConnection crossfireServerConnection, @NotNull
final GuiStateManager guiStateManager, @NotNull
final TooltipManager tooltipManager, @NotNull
final JXCWindowRenderer windowRenderer, @NotNull
final GUIElementListener elementListener, @NotNull
final MetaserverModel metaserverModel, @NotNull
final CharacterModel characterModel, @NotNull
final CommandQueue commandQueue, @NotNull
final Shortcuts shortcuts, @NotNull
final CommandExecutor commandExecutor, @NotNull
final CommandCallback commandCallback, @NotNull
final Macros macros, @NotNull
final SmoothFaces smoothFaces)
throws JXCSkinException {
531 final Image nextGroupFace;
534 }
catch (
final IOException ex) {
537 final Image prevGroupFace;
540 }
catch (
final IOException ex) {
568 load(skinSource,
"global", crossfireServerConnection, guiStateManager, tooltipManager, windowRenderer, elementListener, metaserverModel, characterModel, commandQueue,
null, shortcuts, commandExecutor, commandCallback, macros, nextGroupFace, prevGroupFace, smoothFaces);
575 load(skinSource, name, crossfireServerConnection, guiStateManager, tooltipManager, windowRenderer, elementListener, metaserverModel, characterModel, commandQueue, gui, shortcuts, commandExecutor, commandCallback, macros, nextGroupFace, prevGroupFace, smoothFaces);
587 if (skinToDetach !=
null) {
588 skinToDetach.detach();
589 windowRenderer.setTooltip(
null);
619 private void load(@NotNull
final JXCSkinSource skinSource, @NotNull
final String dialogName, @NotNull
final CrossfireServerConnection server, @NotNull
final GuiStateManager guiStateManager, @NotNull
final TooltipManager tooltipManager, @NotNull
final JXCWindowRenderer windowRenderer, @NotNull
final GUIElementListener elementListener, @NotNull
final MetaserverModel metaserverModel, @NotNull
final CharacterModel characterModel, @NotNull
final CommandQueue commandQueue, @Nullable
final Gui gui, @NotNull
final Shortcuts shortcuts, @NotNull
final CommandExecutor commandExecutor, @NotNull
final CommandCallback commandCallback, @NotNull
final Macros macros, @NotNull
final Image nextGroupFace, @NotNull
final Image prevGroupFace, @NotNull
final SmoothFaces smoothFaces)
throws JXCSkinException {
620 String resourceName = dialogName+
".skin";
625 InputStream inputStream;
627 inputStream = skinSource.getInputStream(resourceName);
628 }
catch (
final IOException ignored) {
629 resourceName = dialogName+
".skin";
630 inputStream = skinSource.getInputStream(resourceName);
633 load(skinSource, dialogName, resourceName, inputStream, server, guiStateManager, tooltipManager, windowRenderer, elementListener, metaserverModel, characterModel, commandQueue, gui, shortcuts, commandExecutor, commandCallback, macros, nextGroupFace, prevGroupFace, smoothFaces);
638 throw new JXCSkinException(skinSource.getURI(resourceName)+
": "+ex.getMessage(), ex);
671 private void load(@NotNull
final JXCSkinSource skinSource, @NotNull
final String dialogName, @NotNull
final String resourceName, @NotNull
final InputStream inputStream, @NotNull
final CrossfireServerConnection server, @NotNull
final GuiStateManager guiStateManager, @NotNull
final TooltipManager tooltipManager, @NotNull
final JXCWindowRenderer windowRenderer, @NotNull
final GUIElementListener elementListener, @NotNull
final MetaserverModel metaserverModel, @NotNull
final CharacterModel characterModel, @NotNull
final CommandQueue commandQueue, @Nullable
final Gui gui, @NotNull
final Shortcuts shortcuts, @NotNull
final CommandExecutor commandExecutor, @NotNull
final CommandCallback commandCallback, @NotNull
final Macros macros, @NotNull
final Image nextGroupFace, @NotNull
final Image prevGroupFace, @NotNull
final SmoothFaces smoothFaces)
throws JXCSkinException {
673 try (InputStreamReader isr =
new InputStreamReader(inputStream, StandardCharsets.UTF_8)) {
674 try (LineNumberReader lnr =
new LineNumberReader(isr)) {
676 boolean isDialog =
false;
678 final String line = lnr.readLine();
683 if (line.startsWith(
"#") || line.isEmpty()) {
687 final String[] argsTmp;
693 final Args args =
new Args(argsTmp);
694 final String cmd = args.
get();
695 if (gui !=
null && cmd.equals(
"button")) {
696 parseButton(args, tooltipManager, elementListener, lnr);
697 }
else if (gui !=
null && cmd.equals(
"selectable_button")) {
699 }
else if (gui !=
null && cmd.equals(
"checkbox")) {
701 }
else if (gui !=
null && cmd.equals(
"combobox")) {
703 }
else if (gui !=
null && cmd.equals(
"spinner")) {
705 }
else if (cmd.equals(
"commandlist")) {
706 parseCommandList(args, guiStateManager, lnr, commandExecutor, commandQueue, server, commandCallback, macros);
707 }
else if (cmd.equals(
"commandlist_add")) {
708 parseCommandListAdd(args, guiStateManager, lnr, commandExecutor, commandQueue, server, commandCallback, macros);
709 }
else if (gui !=
null && cmd.equals(
"command_text")) {
710 parseCommandText(args, commandCallback, tooltipManager, elementListener, commandExecutor, server);
711 }
else if (cmd.equals(
"def")) {
713 }
else if (gui !=
null && cmd.equals(
"dialog")) {
715 throw new JXCSkinException(
"'dialog' must not be used more than once per dialog");
717 parseDialog(args, tooltipManager, windowRenderer, elementListener, lnr, gui, dialogName);
719 }
else if (gui !=
null && cmd.equals(
"dialog_hide")) {
721 }
else if (gui !=
null && cmd.equals(
"dupgauge")) {
723 }
else if (cmd.equals(
"event")) {
725 }
else if (gui !=
null && cmd.equals(
"fill")) {
726 parseFill(args, tooltipManager, elementListener);
727 }
else if (cmd.equals(
"font")) {
729 }
else if (gui !=
null && cmd.equals(
"gauge")) {
730 parseGauge(args, tooltipManager, elementListener, lnr);
731 }
else if (gui !=
null && cmd.equals(
"ignore")) {
733 }
else if (gui !=
null && cmd.equals(
"inventory_list")) {
735 }
else if (gui !=
null && cmd.equals(
"floor_list")) {
736 parseList(args,
ListType.
GROUND, tooltipManager, elementListener, commandQueue, server, nextGroupFace, prevGroupFace);
737 }
else if (gui !=
null && cmd.equals(
"spells_list")) {
738 parseList(args,
ListType.
SPELL, tooltipManager, elementListener, commandQueue, server, nextGroupFace, prevGroupFace);
739 }
else if (gui !=
null && cmd.equals(
"spell_skills")) {
741 }
else if (gui !=
null && cmd.equals(
"quests_list")) {
742 parseList(args,
ListType.
QUEST, tooltipManager, elementListener, commandQueue, server, nextGroupFace, prevGroupFace);
743 }
else if (gui !=
null && cmd.equals(
"knowledge_types")) {
745 }
else if (gui !=
null && cmd.equals(
"knowledge_list")) {
747 }
else if (gui !=
null && cmd.equals(
"horizontal")) {
749 }
else if (gui !=
null && cmd.equals(
"item")) {
750 parseItem(args, tooltipManager, elementListener, commandQueue, commandExecutor, server, shortcuts, nextGroupFace, prevGroupFace);
751 }
else if (cmd.equals(
"key")) {
753 }
else if (gui !=
null && cmd.equals(
"label_html")) {
755 }
else if (gui !=
null && cmd.equals(
"label_multi")) {
757 }
else if (gui !=
null && cmd.equals(
"label_query")) {
759 }
else if (gui !=
null && cmd.equals(
"label_failure")) {
761 }
else if (gui !=
null && cmd.equals(
"label_message")) {
762 parseLabelMessage(args, tooltipManager, elementListener, server, windowRenderer);
763 }
else if (gui !=
null && cmd.equals(
"label_text")) {
765 }
else if (gui !=
null && cmd.equals(
"label_stat")) {
767 }
else if (gui !=
null && cmd.equals(
"label_stat2")) {
769 }
else if (gui !=
null && cmd.equals(
"label_newchar")) {
771 }
else if (gui !=
null && cmd.equals(
"label_sounds_credits")) {
773 }
else if (gui !=
null && cmd.equals(
"link_size")) {
775 }
else if (gui !=
null && cmd.equals(
"log_label")) {
777 }
else if (gui !=
null && cmd.equals(
"log_message")) {
779 }
else if (gui !=
null && cmd.equals(
"log_color")) {
781 }
else if (gui !=
null && cmd.equals(
"log_filter")) {
783 }
else if (gui !=
null && cmd.equals(
"minimap")) {
785 }
else if (gui !=
null && cmd.equals(
"map")) {
786 parseMap(args, tooltipManager, elementListener, server, smoothFaces);
787 }
else if (gui !=
null && cmd.equals(
"map_directions")) {
789 }
else if (gui !=
null && cmd.equals(
"meta_list")) {
790 parseMetaList(args, tooltipManager, elementListener, metaserverModel);
791 }
else if (gui !=
null && cmd.equals(
"picture")) {
792 parsePicture(args, tooltipManager, elementListener, lnr, gui,
false);
793 }
else if (gui !=
null && cmd.equals(
"picture_scaled")) {
794 parsePicture(args, tooltipManager, elementListener, lnr, gui,
true);
795 }
else if (gui !=
null && cmd.equals(
"picture_stat")) {
797 }
else if (gui !=
null && cmd.equals(
"query_text")) {
798 parseQueryText(args, server, commandCallback, tooltipManager, elementListener);
799 }
else if (gui !=
null && cmd.equals(
"set_forced_active")) {
801 }
else if (gui !=
null && cmd.equals(
"set_auto_size")) {
803 }
else if (gui !=
null && cmd.equals(
"set_default")) {
805 }
else if (gui !=
null && cmd.equals(
"set_invisible")) {
807 }
else if (gui !=
null && cmd.equals(
"set_modal")) {
809 }
else if (gui !=
null && cmd.equals(
"set_user_resizable")) {
811 }
else if (gui !=
null && cmd.equals(
"scrollbar")) {
813 }
else if (gui ==
null && cmd.equals(
"skin_name")) {
815 }
else if (gui !=
null && cmd.equals(
"text")) {
816 parseText(args, commandCallback, tooltipManager, elementListener);
817 }
else if (gui !=
null && cmd.equals(
"textbutton")) {
819 }
else if (gui !=
null && cmd.equals(
"textgauge")) {
821 }
else if (cmd.equals(
"tooltip")) {
823 }
else if (gui !=
null && cmd.equals(
"vertical")) {
825 }
else if (cmd.equals(
"character_list")) {
827 }
else if (cmd.equals(
"hide_input")) {
829 }
else if (gui !=
null && cmd.equals(
"focus")) {
832 throw new IOException(
"unknown keyword '"+cmd+
"'");
836 throw new IOException(
"excess arguments");
840 throw new IOException(ex.getMessage()+
" in line "+lnr.getLineNumber(), ex);
841 }
catch (
final IllegalArgumentException ex) {
842 final Object msg = ex.getMessage();
843 throw new IOException(
"invalid parameter"+(msg ==
null ?
"" :
" ("+msg+
")")+
" in line "+lnr.getLineNumber(), ex);
858 final Collection<GUIElement> unreferencedElements =
new TreeSet<>(
ELEMENT_COMPARATOR);
861 unreferencedElements.add(element);
865 if (!unreferencedElements.isEmpty()) {
866 throw new IOException(
"missing elements in focus block: "+unreferencedElements);
876 }
catch (
final IOException ex) {
877 throw new JXCSkinException(skinSource.getURI(resourceName)+
": "+ex.getMessage(), ex);
893 final String name = args.get();
898 @Nullable
final String label;
899 @Nullable
final Font font;
900 @Nullable
final Color color;
903 if (args.hasMore()) {
916 insertGuiElement(
new GUIButton(tooltipManager, elementListener, name, upImage, downImage, label, font, color, textX, textY, autoRepeat, commandList,
guiFactory,
newCharModel));
929 final String name = args.get();
949 throw new IOException(
"missing 'def checkbox' command");
952 final String name = args.get();
968 final String name = args.get();
969 final String type = args.get();
976 default ->
throw new IOException(
"undefined 'combobox' type '"+type+
"'");
990 final String name = args.get();
1009 final String commandListName = args.get();
1012 if (args.hasMore()) {
1014 addCommand(commandListName, args, element, args.get(), guiStateManager, commandExecutor, lnr, commandQueue, server, commandCallback, macros);
1032 final String name = args.get();
1034 addCommand(name, args, element, args.get(), guiStateManager, commandExecutor, lnr, commandQueue, server, commandCallback, macros);
1049 final String name = args.get();
1058 insertGuiElement(
new GUICommandText(commandCallback, commandHistory, tooltipManager, elementListener, name,
newCharModel, activeImage, inactiveImage, font, inactiveColor, activeColor, margin,
"", commandExecutor,
guiFactory, crossfireServerConnection));
1069 final String type = args.get();
1080 case "checkbox_option":
1081 final String optionName = args.get();
1090 final String frame = args.get();
1104 if (alpha < 0 || alpha > 1.0F) {
1105 throw new IOException(
"invalid alpha value: "+alpha);
1107 dialogFactory =
new DialogFactory(frameNW, frameN, frameNE, frameW, frameC, frameE, frameSW, frameS, frameSE, titleFont, titleColor, titleBackgroundColor, alpha,
guiFactory);
1133 defaultItemPainter =
new ItemPainter(cursedImage, damnedImage, magicImage, blessedImage, appliedImage, unidentifiedImage, selectorImage, lockedImage, unpaidImage, markedImage, cursedColor, damnedColor, magicColor, blessedColor, appliedColor, unidentifiedColor, selectorColor, lockedColor, unpaidColor, markedColor, font, nrofColor);
1138 final String up = args.get();
1139 final String down = args.get();
1156 throw new IOException(
"unknown type '"+type+
"'");
1174 throw new IOException(
"missing 'def dialog' command");
1182 final Iterable<AbstractGUIElement> elements =
dialogFactory.
newDialog(tooltipManager, windowRenderer, elementListener, title);
1186 gui.getComponent().setName(dialogName);
1187 gui.setDefaultPosition(defaultX, defaultY);
1189 gui.setSaveDialog();
1202 }
while (args.hasMore());
1215 final String name = args.get();
1226 final GUIDupGauge element =
new GUIDupGauge(tooltipManager, elementListener, name, positiveDivImage, positiveModImage, emptyImage, orientationDiv, orientationMod, tooltipPrefix.isEmpty() ?
null : tooltipPrefix, color, font, commandList,
guiFactory);
1240 final String type = args.get();
1277 final String subtype = args.get();
1290 throw new IOException(
"undefined event sub-type: "+subtype);
1295 throw new IOException(
"undefined event type: "+type);
1308 final String name = args.get();
1311 if (alpha < 0 || alpha > 1.0F) {
1312 throw new IOException(
"invalid alpha value: "+alpha);
1324 final String name = args.get();
1341 throw new IOException(
"missing 'def dialog' command");
1344 final String name = args.get();
1345 final Image positiveImage = args.get().equals(
"null") ? null :
imageParser.
getImage(args.getPrev());
1346 final Image positiveLowImage = args.get().equals(
"null") ? null :
imageParser.
getImage(args.getPrev());
1347 final Image negativeImage = args.get().equals(
"null") ? null :
imageParser.
getImage(args.getPrev());
1348 final Image emptyImage = args.get().equals(
"null") ? null :
imageParser.
getImage(args.getPrev());
1355 final GUIGauge element =
new GUIGauge(tooltipManager, elementListener, name, positiveImage, positiveLowImage, negativeImage, emptyImage, orientation, tooltipFormat.isEmpty() ?
null : tooltipFormat, alpha, commandList,
guiFactory,
model.
getStats());
1367 final String name = args.get();
1387 throw new IOException(
"cannot use '"+type+
"_list"+
"' without 'def item' command");
1390 final String name = args.get();
1400 element =
new GUIInventoryList(tooltipManager, elementListener, name, cellWidth, cellHeight,
inventoryView, selectedItem, commandQueue, itemPainter, server,
facesManager,
floorView,
guiFactory);
1404 element =
new GUIFloorList(tooltipManager, elementListener, name, cellWidth, cellHeight,
floorView, selectedItem, commandQueue, itemPainter, server,
facesManager,
model.
getItemSet(), nextGroupFace, prevGroupFace,
guiFactory);
1408 element =
new GUISpellList(tooltipManager, elementListener, name, cellWidth, cellHeight,
spellView, selectedItem,
model.
getSpellsManager(),
keybindingsManager, commandQueue, itemPainter,
facesManager,
guiFactory);
1412 element =
new GUIQuestList(tooltipManager, elementListener, name, cellWidth, cellHeight,
questView, selectedItem,
model.
getQuestsManager(), commandQueue, itemPainter,
facesManager,
guiFactory);
1417 element =
new GUISpellSkillList(tooltipManager, elementListener, name, cellWidth, cellHeight,
spellSkillsView, selectedItem,
model.
getSpellsManager(), itemPainter,
facesManager, defaultSkillIcon,
guiFactory);
1420 case KNOWLEDGE_TYPES:
1421 element =
new GUIKnowledgeTypeList(tooltipManager, elementListener, name, cellWidth, cellHeight,
knowledgeTypeView, selectedItem,
model.
getKnowledgeManager(), itemPainter,
facesManager,
guiFactory);
1424 case KNOWLEDGE_LIST:
1425 element =
new GUIKnowledgeList(tooltipManager, elementListener, name, cellWidth, cellHeight,
knowledgeView, selectedItem, itemPainter,
facesManager,
model.
getKnowledgeManager(), commandQueue,
guiFactory);
1429 throw new AssertionError(
"unhandled type "+type);
1444 final GroupLayout layout = (GroupLayout)gui.getLayout();
1445 final String begin = args.get();
1446 if (!begin.equals(
"begin")) {
1447 throw new IOException(
"syntax error: expecting 'begin'");
1449 final Collection<GUIElement> unreferencedElements =
new TreeSet<>(
ELEMENT_COMPARATOR);
1451 unreferencedElements.add(element);
1454 final Group content =
parseBegin(args, layout, lnr, unreferencedElements,
true);
1455 if (!unreferencedElements.isEmpty()) {
1456 throw new IOException(
"layout doesn't define elements "+unreferencedElements);
1458 layout.setHorizontalGroup(content);
1462 if (background ==
null) {
1463 throw new AssertionError(
"element 'dialog_background' is missing");
1465 final Group content =
parseBegin(args, layout, lnr, unreferencedElements,
true);
1469 final Group group2 = layout.createSequentialGroup();
1471 if (title ==
null) {
1473 if (close ==
null) {
1475 group2.addGroup(content);
1478 final Group group3 = layout.createSequentialGroup();
1479 group3.addGap(0, 10, Short.MAX_VALUE);
1480 group3.addComponent(close);
1481 final Group group4 = layout.createParallelGroup();
1482 group4.addGroup(group3);
1483 group4.addGroup(content);
1484 group2.addGroup(group4);
1487 if (close ==
null) {
1489 final Group group3 = layout.createSequentialGroup();
1490 group3.addGap(0, 10, Short.MAX_VALUE);
1491 group3.addComponent(help);
1492 final Group group4 = layout.createParallelGroup();
1493 group4.addGroup(group3);
1494 group4.addGroup(content);
1495 group2.addGroup(group4);
1498 final Group group3 = layout.createSequentialGroup();
1499 group3.addGap(0, 10, Short.MAX_VALUE);
1500 group3.addComponent(help);
1501 group3.addComponent(close);
1502 final Group group4 = layout.createParallelGroup();
1503 group4.addGroup(group3);
1504 group4.addGroup(content);
1505 group2.addGroup(group4);
1510 if (close ==
null) {
1512 final Group group3 = layout.createParallelGroup();
1513 group3.addComponent(title);
1514 group3.addGroup(content);
1515 group2.addGroup(group3);
1518 final Group group3 = layout.createSequentialGroup();
1519 group3.addComponent(title);
1520 group3.addComponent(close);
1521 final Group group4 = layout.createParallelGroup();
1522 group4.addGroup(group3);
1523 group4.addGroup(content);
1524 group2.addGroup(group4);
1527 if (close ==
null) {
1529 final Group group3 = layout.createSequentialGroup();
1530 group3.addComponent(title);
1531 group3.addComponent(help);
1532 final Group group4 = layout.createParallelGroup();
1533 group4.addGroup(group3);
1534 group4.addGroup(content);
1535 group2.addGroup(group4);
1538 final Group group3 = layout.createSequentialGroup();
1539 group3.addComponent(title);
1540 group3.addComponent(help);
1541 group3.addComponent(close);
1542 final Group group4 = layout.createParallelGroup();
1543 group4.addGroup(group3);
1544 group4.addGroup(content);
1545 group2.addGroup(group4);
1550 if (!unreferencedElements.isEmpty()) {
1551 throw new IOException(
"layout doesn't define elements "+unreferencedElements);
1554 final Group group1 = layout.createParallelGroup();
1555 group1.addGroup(group2);
1556 group1.addComponent(background);
1558 layout.setHorizontalGroup(group1);
1570 if (component ==
null) {
1574 return unreferencedElements.remove(component) ? component :
null;
1592 final String type = args.get();
1593 final String name = args.get();
1599 throw new IOException(
"cannot use 'item floor' without 'def item' command");
1603 element =
new GUIItemFloor(tooltipManager, elementListener, commandQueue, name, itemPainter, index, server,
floorView,
model.
getItemSet(),
facesManager, nextGroupFace, prevGroupFace, 0,
guiFactory);
1610 throw new IOException(
"cannot use 'item inventory' without 'def item' command");
1614 element =
new GUIItemInventory(tooltipManager, elementListener, commandQueue, name, itemPainter, index, server,
facesManager,
floorView,
inventoryView, 0,
guiFactory);
1625 element =
new GUIItemShortcut(tooltipManager, elementListener, name, activateColor, activateImage, immediateColor, immediateImage, shortcutSlot,
facesManager, commandExecutor, shortcuts, font,
guiFactory);
1631 throw new IOException(
"cannot use 'item spelllist' without 'def item' command");
1635 element =
new GUIItemSpell(tooltipManager, elementListener, commandQueue, name, itemPainter, index,
facesManager,
model.
getSpellsManager(),
spellView, 0,
guiFactory);
1639 throw new IOException(
"undefined item type: "+type);
1651 private void parseKey(@NotNull
final Args args, @Nullable
final Gui gui, @NotNull
final String line)
throws IOException {
1656 throw new IOException(
"invalid key binding: "+ex.getMessage(), ex);
1660 while (args.hasMore()) {
1675 final String name = args.get();
1692 final String name = args.get();
1709 final String name = args.get();
1726 final String name = args.get();
1742 final String name = args.get();
1758 final String name = args.get();
1762 insertGuiElement(
new GUIOneLineLabel(tooltipManager, elementListener, name,
null, font, color, color,
null,
Alignment.
LEFT, text,
guiFactory));
1774 final String name = args.get();
1781 insertGuiElement(
new GUILabelStats(tooltipManager, elementListener, name, textFont, textColor, textHighlightColor, backgroundColor, stat, alignment,
model.
getStats(),
guiFactory));
1793 final String name = args.get();
1805 insertGuiElement(
new GUILabelStats2(tooltipManager, elementListener, name, font, colorNormal, colorUpgradable, colorDepleted, colorBoosted, colorBoostedUpgradable,
null, statCurrent, statBase, statRace, statApplied, alignment,
model.
getStats(),
guiFactory));
1818 final String name = args.get();
1825 insertGuiElement(
new GUINewcharLabel(tooltipManager, elementListener, name, server, font, fontError, stat, type,
newCharModel, color, colorError,
guiFactory));
1837 final String name = args.get();
1851 final String type = args.get();
1852 final List<Component> elements =
new ArrayList<>();
1853 while (args.hasMore()) {
1856 if (elements.size() < 2) {
1857 throw new IOException(
"'link_size' needs at least two gui elements");
1859 final Component[] array = elements.toArray(
new Component[elements.size()]);
1860 final GroupLayout layout = (GroupLayout)gui.getLayout();
1863 layout.linkSize(SwingConstants.HORIZONTAL, array);
1867 layout.linkSize(SwingConstants.VERTICAL, array);
1871 layout.linkSize(SwingConstants.HORIZONTAL, array);
1872 layout.linkSize(SwingConstants.VERTICAL, array);
1876 throw new IOException(
"undefined type '"+type+
"'");
1889 final String name = args.get();
1890 final Image backgroundImage = args.get().equals(
"null") ? null :
imageParser.
getImage(args.getPrev());
1897 final Fonts fonts =
new Fonts(fontPrint, fontFixed, fontFixedBold, fontArcane);
1911 final String name = args.get();
1912 final Image backgroundImage = args.get().equals(
"null") ? null :
imageParser.
getImage(args.getPrev());
1919 final Fonts fonts =
new Fonts(fontPrint, fontFixed, fontFixedBold, fontArcane);
1930 final String name = args.get();
1935 throw new IOException(
"element '"+name+
"' is not of type 'log'");
1938 throw new IOException(
"invalid color index "+index);
1950 final String name = args.get();
1951 final String type = args.get();
1952 final boolean add =
switch (type) {
1953 case "only" ->
true;
1954 case "not" ->
false;
1955 default ->
throw new IOException(
"type '"+type+
"' is invalid");
1962 throw new IOException(ex);
1964 }
while (args.hasMore());
1967 throw new IOException(
"element '"+name+
"' is not of type 'log'");
1981 final String name = args.get();
1985 if (facesProvider ==
null) {
1986 throw new IOException(
"cannot create faces with size 4");
2002 final String name = args.get();
2004 if (facesProvider ==
null) {
2005 throw new IOException(
"cannot create faces with size "+
defaultTileSize);
2020 final String name = args.get();
2022 if (facesProvider ==
null) {
2023 throw new IOException(
"cannot create faces with size "+
defaultTileSize);
2038 final String name = args.get();
2041 final Image image = args.get().equals(
"null") ? null :
imageParser.
getImage(args.getPrev());
2046 final String format = args.get();
2047 final String tooltip = args.get();
2048 insertGuiElement(
new GUIMetaElementList(tooltipManager, elementListener, name, cellWidth, cellHeight, metaserverModel, image, font, format, tooltip, text, label, connectCommandList,
guiFactory));
2063 final String name = args.get();
2066 if (alpha < 0 || alpha > 1.0F) {
2067 throw new IOException(
"invalid alpha value: "+alpha);
2070 final GUIPicture picture =
new GUIPicture(tooltipManager, elementListener, name, image, alpha, image.getWidth(), image.getHeight(),
guiFactory, text);
2072 if (name.equals(
"dialog_help")) {
2073 gui.setHelp(picture);
2087 final String name = args.get();
2089 final BufferedImage imageInactive = args.get().equals(
"null") ? null :
imageParser.
getImage(args.getPrev());
2090 final BufferedImage imageActive = args.get().equals(
"null") ? null :
imageParser.
getImage(args.getPrev());
2091 if (imageInactive ==
null && imageActive ==
null) {
2092 throw new IOException(
"picture is never visible");
2095 final int preferredWidth;
2096 final int preferredHeight;
2097 if (imageInactive ==
null) {
2098 preferredWidth = imageActive.getWidth();
2099 preferredHeight = imageActive.getHeight();
2100 }
else if (imageActive ==
null) {
2101 preferredWidth = imageInactive.getWidth();
2102 preferredHeight = imageInactive.getHeight();
2104 preferredWidth = imageInactive.getWidth();
2105 preferredHeight = imageInactive.getHeight();
2106 if (preferredWidth != imageActive.getWidth() || preferredHeight != imageActive.getHeight()) {
2107 throw new IOException(
"pictures do not have the same size");
2110 insertGuiElement(
new GUIPictureStat(tooltipManager, elementListener, name, stat, imageInactive, imageActive, preferredWidth, preferredHeight,
model.
getStats(),
guiFactory, text));
2124 final String name = args.get();
2133 insertGuiElement(
new GUIQueryText(server, commandHistory, commandCallback, tooltipManager, elementListener, name,
newCharModel, activeImage, inactiveImage, font, inactiveColor, activeColor, margin,
"",
guiFactory));
2146 throw new IOException(
"argument to set_forced_active must be an activatable gui element");
2162 gui.setAutoSize(
new Extent(x, y, w, h));
2196 throw new JXCSkinException(
"can't use hide_input on a non text field "+args.getPrev());
2210 throw new IOException(
"focus must not be used more than once");
2213 final String begin = args.get();
2214 if (!begin.equals(
"begin")) {
2215 throw new IOException(
"syntax error: expecting 'begin'");
2220 final String line = lnr.readLine();
2222 throw new IOException(
"unterminated 'begin' block");
2225 if (line.startsWith(
"#") || line.isEmpty()) {
2229 final String[] argsTmp;
2235 final Args args2 =
new Args(argsTmp);
2236 final String cmd = args2.
get();
2237 if (cmd.equals(
"end")) {
2239 throw new IOException(
"excess arguments");
2248 throw new JXCSkinException(
"can't use hide_input on a non text field "+cmd);
2252 throw new IOException(
"excess arguments");
2256 throw new IOException(
"not an activatable element: "+element);
2259 throw new IOException(
"duplicate element: "+element);
2263 if (tmp.isEmpty()) {
2264 throw new IOException(
"focus block must not be empty");
2281 gui.setUserResizable(
true);
2293 final String name = args.get();
2299 throw new IOException(
"'"+element+
"' is not a scrollable element");
2310 final String newSkinName = args.get();
2313 if (!newSkinName.matches(
"[-a-z_0-9]+")) {
2314 throw new IOException(
"invalid skin_name: "+newSkinName);
2317 throw new IOException(
"minimum resolution ("+minResolution+
") must not exceed maximum resolution ("+maxResolution+
")");
2333 final String name = args.get();
2343 insertGuiElement(
new GUITextField(commandCallback, commandHistory, tooltipManager, elementListener, name,
newCharModel, activeImage, inactiveImage, font, inactiveColor, activeColor, margin,
"", commandList,
guiFactory));
2357 throw new IOException(
"missing 'def textbutton' command");
2360 final String name = args.get();
2379 throw new IOException(
"missing 'def dialog' command");
2382 final String name = args.get();
2395 final GUITextGauge element =
new GUITextGauge(tooltipManager, elementListener, name, positiveImage, positiveLowImage, negativeImage, emptyImage, orientation, tooltipFormat.isEmpty() ?
null : tooltipFormat, color, font, alpha, commandList,
guiFactory,
model.
getStats());
2424 private void parseVertical(@NotNull
final Args args, @NotNull
final Container gui, @NotNull
final LineNumberReader lnr,
final boolean isDialog)
throws IOException,
JXCSkinException {
2425 final GroupLayout layout = (GroupLayout)gui.getLayout();
2426 final String begin = args.get();
2427 if (!begin.equals(
"begin")) {
2428 throw new IOException(
"syntax error: expecting 'begin'");
2430 final Collection<GUIElement> unreferencedElements =
new TreeSet<>(
ELEMENT_COMPARATOR);
2432 unreferencedElements.add(element);
2435 final Group content =
parseBegin(args, layout, lnr, unreferencedElements,
true);
2436 if (!unreferencedElements.isEmpty()) {
2437 throw new IOException(
"layout doesn't define elements "+unreferencedElements);
2439 layout.setVerticalGroup(content);
2443 if (background ==
null) {
2444 throw new AssertionError(
"element 'dialog_background' is missing");
2446 final Group content =
parseBegin(args, layout, lnr, unreferencedElements,
true);
2450 final Group group2 = layout.createSequentialGroup();
2452 if (title ==
null) {
2454 if (close ==
null) {
2456 group2.addGroup(content);
2459 final Group group3 = layout.createParallelGroup();
2460 group3.addComponent(close);
2461 group3.addGroup(content);
2462 group2.addGroup(group3);
2465 if (close ==
null) {
2467 final Group group3 = layout.createParallelGroup();
2468 group3.addComponent(help);
2469 group3.addGroup(content);
2470 group2.addGroup(group3);
2473 final Group group3 = layout.createParallelGroup();
2474 group3.addComponent(help);
2475 group3.addComponent(close);
2476 group3.addGroup(content);
2477 group2.addGroup(group3);
2483 if (close ==
null) {
2485 group2.addComponent(title);
2486 group2.addGroup(content);
2489 final Group group3 = layout.createParallelGroup();
2490 group3.addComponent(title);
2491 group3.addComponent(close);
2492 group2.addGroup(group3);
2493 group2.addGroup(content);
2497 if (close ==
null) {
2499 final Group group3 = layout.createParallelGroup();
2500 group3.addComponent(title);
2501 group3.addComponent(help);
2502 group2.addGroup(group3);
2503 group2.addGroup(content);
2506 final Group group3 = layout.createParallelGroup();
2507 group3.addComponent(title);
2508 group3.addComponent(help);
2509 group3.addComponent(close);
2510 group2.addGroup(group3);
2511 group2.addGroup(content);
2516 if (!unreferencedElements.isEmpty()) {
2517 throw new IOException(
"layout doesn't define elements "+unreferencedElements);
2520 final Group group1 = layout.createParallelGroup();
2521 group1.addGroup(group2);
2522 group1.addComponent(background);
2524 layout.setVerticalGroup(group1);
2536 if (resolution ==
null) {
2537 throw new IOException(
"invalid resolution: "+text);
2552 final String name = args.get();
2585 private void addCommand(@NotNull
final String listName, @NotNull
final Args args, @Nullable
final AbstractGUIElement element, @NotNull
final String command, @NotNull
final GuiStateManager guiStateManager, @NotNull
final CommandExecutor commandExecutor, @NotNull
final LineNumberReader lnr, @NotNull
final CommandQueue commandQueue, @NotNull
final CrossfireServerConnection crossfireServerConnection, @NotNull
final CommandCallback commandCallback, @NotNull
final Macros macros)
throws IOException,
JXCSkinException {
2587 commandList.
add(
commandParser.
parseCommandArgs(args, element, command, guiStateManager, commandExecutor, lnr, commandQueue, crossfireServerConnection, commandCallback, macros));
2615 private Group
parseBegin(@NotNull
final Args beginArgs, @NotNull
final GroupLayout layout, @NotNull
final LineNumberReader lnr, @NotNull
final Collection<GUIElement> unreferencedElements,
final boolean topLevel)
throws IOException,
JXCSkinException {
2616 final String type = beginArgs.get();
2617 final Group group =
switch (type) {
2618 case "seq" -> layout.createSequentialGroup();
2619 case "par" -> layout.createParallelGroup();
2620 default ->
throw new IOException(
"undefined type '"+type+
"'");
2625 final String line = lnr.readLine();
2627 throw new IOException(
"unterminated 'begin' block");
2630 if (line.startsWith(
"#") || line.isEmpty()) {
2634 final String[] argsTmp;
2640 final Args args =
new Args(argsTmp);
2641 final String cmd = args.
get();
2642 if (cmd.equals(
"end")) {
2644 throw new IOException(
"excess arguments");
2651 group.addGroup(
parseBegin(args, layout, lnr, unreferencedElements,
false));
2655 if (!(group instanceof SequentialGroup)) {
2656 throw new IOException(
"'border_gap' cannot be used outside 'seq' groups");
2658 ((SequentialGroup)group).addContainerGap();
2667 group.addGap(tmp, size, max);
2672 group.addGap(0, 1, Short.MAX_VALUE);
2678 if (!unreferencedElements.remove(element)) {
2679 throw new IOException(
"layout defines element '"+element+
"' more than once");
2695 group.addComponent(element, min, pref, max);
2697 group.addComponent(element);
2702 throw new IOException(
"excess arguments");
2707 throw new IOException(
"begin...end block must have at least one element");
2711 throw new IOException(
"begin...end block must have at least two elements");
2723 private BufferedImage
scaleImage(@NotNull
final BufferedImage sourceImage) {
2724 final int height = (int)(sourceImage.getHeight()*
fontScaleFactor+0.5F);
2725 final Image scaled = sourceImage.getScaledInstance(-1, height, Image.SCALE_SMOOTH);
2726 final BufferedImage bufferedScaled =
new BufferedImage(scaled.getWidth(
null), scaled.getHeight(
null), BufferedImage.TYPE_INT_ARGB);
2727 bufferedScaled.getGraphics().drawImage(scaled, 0, 0,
null);
2728 return bufferedScaled;
2741 }
catch (
final IllegalArgumentException ex) {