From b4c56163e54189910db27064e716489cc47b1492 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Mon, 30 May 2022 20:39:39 +0200 Subject: [PATCH] Add right-click actions to componentTree --- core/resources/l10n/messages.properties | 2 + core/resources/pix/icons/edit-copy.png | Bin 730 -> 1895 bytes core/resources/pix/icons/edit-duplicate.png | Bin 0 -> 2195 bytes core/resources/pix/icons/edit-edit.png | Bin 0 -> 4438 bytes .../gui/figure3d/photo/PhotoFrame.java | 2 +- .../sf/openrocket/gui/main/BasicFrame.java | 34 +++++- .../sf/openrocket/gui/main/RocketActions.java | 107 ++++++++++++++++-- .../src/net/sf/openrocket/gui/util/Icons.java | 2 + 8 files changed, 133 insertions(+), 14 deletions(-) create mode 100644 core/resources/pix/icons/edit-duplicate.png create mode 100644 core/resources/pix/icons/edit-edit.png diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties index 521279b03..cd4eb8f97 100644 --- a/core/resources/l10n/messages.properties +++ b/core/resources/l10n/messages.properties @@ -33,6 +33,8 @@ RocketActions.CopyAct.Copy = Copy RocketActions.CopyAct.ttip.Copy = Copy this component (and subcomponents) to the clipboard. RocketActions.PasteAct.Paste = Paste RocketActions.PasteAct.ttip.Paste = Paste the component or simulation on the clipboard to the design. +RocketActions.DuplicateAct.Duplicate = Duplicate +RocketActions.DuplicateAct.ttip.Duplicate = Duplicate this component (and subcomponents). RocketActions.EditAct.Edit = Edit RocketActions.EditAct.ttip.Edit = Edit the selected component. RocketActions.NewStageAct.Newstage = New stage diff --git a/core/resources/pix/icons/edit-copy.png b/core/resources/pix/icons/edit-copy.png index b7c938a999b0e8490cf7d69ed9a6fd42ad580731..28ac512bdab4c217db61f816485cb4224b6b5c32 100644 GIT binary patch literal 1895 zcmbVN3v3f*9KXF}56}UdcEi~7mcd}O_h@VBK?mJh=%yXBG}+`axm~aA(f00kcUvD5 z*rsHR23Mzv*6J&n3brlW1xa97>yZe2= z|Nr-YU28@8ym9Hd=@0~sE45pl;GU_RsZW8cdSHkGw=}{2ngl@^6O}Uo+PGN-LCFKG zt4gkNyh74^P)oV_8b%uq3IGj3vu1|{iuN-yT*G)-&W!xn*@eKY+l*8i9GF8WVSKDT zDl!YAdPRbi)urd-YvVzR=9IP->HGG|H zMu4v)B7#DkmXl(E0wF`glz`$|Oc6B^{ z|AspL`A-Hwa2<}gjmP>D48~1JvaKG-hy~=aXvq~37}Uu~e4R)$wtC=Zo}xw|OGJi} zdC|r5fzeb|jJgaHIxPVgI4GLs6cJM&*}_;UnK2_EZ-fTJH9Fjdn@EG6)DwjmPGZ;y z)WN%1PvlXkL8HSIsNO{4`a+EO0}9Hy+G%?sxys{qsc@bQcwPVjiXEl%gQ*tOU{(iS@(+rZ%<;u<&plkQA-d zw;7?8#4>Iq_L_a-2#nB%d<-D{hjfp?B;F&3D3P&vLA3ruL7~8XRO!VLCFoy84u5;3 zZ%5%^7%0p590CvVNyczse2HLA{hW5}9t0)kmRc>Y8Eb>@zwFPlWUcDjedpGOThq} zAT8~O_kLM+VN7P_B;A2a+VZnkk4D@3OST=p-*O=Z@k9GB-o4dh+%kP%?WfgKQUhFz zHsfM{cXv}D{QAo5#@~{L2Gw78ww(QbQ*sl$w4Jsr)PBDBCVo5R6G%J9)uf)et~aB) zyD)X>p7ptN7Dw4Pvx(ECM-vk}7Oe2D@jaY;dGEG+&TBsuCp;@8W;J|qCK7t@o3=D@ zL0a3fH+;|EpEh{-_LcOqHs{*<@x50g zO|4@Kd-iPn_2Pm0yzSZ{qZ6{iKKwa<~S4F<=4z zVFV)s12Ypd(~(n`&I1Gx3j;{)k6#RIe0)x2@t#tiMluYp!5HKmQ@@KR{(%91K9a|NjLDAeR3i=d-dg{`>j+$M3&?feQbl z>ShFCMuy)&;lCgZG6;x&gA4|$1qdJ(9&XM$5nf(PW=1BN17~h9ym;}P;TO~;VB3If z2NM+rBMn)GUw^+KhWvvX_Tk%q27mx!i3@P7s!Vcc{{yuB|C$p~?=gLR1VV)$P~kcZ*^^S2B? zfB#`%VPS`B24NHz|X1w}JZ@E24Ohz(Nl0~mT=e*Fjgn*r*77zPOe1P}|d%^+L>t7FHH!W{?67E}#uSL1vIGKu0k_qmh-9je(hsje&!kje!vu z28@3o26J!%*+9Jj0mQQ7=*5Rgf0)!jSs0i(AUjTDfaP!|hIb!7GdzF&k>Sz%KR{cV o!Q%gc;l>C|`)5v`y9W?p03+98#-LdpAOHXW07*qoM6N<$f@qU0RR910 diff --git a/core/resources/pix/icons/edit-duplicate.png b/core/resources/pix/icons/edit-duplicate.png new file mode 100644 index 0000000000000000000000000000000000000000..c0211635793661ad6d11cde079a65465221dc27e GIT binary patch literal 2195 zcmbVM3s4kg9A8ls-*7LvXHq(3!2m&=TdMDwsH~~WpMJa+JagxGGBSw)dWn@Vs8ai}P&o8*xEZd7AHTY!Jc~n(q zaXb(R=mWG~^1E@8VHljCaEihp0#l~@R4$15l!O)r8&G(^D6696Lp4UuDHW(z9rW59 zgjWvJ`jk+jV9M|yC*!1^(1L0L3VawR7x?p=#04G)`M?W&ssgcO7%O`uRZ=|CBdEil z{~-XAYqy6r9_q#G4NE9$Y9S0Gl#qv_6~}ZL;8{SC3j91sErd1`v}j~D#Sb`D@;f9c zzok^;S}da!t*6i-c8(W)S`dk?wg4Na0;>+zjlu{Lqe%zJuto!GpppoZC5UFIT@pms z^wv-#MiUN-U@3;B%}E6H01Dei;8gBE!2-{^B)^w~A&Xwl4RG1#)}i4Gd#Fv?8Qn1KdPj59e6n1$htW&xZw zriE7dBHGjyEXQlzZq@NxnSr1Sy%rymk0(+N?GJbWBz;Wro7EM`r3N@ZNOr^AJwbcn zkFu|Dg^xG?WW{WD)!O_++wuRI52c3pa6UJH0~FV3;o#b!X)X-@@0vAzYc-Tw;BeAu z%kYqe58*Kle304?hj8Rdxd=fb#B^J-!?MJ?+@9umsn5K1uhlQ3&mMWl@afK|$k|mR zd#op-6OWsUPM{+rx2C4H(?=x9bsNx$3vD*X2~RfHx$Pn~yR7W|r46e+T;$bS3w?d9 zGU2=DmroebufFom#XpB#Vy;g(+GpG5$>YzgU0OY>`>MWEp93ElZXTcG?!2n(i)Ajq zzv6tGTg8(BcsJ) zV#5Mcp#Py$Oj&8h`trQW#ceuoYTt2)x0Wd@xcf(4?4CRKFMgYlvM{@?wem(!Fmuuv zYS;U=zEPVtY%G6w&%9R~MrBQ`$5Z0cK$pYiSF3l{FI|88-uyKq60=SmJeDyR8;TT1 z&&<4k`J3M&yj{=iYm6agn!lFU)zmDG7`XVe@|hbmR=md|jysJftz<0!)wOo~)i`AG z>&KN}UGWzN_Zx_p=6RzGKlV&<5w&^P|`jq|5bhd%JS-C z`BQEtUM|1N?m-rm7J)-&quZgq&gu6~>we*O)&BVXXBFe7y#e3slB036hp+m%uDUR8 z$S1p#pu2Pgml literal 0 HcmV?d00001 diff --git a/core/resources/pix/icons/edit-edit.png b/core/resources/pix/icons/edit-edit.png new file mode 100644 index 0000000000000000000000000000000000000000..44310b471b9af757e11ecde985a6711d87066ee7 GIT binary patch literal 4438 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 l!p2`n 0) { - + } else if (sims != null && sims.length > 0) { Simulation[] simsCopy = new Simulation[sims.length]; for (int i=0; i < sims.length; i++) { simsCopy[i] = sims[i].copy(); } + OpenRocketClipboard.setClipboard(simsCopy); parentFrame.selectTab(BasicFrame.SIMULATION_TAB); } @@ -765,9 +783,81 @@ public class RocketActions { (OpenRocketClipboard.getClipboardSimulations() != null)); } } - - - + + + /** + * Action that duplicates the selected component. + */ + private class DuplicateAction extends RocketAction { + private static final long serialVersionUID = 1L; + + public DuplicateAction() { + //// Copy + this.putValue(NAME, trans.get("RocketActions.DuplicateAct.Duplicate")); + this.putValue(MNEMONIC_KEY, KeyEvent.VK_D); + this.putValue(ACCELERATOR_KEY, DUPLICATE_KEY_STROKE); + //// Copy this component (and subcomponents) to the clipboard. + this.putValue(SHORT_DESCRIPTION, trans.get("RocketActions.DuplicateAct.ttip.Duplicate")); + this.putValue(SMALL_ICON, Icons.EDIT_DUPLICATE); + clipboardChanged(); + } + + @Override + public void actionPerformed(ActionEvent e) { + List components = selectionModel.getSelectedComponents(); + if (components != null) { + components = new ArrayList<>(components); + fillInMissingSelections(components); + + components.sort(Comparator.comparing(c -> c.getParent() != null ? -c.getParent().getChildPosition(c) : 0)); + } + Simulation[] sims = selectionModel.getSelectedSimulations(); + + if (isCopyable(components)) { + List duplicateComponents = new LinkedList<>(copyComponentsMaintainParent(components)); + + List> positions = new LinkedList<>(); + for (RocketComponent component : duplicateComponents) { + positions.add(getPastePosition(component)); + } + + if (duplicateComponents.size() == 1) { + document.addUndoPosition("Duplicate " + duplicateComponents.get(0).getComponentName()); + } else { + document.addUndoPosition("Duplicate components"); + } + + for (int i = 0; i < duplicateComponents.size(); i++) { + positions.get(i).getU().addChild(duplicateComponents.get(i), positions.get(i).getV()); + } + + selectionModel.setSelectedComponents(duplicateComponents); + } else if (sims != null && sims.length > 0) { + ArrayList copySims = new ArrayList(); + + for (Simulation s: sims) { + Simulation copy = s.duplicateSimulation(rocket); + String name = copy.getName(); + if (name.matches(OpenRocketDocument.SIMULATION_NAME_PREFIX + "[0-9]+ *")) { + copy.setName(document.getNextSimulationName()); + } + document.addSimulation(copy); + copySims.add(copy); + } + // TODO: undo + selectionModel.setSelectedSimulations(copySims.toArray(new Simulation[0])); + + parentFrame.selectTab(BasicFrame.SIMULATION_TAB); + } + } + + @Override + public void clipboardChanged() { + this.setEnabled(isCopyable(selectionModel.getSelectedComponent()) || + isSimulationSelected()); + } + + } @@ -782,6 +872,7 @@ public class RocketActions { this.putValue(NAME, trans.get("RocketActions.EditAct.Edit")); //// Edit the selected component. this.putValue(SHORT_DESCRIPTION, trans.get("RocketActions.EditAct.ttip.Edit")); + this.putValue(SMALL_ICON, Icons.EDIT_EDIT); clipboardChanged(); } diff --git a/swing/src/net/sf/openrocket/gui/util/Icons.java b/swing/src/net/sf/openrocket/gui/util/Icons.java index be095f1c1..ebbfe4345 100644 --- a/swing/src/net/sf/openrocket/gui/util/Icons.java +++ b/swing/src/net/sf/openrocket/gui/util/Icons.java @@ -58,9 +58,11 @@ public class Icons { public static final Icon FILE_QUIT = loadImageIcon("pix/icons/application-exit.png", "Quit OpenRocket"); public static final Icon EDIT_UNDO = loadImageIcon("pix/icons/edit-undo.png", trans.get("Icons.Undo")); public static final Icon EDIT_REDO = loadImageIcon("pix/icons/edit-redo.png", trans.get("Icons.Redo")); + public static final Icon EDIT_EDIT = loadImageIcon("pix/icons/edit-edit.png", "Edit"); public static final Icon EDIT_CUT = loadImageIcon("pix/icons/edit-cut.png", "Cut"); public static final Icon EDIT_COPY = loadImageIcon("pix/icons/edit-copy.png", "Copy"); public static final Icon EDIT_PASTE = loadImageIcon("pix/icons/edit-paste.png", "Paste"); + public static final Icon EDIT_DUPLICATE = loadImageIcon("pix/icons/edit-duplicate.png", "Duplicate"); public static final Icon EDIT_DELETE = loadImageIcon("pix/icons/edit-delete.png", "Delete"); public static final Icon EDIT_SCALE = loadImageIcon("pix/icons/edit-scale.png", "Scale");