Compare commits

...

207 Commits

Author SHA1 Message Date
DarkPhoenix
44a7e53b9e Bump version 2019-05-02 19:54:29 +03:00
DarkPhoenix
b35bdd4e33 Add triglavian invasion effects 2019-05-02 19:50:40 +03:00
DarkPhoenix
7f52f6fe44 Show spoolup time for MJFGs 2019-05-02 19:13:21 +03:00
DarkPhoenix
34e49da0c1 Add renders of new ships 2019-05-02 18:52:53 +03:00
DarkPhoenix
5132698974 Add draugur effect 2019-05-02 18:24:47 +03:00
DarkPhoenix
832cebcaaf Add ikitursa effects 2019-05-02 18:17:23 +03:00
DarkPhoenix
4eaccd1eed Change spoolup context menu to show 20-ish amount of items 2019-05-02 18:03:31 +03:00
DarkPhoenix
5245f289a5 Add nergal effects 2019-05-02 17:16:38 +03:00
DarkPhoenix
672aed44f2 Add trig frigate resist effects 2019-05-02 16:40:38 +03:00
DarkPhoenix
8c890cf9a5 Rerun effectUsedBy script 2019-05-02 16:30:28 +03:00
DarkPhoenix
8f9a95db93 Do not crash when icons are missing, and add ADC mutaplasmid override 2019-05-02 16:10:22 +03:00
DarkPhoenix
5a056e6d47 Update database to 1498791 2019-05-02 15:54:04 +03:00
DarkPhoenix
b121085271 Unfuck EFT import 2019-05-02 04:34:49 +03:00
DarkPhoenix
b3ef55cb7f Make modification of mutated stats undoable 2019-05-02 00:18:17 +03:00
DarkPhoenix
bbc8fd0f97 Change mutations UI to properly keep all needed elements visible and scroll when needed 2019-05-01 22:22:18 +03:00
DarkPhoenix
b11a576922 Fix an oversight which led to charges not unloading when replacing module 2019-05-01 17:59:59 +03:00
DarkPhoenix
7375258b9f Change citadel security status via fitting pane context menu 2019-05-01 16:44:08 +03:00
DarkPhoenix
c447cf06d7 Add context menu to change system security status 2019-05-01 16:22:26 +03:00
DarkPhoenix
a7dcf1ace6 Add commands which handle switching fit security status 2019-05-01 15:33:27 +03:00
DarkPhoenix
bd3d81e2f8 Plug fit security status in citadel rig effect 2019-05-01 15:22:01 +03:00
DarkPhoenix
732b7a5ab9 Add system security column to fits 2019-05-01 15:04:46 +03:00
DarkPhoenix
3c16600c53 Include sqlalchemy's baked in mac/windows specs as well 2019-05-01 14:54:49 +03:00
DarkPhoenix
d0921ba9ec Add extra import to include in distributives 2019-05-01 13:08:50 +03:00
DarkPhoenix
8ec840740b Change fix for #1909 2019-05-01 13:04:12 +03:00
DarkPhoenix
580ff1c435 Readability fixes 2019-05-01 01:28:41 +03:00
DarkPhoenix
d68296bfd7 Bump pyyaml version 2019-05-01 01:28:09 +03:00
DarkPhoenix
c520b5e4f5 Bump version 2019-05-01 01:08:47 +03:00
DarkPhoenix
2da85888be Fix scrolling speed in ship browser 2019-05-01 01:05:31 +03:00
DarkPhoenix
bdd4a8cfb7 Fix scroll rate in preference panel 2019-05-01 00:55:52 +03:00
DarkPhoenix
dbef3b8c84 Unfuck character editor context menus 2019-04-30 22:39:38 +03:00
DarkPhoenix
63a6d746e8 Search for DNA in chat line 2019-04-30 21:18:12 +03:00
DarkPhoenix
7b8d9f8dbe Round fitting resource attributes properly 2019-04-30 18:51:01 +03:00
DarkPhoenix
7aa73e4b2f Fix issue with putting ID instead of name into capping key 2019-04-30 18:48:53 +03:00
DarkPhoenix
33883c562a Collapse all children when collapsing market group 2019-04-30 17:37:16 +03:00
DarkPhoenix
36c17dde8d Show damage mod cumulative damage boost in misc column 2019-04-30 17:22:06 +03:00
DarkPhoenix
5a9fd188f0 Add ability to clone drones by ctrl-dragging them 2019-04-30 16:16:09 +03:00
DarkPhoenix
4b65662c9f Fix triage effect local tank stacking penalty 2019-04-30 15:37:27 +03:00
DarkPhoenix
cd3d1a9557 Add SBA jargon entry 2019-04-30 15:25:20 +03:00
DarkPhoenix
9f63b0b412 Import DNA-formatted fits from chat messages 2019-04-30 15:18:40 +03:00
DarkPhoenix
9249ef24b3 Allow to undo operations even if removed module doesn't fit (e.g. removing excessive modules on t3c with removed subs) 2019-04-30 14:39:11 +03:00
DarkPhoenix
63a599ca85 Restore removed dummies on all commands which can deal with t3c subsystems 2019-04-30 13:50:10 +03:00
DarkPhoenix
fa2bceaff9 Allow recalculating fit without changing slot layout (e.g. when t3c subs added/removed) 2019-04-30 13:21:39 +03:00
DarkPhoenix
e9dffeadf6 Undo removal of subsystems properly 2019-04-30 02:03:32 +03:00
DarkPhoenix
161c4629cf Do not paint subsystems with red when restrictions are disabled 2019-04-30 01:31:35 +03:00
DarkPhoenix
951ffcd35a Fix an oversight 2019-04-30 01:25:13 +03:00
DarkPhoenix
ba21ebe058 Restore mass-removed t3 modules properly 2019-04-30 01:23:52 +03:00
DarkPhoenix
f8c2604fb2 Do not crash when changing slot color option with item view unitialized 2019-04-30 00:47:04 +03:00
DarkPhoenix
966763aaa4 Make toggling ignore of restrictions undoable 2019-04-30 00:44:54 +03:00
DarkPhoenix
4eb8973c31 Rework how effect-blocking modules (scram) work to properly support undoing 2019-04-29 20:25:28 +03:00
DarkPhoenix
72fe52e560 Do not use GetItemData where we do not need it 2019-04-29 15:37:54 +03:00
DarkPhoenix
e346239174 Limit amount of commands by 100 2019-04-29 09:48:37 +03:00
DarkPhoenix
dd27a26fa9 Allow batch changes of projected fighter/module states 2019-04-29 09:47:38 +03:00
DarkPhoenix
162b115c91 Allow alt actions when removing projected items via double-click 2019-04-29 09:28:21 +03:00
DarkPhoenix
99f4ed6b33 Allow batch change of projected fighter variations 2019-04-29 09:17:46 +03:00
DarkPhoenix
53252241e1 Allow batch changes of projected drone variations 2019-04-29 08:46:01 +03:00
DarkPhoenix
698328e335 Allow batch changes of projected module metas 2019-04-29 07:51:13 +03:00
DarkPhoenix
dece788f66 Allow alt-switch of ability states for projected fighters 2019-04-29 01:18:15 +03:00
DarkPhoenix
958fbac582 Implement alt logic in projected item removal via context menu 2019-04-29 01:04:22 +03:00
DarkPhoenix
99d72956e8 Detect structure modules as modules 2019-04-29 00:43:18 +03:00
DarkPhoenix
eb601e991a Print "stack" suffix on remove context menu text depending on passed source context rather than item context 2019-04-29 00:25:02 +03:00
DarkPhoenix
cb7f0052c4 Do not remove items on Del key used with modifier keys 2019-04-29 00:02:29 +03:00
DarkPhoenix
8b75b5f184 Change projected view to support multi-selection 2019-04-29 00:00:07 +03:00
DarkPhoenix
bf5935e033 Alt + changing fighter state now changes state of similar fighters 2019-04-28 18:59:37 +03:00
DarkPhoenix
67e80deed9 Change command which changes projected item state to support multi-selection 2019-04-28 18:43:28 +03:00
DarkPhoenix
e39f9ffecf Rework projected removal command to be able to handle multi-selection 2019-04-27 23:27:16 +03:00
DarkPhoenix
e865c9a399 Use GetModifiers() instead of asking status of individual keys 2019-04-27 14:22:09 +03:00
Anton Vorobyov
a919510d07 Merge pull request #1955 from MaruMaruOO/master
Fixed AoE DD's signature radius for EFS exports.
2019-04-27 10:28:57 +03:00
MaruMaruOO
bd86d3289f Fixed AoE DD's signature radius for EFS exports. 2019-04-27 00:57:41 -04:00
DarkPhoenix
c8ff644b63 Make command fit panel multi-selectable 2019-04-25 23:55:41 +03:00
DarkPhoenix
6703a08976 Change command fit state changing command to support multi-selection 2019-04-25 19:27:36 +03:00
DarkPhoenix
3d70d9e37c Add multi-selection support to command fit remove command 2019-04-25 18:47:39 +03:00
DarkPhoenix
ef62d5cf97 Make boosters panel multi-selectable 2019-04-25 18:32:18 +03:00
DarkPhoenix
caf5f33c80 Change booster state changing command to support multi-selection 2019-04-25 18:13:24 +03:00
DarkPhoenix
29c29469c6 Change booster removal commands to support multi-selection 2019-04-25 17:56:53 +03:00
DarkPhoenix
7b564f1f53 Show "Change Ship Skills" even when clicking outside of module area 2019-04-25 17:38:31 +03:00
DarkPhoenix
f280955ac3 Do not change state of selection when state of item outside of selection is changed 2019-04-25 17:33:17 +03:00
DarkPhoenix
e09fce8411 Allow removal of several implants at once via context menu 2019-04-25 17:15:01 +03:00
DarkPhoenix
5777103d21 Allow batch implant state switching 2019-04-25 16:29:04 +03:00
DarkPhoenix
8671b20790 Rework implant state switch command to accept multiple positions 2019-04-25 15:20:20 +03:00
DarkPhoenix
dc30b3ed1d Change implant remove command to support batch removal 2019-04-25 14:56:09 +03:00
DarkPhoenix
07a9f77287 Allow to change batches of items in cargo 2019-04-25 13:36:54 +03:00
DarkPhoenix
9d58ceb14d Remove multiple cargo items at once via context menu 2019-04-25 09:56:09 +03:00
DarkPhoenix
71c421702c Rework code of remove item context menu a little 2019-04-25 09:50:56 +03:00
DarkPhoenix
989f12453d Remove multiple cargo items with delete key 2019-04-25 09:27:52 +03:00
DarkPhoenix
b7d6892d9f Rework change cargo meta command to support changing multiple items at once 2019-04-24 23:11:28 +03:00
DarkPhoenix
cfb351a751 Change cargo removal command to be able to accept multiple typeIDs 2019-04-24 22:09:45 +03:00
DarkPhoenix
dde1e7990d Allow batch manipulation of fighter abilities 2019-04-24 17:25:20 +03:00
DarkPhoenix
6e4ec54ac6 Allow batch toggling of fighters 2019-04-24 16:49:39 +03:00
DarkPhoenix
81da217a09 Allow batch meta level change on fighters 2019-04-24 16:28:40 +03:00
DarkPhoenix
2d1613d8bc Make sure that clicked item is always included in selection 2019-04-24 16:01:02 +03:00
DarkPhoenix
ccc2e6ece3 Make sure that drone variation is applicable to multiple modules, if multiple drones of the same type are selected 2019-04-24 15:56:19 +03:00
DarkPhoenix
1206e95cfb Rework meta switcher code 2019-04-24 15:37:15 +03:00
DarkPhoenix
921ccd3be9 Allow removal of batches of fighters 2019-04-24 15:17:30 +03:00
DarkPhoenix
178e3a3d56 Change sorting of fighters - group up by sub-roles as well 2019-04-24 13:28:14 +03:00
DarkPhoenix
5d255547e4 Make fighter panel multi-selectable and change fighter sorting to match order in fighter tube panel 2019-04-24 13:16:01 +03:00
DarkPhoenix
bd148b8792 Stylistic fixes 2019-04-24 12:47:50 +03:00
DarkPhoenix
b88ebdcfc0 Send context only for selected module in fitting panel 2019-04-24 12:45:21 +03:00
DarkPhoenix
3a26815d18 Change the fix again to not go into recursion problems on windows 2019-04-24 12:00:48 +03:00
DarkPhoenix
b70fcd9659 Change ScreenToClient fix 2019-04-24 11:46:20 +03:00
DarkPhoenix
71aa557770 Focus contents of various pages when user switches to their tabs 2019-04-24 09:02:28 +03:00
DarkPhoenix
d35bf6514f Do not pass selection as fallback, as we're now calculating clicked row properly 2019-04-24 08:16:23 +03:00
DarkPhoenix
abe015bee3 Add custom implementation of ScreenToClient 2019-04-24 08:11:37 +03:00
DarkPhoenix
929520091f Use wx' builtin when calculating relative coordinates 2019-04-23 23:50:49 +03:00
DarkPhoenix
d4847112a9 Make drone additions pane multi-selectable, change all related commands to support it too 2019-04-23 22:12:07 +03:00
DarkPhoenix
4e2c3a3fcc Do not select cargo/drone tab when adding drones/cargo fails 2019-04-23 19:21:31 +03:00
DarkPhoenix
91e6d89022 Update resource panel when addition panel tabs is selected as side-effect of context menu use 2019-04-23 19:18:33 +03:00
DarkPhoenix
b9a71c08b7 Change only modules which are supposed to change 2019-04-23 18:14:14 +03:00
DarkPhoenix
070dd62e6d Add alt support to variations menu for modules 2019-04-23 18:11:02 +03:00
DarkPhoenix
b404abca41 Add ability to remove similar modules to "remove module" context menu 2019-04-23 17:50:20 +03:00
DarkPhoenix
99f00b25a1 Change few details on how we handle charge switch 2019-04-23 17:39:48 +03:00
DarkPhoenix
45936b5b98 Change implant set context menu to single item as well 2019-04-23 16:34:40 +03:00
DarkPhoenix
b1aac9f56d Do not show menu items which are specific to modules when user clicks rack separator 2019-04-23 16:23:35 +03:00
DarkPhoenix
13f370ceb9 Change several context menus to use single-item class 2019-04-23 16:19:07 +03:00
DarkPhoenix
b5a4f97cb5 Change some context menus to use unconditional class 2019-04-23 16:09:12 +03:00
DarkPhoenix
0679a0af0f Change logic for selection-based context menus 2019-04-23 15:58:42 +03:00
DarkPhoenix
53fe3242b9 Start reworking context menus into several special classes to improve menu handling consistency 2019-04-23 15:38:53 +03:00
DarkPhoenix
6615bed1cd Fix context menu which is used to fill from market 2019-04-23 13:47:47 +03:00
DarkPhoenix
ad0c7a7a9d Rework conditions in some context menus to check mainItem when it's needed by the menu 2019-04-23 10:14:59 +03:00
DarkPhoenix
87ba6a9af0 Drop selection on ESC in fitting pane 2019-04-22 21:10:38 +03:00
DarkPhoenix
5c44df7f21 Unfuck state change for passive modules 2019-04-22 19:36:45 +03:00
DarkPhoenix
24bc675319 Do not crash when right-clicking without any selection 2019-04-22 19:26:24 +03:00
DarkPhoenix
be2e0b5de4 Rely on clicked item instead of selection where it makes sense 2019-04-22 17:33:40 +03:00
DarkPhoenix
e4481e8fb4 Do not rely on modeID, it doesn't always get set of fit by default when creating new fit 2019-04-22 17:04:30 +03:00
DarkPhoenix
19b1eb161b Pass main item argument to context menu definitions 2019-04-22 17:01:53 +03:00
DarkPhoenix
30ed1ac81d Do not crash on context menu when module which can have ammo and t3d mode are selected 2019-04-22 16:15:17 +03:00
DarkPhoenix
b4288e17e5 Send proper main module to context menu if possible 2019-04-22 16:06:16 +03:00
DarkPhoenix
c03d000c45 Pass item which is considered to be clicked item to context menus 2019-04-22 15:45:37 +03:00
DarkPhoenix
881ec8b5b4 Select all items in fitting panel on ctrl-a 2019-04-22 13:36:59 +03:00
DarkPhoenix
71d5b28b75 Reuse selected module getter in fitting panel code 2019-04-22 13:20:10 +03:00
DarkPhoenix
a15fdc3b23 Use shared unselection function when starting drag 2019-04-22 13:07:18 +03:00
DarkPhoenix
55cd33e653 Do not show "show in fitting browser" context menu when it doesn't do anything to avoid confusion 2019-04-22 12:36:10 +03:00
DarkPhoenix
af0b2b9f1b Do not crash when alt-clicking state column 2019-04-22 07:24:14 +03:00
DarkPhoenix
983641d1d5 If loading into specified group of modules fails during alt-drag, attempt to load into all 2019-04-22 00:43:52 +03:00
DarkPhoenix
4ab21e92bf Load charge into all modules when dropping onto illegal location 2019-04-22 00:36:48 +03:00
DarkPhoenix
413f00a475 Select just item being dragged when drag starts 2019-04-22 00:18:46 +03:00
DarkPhoenix
bde2043294 Do not crash when landing module on non-filled module slot while holding alt 2019-04-21 23:47:21 +03:00
DarkPhoenix
d45857f1fc Do not fill with module while adding anything via alt-number from market 2019-04-21 23:42:14 +03:00
DarkPhoenix
8a19bf78ce Fix changing projected fit amount to 0 2019-04-21 21:30:32 +03:00
DarkPhoenix
d523722988 Change projected fit context menu entry names 2019-04-21 21:26:58 +03:00
DarkPhoenix
031cb6fcfb Change projected fits behavior - now addition adds 1 fit in any case (even if fit is already projected), and removal via double-click works like with drones 2019-04-21 21:22:57 +03:00
DarkPhoenix
72fc560241 Make all secondary windows closable by ESC 2019-04-21 19:34:14 +03:00
DarkPhoenix
1d7be66eb1 Destroy stats window when it's closed 2019-04-21 18:30:25 +03:00
DarkPhoenix
5d32a31dc3 Fix implant editor 2019-04-21 18:23:56 +03:00
DarkPhoenix
4821bd1c72 Mass-replace modules when dragging something from market and dropping over another module while holding alt 2019-04-21 15:33:00 +03:00
DarkPhoenix
6694caafa0 Do not unselect on removing modules with delete key 2019-04-21 14:38:01 +03:00
DarkPhoenix
7b71c16cec Remove bunch of modules on alt-double click 2019-04-21 14:35:52 +03:00
DarkPhoenix
df6e7b5772 Remove charge changing functionality from module add command, as it wasn't used anyway 2019-04-21 11:36:21 +03:00
DarkPhoenix
7abc14eb7f Take into consideration effects as well 2019-04-21 11:30:24 +03:00
DarkPhoenix
4d21fa517a Reformat code a little 2019-04-21 11:22:59 +03:00
DarkPhoenix
f2a82c31c4 Add ability to fill fit with item, and fix some charge-related stuff 2019-04-21 10:53:26 +03:00
DarkPhoenix
337973965a Run state change on secondary modules even if clicked module is already in that state 2019-04-21 00:37:11 +03:00
DarkPhoenix
f0b3aafd54 Change state of similar mods when you have alt pressed 2019-04-21 00:32:37 +03:00
DarkPhoenix
f6b97859aa Move code which handles module filtering to command helpers 2019-04-21 00:24:50 +03:00
DarkPhoenix
1064a90a1c Allow to close all tabs with one action 2019-04-21 00:12:12 +03:00
DarkPhoenix
4c736de598 Revert change which allowed to use alt key as modifier to open fits in new tabs or switch modules to offline 2019-04-20 23:47:23 +03:00
DarkPhoenix
8d0ad26159 Change "open stats in the same window" modifier to use shift 2019-04-20 23:40:30 +03:00
DarkPhoenix
3efa07d821 When changing charges, check not just market group, but group too 2019-04-20 23:16:25 +03:00
DarkPhoenix
44240c1d37 Avoid committing more than once when restoring removed modules 2019-04-20 23:10:36 +03:00
DarkPhoenix
9eaeb60af7 Add ability to fill with modules by ctrl+alt+dragging 2019-04-20 22:54:19 +03:00
DarkPhoenix
cecb8f69a3 Change behavior of module state switching 2019-04-20 21:49:13 +03:00
DarkPhoenix
d402735c8b Accurately restore mod states on Undo(), if they were both changed manually and during state check on Do() 2019-04-20 21:22:34 +03:00
DarkPhoenix
327ad78eb8 Fix insert for boosters as well 2019-04-20 19:31:44 +03:00
DarkPhoenix
edc1ef0e38 Do not choke on context menu spawning when module cannot be found 2019-04-20 16:28:12 +03:00
DarkPhoenix
c6bfd0bc05 Do not refetch mouse state 2019-04-20 14:44:24 +03:00
DarkPhoenix
3badab0353 Fix various bugs replated to DB management 2019-04-20 14:29:10 +03:00
DarkPhoenix
8ca5b34c14 Insert instead of appending when we're asked to... 2019-04-20 13:30:07 +03:00
DarkPhoenix
417ffd396c Undo application of sets without committing results until operation is done 2019-04-20 12:59:48 +03:00
DarkPhoenix
ccb0732f7d Update preference tooltip 2019-04-20 04:24:55 +03:00
Anton Vorobyov
a994f55011 Merge pull request #1949 from pyfa-org/command_reliability
Command reliability
2019-04-20 04:20:25 +03:00
DarkPhoenix
c2d309430e Make sure cargo-module drag commands are using actual fit data too 2019-04-20 04:10:32 +03:00
DarkPhoenix
c3e1ec2760 Ensure that module-fit relationship is established before running some checks which can rely on them 2019-04-20 04:06:57 +03:00
DarkPhoenix
8c40489049 Fix wrong log message 2019-04-20 03:59:08 +03:00
DarkPhoenix
051800bc16 Do not break selection on delimiters when generating context menus 2019-04-20 03:49:09 +03:00
DarkPhoenix
d1a3e5c0e8 Fix some module-related command calls 2019-04-20 03:43:15 +03:00
DarkPhoenix
48d795676f Do not pass modules to charge switching commands 2019-04-20 02:52:28 +03:00
DarkPhoenix
3fec9ba173 Do not choke on mass projected fit removal, and restore projected fit amounts 2019-04-20 02:30:57 +03:00
DarkPhoenix
264208b42e Make sure projected drones do not choke on mass removal 2019-04-20 02:12:03 +03:00
DarkPhoenix
62e8da6ff2 Make fighter pane more reliable and change few already checked panels as well 2019-04-20 01:51:55 +03:00
DarkPhoenix
e3f21cf700 Make local drone commands reliable 2019-04-20 01:15:38 +03:00
DarkPhoenix
fbc34224bc Make implant-related command calls more reliable 2019-04-20 00:43:13 +03:00
DarkPhoenix
93cd3b97fa Make command-related commands and their invokations more reliable 2019-04-20 00:35:14 +03:00
DarkPhoenix
fda83bcb49 Check cargo command reliability 2019-04-20 00:19:17 +03:00
DarkPhoenix
85b046a640 Finish reworking booster command invokation 2019-04-20 00:07:27 +03:00
DarkPhoenix
1177575f77 Make item remove context menu safer 2019-04-19 23:12:29 +03:00
DarkPhoenix
fc4a10efe3 Fix booster removal 2019-04-19 22:57:06 +03:00
DarkPhoenix
f541b4329e Add extra checks to variations context menu 2019-04-19 20:05:33 +03:00
DarkPhoenix
7ba1a4c78f Avoid unnecessary calculation 2019-04-19 19:54:21 +03:00
DarkPhoenix
0675ed9a73 Remove drone stacks when holding alt 2019-04-19 19:40:59 +03:00
DarkPhoenix
68a13a6bb8 Use alt key in more actions as alternative or replacement to ctrl 2019-04-19 16:59:30 +03:00
DarkPhoenix
61ef7c3487 Make market item list single-selectable 2019-04-19 15:51:04 +03:00
DarkPhoenix
a5bb16c460 Add drone stack via ctrl-doubleclick 2019-04-19 13:56:17 +03:00
DarkPhoenix
e694ced86c Add drone stack context menu now also follows drone bandwidth limits 2019-04-19 13:34:04 +03:00
DarkPhoenix
babc2d1e42 Add 'stack' word to remove drone menu
Because that's what it does
2019-04-19 13:06:00 +03:00
DarkPhoenix
8e717b19d9 All lines are already unicode in py3 2019-04-19 12:46:26 +03:00
DarkPhoenix
dbca0f9dea Limit drone stacks by ship bandwidth too 2019-04-19 12:29:49 +03:00
DarkPhoenix
7380244cd9 Bump sqlalchemy requirement 2019-04-19 10:48:25 +03:00
DarkPhoenix
5b7c777d6b Add 2 functions which calculate how many drones ship can release / store 2019-04-19 09:36:28 +03:00
DarkPhoenix
2fb9d3479f When mass-changing charges, take not only items with the same typeID but all items from the same market group 2019-04-19 01:02:44 +03:00
DarkPhoenix
21f095250d Make it possible to fill with mutated modules 2019-04-18 21:36:07 +03:00
DarkPhoenix
270376e09c Swap variations and mutaplasmid conversion menu items 2019-04-18 21:21:59 +03:00
DarkPhoenix
1ed71c6580 Fix mistype in readme and add myself to contacts 2019-04-18 18:04:12 +03:00
DarkPhoenix
febc98045c Remove some GTK-specific close buttons (as gnome now has close titlebar in dialogs for a while) and resize some dialogs 2019-04-18 16:58:28 +03:00
DarkPhoenix
72ecc62732 Even bigger on linux! 2019-04-18 16:45:14 +03:00
DarkPhoenix
9e1681d3f9 Do not show boosters in implant editor search output 2019-04-18 16:41:18 +03:00
DarkPhoenix
0471ffa924 Change size of implant editor to fit some quite long hardwiring names (like em-8 series) 2019-04-18 16:38:57 +03:00
DarkPhoenix
4269a00428 Show deadspace variations before officer variations 2019-04-18 15:57:19 +03:00
DarkPhoenix
440b2caa8d Ensure that in variations menu, items are sorted by name within the same meta group and meta level 2019-04-18 15:54:56 +03:00
237 changed files with 5657 additions and 3574 deletions

View File

@@ -24,7 +24,7 @@ $ brew install Caskroom/cask/pyfa
### Linux Distro-specific Packages
The following is a list of pyfa packages available for certain distributions. Please note that these packages are maintained by third-parties and are not evaluated by the pyfa developers.
* Debian/Ubuntu/derivitives: https://github.com/AdamMajer/Pyfa/releases
* Debian/Ubuntu/derivatives: https://github.com/AdamMajer/Pyfa/releases
* Arch: https://aur.archlinux.org/packages/pyfa/
* openSUSE: https://build.opensuse.org/package/show/home:rmk2/pyfa
* FreeBSD: http://www.freshports.org/games/pyfa/ (see [#484](https://github.com/pyfa-org/Pyfa/issues/484) for instructions)
@@ -53,6 +53,10 @@ pyfa is licensed under the GNU GPL v3.0, see LICENSE
* [TweetFleet Slack](https://www.fuzzwork.co.uk/tweetfleet-slack-invites/): @blitzmann
* [Gitter chat](https://gitter.im/pyfa-org/Pyfa): @ blitzmann
* Email: sable.blitzmann@gmail.com
* Kadesh / DarkPhoenix
* GitHub: @DarkFenX
* EVE: Kadesh Priestess
* Email: phoenix@mail.ru
## CCP Copyright Notice
EVE Online, the EVE logo, EVE and all associated logos and designs are the intellectual property of CCP hf. All artwork, screenshots, characters, vehicles, storylines, world facts or other recognizable features of the intellectual property relating to these trademarks are likewise the intellectual property of CCP hf. EVE Online and the EVE logo are the registered trademarks of CCP hf. All rights are reserved worldwide. All other trademarks are the property of their respective owners. CCP hf. has granted permission to pyfa to use EVE Online and all associated logos and designs for promotional and information purposes on its website but does not endorse, and is not in any way affiliated with, pyfa. CCP is in no way responsible for the content on or functioning of this program, nor can it be liable for any damage arising from the use of this program.

View File

@@ -29,7 +29,8 @@ added_files = [
import_these = [
'numpy.core._dtype_ctypes' # https://github.com/pyinstaller/pyinstaller/issues/3982
'numpy.core._dtype_ctypes', # https://github.com/pyinstaller/pyinstaller/issues/3982
'sqlalchemy.ext.baked' # windows build doesn't launch without if when using sqlalchemy 1.3.x
]
icon = os.path.join(os.getcwd(), "dist_assets", "mac", "pyfa.icns")
@@ -86,4 +87,4 @@ app = BUNDLE(
'CFBundleDisplayName': 'pyfa',
'CFBundleIdentifier': 'org.pyfaorg.pyfa',
}
)
)

View File

@@ -17,7 +17,7 @@ added_files = [
('../../imgs/gui/*.gif', 'imgs/gui'),
('../../imgs/icons/*.png', 'imgs/icons'),
('../../imgs/renders/*.png', 'imgs/renders'),
('../../service/jargon/*.yaml', 'service/jargon'),
('../../service/jargon/*.yaml', 'service/jargon'),
('../../dist_assets/win/pyfa.ico', '.'),
('../../dist_assets/win/pyfa.exe.manifest', '.'),
('../../dist_assets/win/Microsoft.VC90.CRT.manifest', '.'),
@@ -29,7 +29,8 @@ added_files = [
]
import_these = [
'numpy.core._dtype_ctypes' # https://github.com/pyinstaller/pyinstaller/issues/3982
'numpy.core._dtype_ctypes', # https://github.com/pyinstaller/pyinstaller/issues/3982
'sqlalchemy.ext.baked' # windows build doesn't launch without if when using sqlalchemy 1.3.x
]
# Walk directories that do dynamic importing

View File

@@ -93,3 +93,11 @@ class SpoolType(IntEnum):
SCALE = 0 # [0..1]
TIME = 1 # Expressed via time in seconds since spool up started
CYCLES = 2 # Expressed in amount of cycles since spool up started
@unique
class FitSystemSecurity(IntEnum):
HISEC = 0
LOWSEC = 1
NULLSEC = 2
WSPACE = 3

View File

@@ -0,0 +1,15 @@
"""
Migration 31
- added fit system security column
"""
import sqlalchemy
def upgrade(saveddata_engine):
try:
saveddata_engine.execute("SELECT systemSecurity FROM fits LIMIT 1")
except sqlalchemy.exc.DatabaseError:
saveddata_engine.execute("ALTER TABLE fits ADD COLUMN systemSecurity INT")

View File

@@ -44,6 +44,7 @@ from eos.saveddata.module import Module
from eos.saveddata.targetResists import TargetResists
from eos.saveddata.user import User
fits_table = Table("fits", saveddata_meta,
Column("ID", Integer, primary_key=True),
Column("ownerID", ForeignKey("users.ID"), nullable=True, index=True),
@@ -59,7 +60,8 @@ fits_table = Table("fits", saveddata_meta,
Column("notes", String, nullable=True),
Column("ignoreRestrictions", Boolean, default=0),
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
Column("modified", DateTime, nullable=True, default=datetime.datetime.now, onupdate=datetime.datetime.now)
Column("modified", DateTime, nullable=True, default=datetime.datetime.now, onupdate=datetime.datetime.now),
Column("systemSecurity", Integer, nullable=True)
)
projectedFits_table = Table("projectedFits", saveddata_meta,

View File

@@ -267,7 +267,7 @@ class HandledImplantList(HandledList):
HandledList.insert(self, idx, implant)
self.remove(implant)
raise HandledListActionError(implant)
HandledList.append(self, implant)
HandledList.insert(self, idx, implant)
def makeRoom(self, implant):
# if needed, remove booster that was occupying slot
@@ -308,7 +308,7 @@ class HandledBoosterList(HandledList):
HandledList.insert(self, idx, booster)
self.remove(booster)
raise HandledListActionError(booster)
HandledList.append(self, booster)
HandledList.insert(self, idx, booster)
def makeRoom(self, booster):
# if needed, remove booster that was occupying slot

View File

@@ -19,7 +19,7 @@
import eos.config
from eos.const import FittingModuleState
from eos.const import FittingModuleState, FitSystemSecurity
from eos.utils.spoolSupport import SpoolType, SpoolOptions, calculateSpoolup, resolveSpoolOptions
@@ -6739,7 +6739,7 @@ class Effect2302(BaseEffect):
damageControl
Used by:
Modules from group: Damage Control (22 of 27)
Modules from group: Damage Control (24 of 29)
"""
type = 'passive'
@@ -9050,7 +9050,7 @@ class Effect3001(BaseEffect):
Used by:
Modules from group: Missile Launcher Torpedo (22 of 22)
Items from market group: Ship Equipment > Turrets & Bays (429 of 883)
Items from market group: Ship Equipment > Turrets & Bays (429 of 888)
Module: Interdiction Sphere Launcher I
"""
@@ -9113,7 +9113,7 @@ class Effect3025(BaseEffect):
Used by:
Modules from group: Energy Weapon (101 of 214)
Modules from group: Hybrid Weapon (105 of 221)
Modules from group: Precursor Weapon (15 of 15)
Modules from group: Precursor Weapon (18 of 18)
Modules from group: Projectile Weapon (99 of 165)
"""
@@ -10027,16 +10027,16 @@ class Effect3380(BaseEffect):
@staticmethod
def handler(fit, module, context):
if 'projected' in context:
fit.ship.increaseItemAttr('warpScrambleStatus', module.getModifiedItemAttr('warpScrambleStrength'))
if module.charge is not None and module.charge.ID == 45010:
for mod in fit.modules:
if not mod.isEmpty and mod.item.requiresSkill('High Speed Maneuvering') and mod.state > FittingModuleState.ONLINE:
mod.state = FittingModuleState.ONLINE
if not mod.isEmpty and mod.item.requiresSkill('Micro Jump Drive Operation') and mod.state > FittingModuleState.ONLINE:
mod.state = FittingModuleState.ONLINE
if module.charge is not None:
if module.charge.ID in (29003, 45010):
fit.ship.increaseItemAttr('warpScrambleStatus', module.getModifiedItemAttr('warpScrambleStrength'))
if module.charge.ID == 45010:
fit.modules.filteredItemIncrease(
lambda mod: mod.item.requiresSkill('High Speed Maneuvering') or mod.item.requiresSkill('Micro Jump Drive Operation'),
'activationBlocked', 1)
else:
fit.ship.forceItemAttr('disallowAssistance', 1)
if module.charge is None:
fit.ship.boostItemAttr('mass', module.getModifiedItemAttr('massBonusPercentage'))
fit.ship.boostItemAttr('signatureRadius', module.getModifiedItemAttr('signatureRadiusBonus'))
@@ -10045,8 +10045,6 @@ class Effect3380(BaseEffect):
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == 'Propulsion Module',
'speedFactor', module.getModifiedItemAttr('speedFactorBonus'))
fit.ship.forceItemAttr('disallowAssistance', 1)
class Effect3392(BaseEffect):
"""
@@ -16287,9 +16285,9 @@ class Effect4902(BaseEffect):
MWDSignatureRadiusRoleBonus
Used by:
Ships from group: Assault Frigate (8 of 12)
Ships from group: Command Destroyer (4 of 4)
Ships from group: Heavy Assault Cruiser (8 of 11)
Ships from group: Assault Frigate (9 of 13)
Ships from group: Command Destroyer (4 of 5)
Ships from group: Heavy Assault Cruiser (9 of 12)
"""
type = 'passive'
@@ -23394,16 +23392,9 @@ class Effect5934(BaseEffect):
return
fit.ship.increaseItemAttr('warpScrambleStatus', module.getModifiedItemAttr('warpScrambleStrength'))
# this is such a dirty hack
for mod in fit.modules:
if not mod.isEmpty and mod.state > FittingModuleState.ONLINE and (
mod.item.requiresSkill('Micro Jump Drive Operation') or
mod.item.requiresSkill('High Speed Maneuvering')
):
mod.state = FittingModuleState.ONLINE
if not mod.isEmpty and mod.item.requiresSkill('Micro Jump Drive Operation') and mod.state > FittingModuleState.ONLINE:
mod.state = FittingModuleState.ONLINE
fit.modules.filteredItemIncrease(
lambda mod: mod.item.requiresSkill('High Speed Maneuvering') or mod.item.requiresSkill('Micro Jump Drive Operation'),
'activationBlocked', module.getModifiedItemAttr('activationBlockedStrenght'))
class Effect5938(BaseEffect):
@@ -25239,7 +25230,7 @@ class Effect6214(BaseEffect):
roleBonusCDLinksPGReduction
Used by:
Ships from group: Command Destroyer (4 of 4)
Ships from group: Command Destroyer (5 of 5)
Ship: Porpoise
"""
@@ -25292,12 +25283,9 @@ class Effect6222(BaseEffect):
def handler(fit, module, context):
if 'projected' in context:
fit.ship.increaseItemAttr('warpScrambleStatus', module.getModifiedItemAttr('warpScrambleStrength'))
if module.charge is not None and module.charge.ID == 47336:
for mod in fit.modules:
if not mod.isEmpty and mod.item.requiresSkill('High Speed Maneuvering') and mod.state > FittingModuleState.ONLINE:
mod.state = FittingModuleState.ONLINE
if not mod.isEmpty and mod.item.requiresSkill('Micro Jump Drive Operation') and mod.state > FittingModuleState.ONLINE:
mod.state = FittingModuleState.ONLINE
fit.modules.filteredItemIncrease(
lambda mod: mod.item.requiresSkill('High Speed Maneuvering') or mod.item.requiresSkill('Micro Jump Drive Operation'),
'activationBlocked', module.getModifiedItemAttr('activationBlockedStrenght'))
class Effect6230(BaseEffect):
@@ -25824,8 +25812,7 @@ class Effect6315(BaseEffect):
eliteBonusCommandDestroyerSkirmish1
Used by:
Ship: Bifrost
Ship: Magus
Ships from group: Command Destroyer (3 of 5)
"""
type = 'passive'
@@ -25874,7 +25861,7 @@ class Effect6317(BaseEffect):
eliteBonusCommandDestroyerMJFGspool2
Used by:
Ships from group: Command Destroyer (4 of 4)
Ships from group: Command Destroyer (5 of 5)
"""
type = 'passive'
@@ -26147,8 +26134,7 @@ class Effect6334(BaseEffect):
eliteBonusCommandDestroyerInfo1
Used by:
Ship: Pontifex
Ship: Stork
Ships from group: Command Destroyer (3 of 5)
"""
type = 'passive'
@@ -28908,7 +28894,7 @@ class Effect6581(BaseEffect):
@staticmethod
def handler(fit, src, context):
# Remote effect bonuses (duration / amount / range / fallout)
# Remote effect bonuses (duration / amount / range / falloff)
for skill, amtAttr, stack in (
('Capital Remote Armor Repair Systems', 'armorDamageAmount', True),
('Capital Shield Emission Systems', 'shieldBonus', True),
@@ -28924,14 +28910,15 @@ class Effect6581(BaseEffect):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill(skill), 'falloffEffectiveness',
src.getModifiedItemAttr('siegeRemoteLogisticsRangeBonus'), stackingPenalties=True)
# Local armor/shield rep effects (duration / amoutn)
# Local armor/shield rep effects (duration / amount)
for skill, amtAttr in (
('Capital Shield Operation', 'shieldBonus'),
('Capital Repair Systems', 'armorDamageAmount')):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill(skill), 'duration',
src.getModifiedItemAttr('siegeLocalLogisticsDurationBonus'))
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill(skill), amtAttr,
src.getModifiedItemAttr('siegeLocalLogisticsAmountBonus'))
src.getModifiedItemAttr('siegeLocalLogisticsAmountBonus'),
stackingPenalties=True)
# Speed bonus
fit.ship.boostItemAttr('maxVelocity', src.getModifiedItemAttr('speedFactor'), stackingPenalties=True)
@@ -30521,7 +30508,14 @@ class Effect6672(BaseEffect):
@staticmethod
def handler(fit, module, context):
secModifier = module.getModifiedItemAttr('securityModifier')
secMap = {
FitSystemSecurity.HISEC: 'hiSecModifier',
FitSystemSecurity.LOWSEC: 'lowSecModifier',
FitSystemSecurity.NULLSEC: 'nullSecModifier',
FitSystemSecurity.WSPACE: 'nullSecModifier'}
fitSec = fit.getSystemSecurity()
attrName = secMap[fitSec]
secModifier = module.getModifiedItemAttr(attrName)
module.multiplyItemAttr('structureRigDoomsdayDamageLossTargetBonus', secModifier)
module.multiplyItemAttr('structureRigScanResBonus', secModifier)
module.multiplyItemAttr('structureRigPDRangeBonus', secModifier)
@@ -32257,7 +32251,7 @@ class Effect6845(BaseEffect):
shipBonusCommandDestroyerRole1DefenderBonus
Used by:
Ships from group: Command Destroyer (4 of 4)
Ships from group: Command Destroyer (4 of 5)
"""
type = 'passive'
@@ -33806,10 +33800,10 @@ class Effect6994(BaseEffect):
class Effect6995(BaseEffect):
"""
targetABCAttack
targetDisintegratorAttack
Used by:
Modules from group: Precursor Weapon (15 of 15)
Modules from group: Precursor Weapon (18 of 18)
"""
type = 'active'
@@ -33973,6 +33967,7 @@ class Effect7012(BaseEffect):
Used by:
Variations of module: Assault Damage Control I (5 of 5)
Module: Abyssal Assault Damage Control
"""
runTime = 'early'
@@ -34149,6 +34144,7 @@ class Effect7026(BaseEffect):
@staticmethod
def handler(fit, src, context, *args, **kwargs):
src.boostItemAttr('maxRange', src.getModifiedChargeAttr('warpScrambleRangeBonus'))
src.forceItemAttr('activationBlockedStrenght', src.getModifiedChargeAttr('activationBlockedStrenght'))
class Effect7027(BaseEffect):
@@ -34856,7 +34852,7 @@ class Effect7077(BaseEffect):
disintegratorWeaponDamageMultiply
Used by:
Modules from group: Entropic Radiation Sink (4 of 4)
Modules from group: Entropic Radiation Sink (6 of 6)
"""
type = 'passive'
@@ -34873,7 +34869,7 @@ class Effect7078(BaseEffect):
disintegratorWeaponSpeedMultiply
Used by:
Modules from group: Entropic Radiation Sink (4 of 4)
Modules from group: Entropic Radiation Sink (6 of 6)
"""
type = 'passive'
@@ -34922,8 +34918,8 @@ class Effect7085(BaseEffect):
shipbonusPCTDamagePC1
Used by:
Variations of ship: Vedmak (2 of 2)
Ship: Tiamat
Ship: Vedmak
"""
type = 'passive'
@@ -34939,8 +34935,8 @@ class Effect7086(BaseEffect):
shipbonusPCTTrackingPC2
Used by:
Variations of ship: Vedmak (2 of 2)
Ship: Tiamat
Ship: Vedmak
"""
type = 'passive'
@@ -34956,7 +34952,6 @@ class Effect7087(BaseEffect):
shipbonusPCTOptimalPF2
Used by:
Ship: Damavik
Ship: Hydra
"""
@@ -34973,7 +34968,7 @@ class Effect7088(BaseEffect):
shipbonusPCTDamagePF1
Used by:
Ship: Damavik
Variations of ship: Damavik (2 of 2)
Ship: Hydra
"""
@@ -35005,13 +35000,13 @@ class Effect7092(BaseEffect):
shipBonusRemoteRepCapNeedRoleBonus2
Used by:
Ship: Damavik
Variations of ship: Damavik (2 of 2)
Variations of ship: Kikimora (2 of 2)
Variations of ship: Vedmak (2 of 2)
Ship: Drekavac
Ship: Hydra
Ship: Kikimora
Ship: Leshak
Ship: Tiamat
Ship: Vedmak
"""
type = 'passive'
@@ -35027,14 +35022,14 @@ class Effect7093(BaseEffect):
shipBonusSmartbombCapNeedRoleBonus2
Used by:
Variations of ship: Damavik (2 of 2)
Variations of ship: Kikimora (2 of 2)
Variations of ship: Rodiva (2 of 2)
Ship: Damavik
Variations of ship: Vedmak (2 of 2)
Ship: Drekavac
Ship: Hydra
Ship: Kikimora
Ship: Leshak
Ship: Tiamat
Ship: Vedmak
"""
type = 'passive'
@@ -35050,13 +35045,13 @@ class Effect7094(BaseEffect):
shipBonusRemoteRepMaxRangeRoleBonus1
Used by:
Ship: Damavik
Variations of ship: Damavik (2 of 2)
Variations of ship: Kikimora (2 of 2)
Variations of ship: Vedmak (2 of 2)
Ship: Drekavac
Ship: Hydra
Ship: Kikimora
Ship: Leshak
Ship: Tiamat
Ship: Vedmak
"""
type = 'passive'
@@ -35088,7 +35083,7 @@ class Effect7111(BaseEffect):
systemSmallPrecursorTurretDamage
Used by:
Celestials named like: Wolf Rayet Effect Beacon Class (5 of 6)
Celestials named like: Wolf Rayet Effect Beacon Class (6 of 6)
"""
runTime = 'early'
@@ -35106,13 +35101,13 @@ class Effect7112(BaseEffect):
shipBonusNeutCapNeedRoleBonus2
Used by:
Ship: Damavik
Variations of ship: Damavik (2 of 2)
Variations of ship: Kikimora (2 of 2)
Variations of ship: Vedmak (2 of 2)
Ship: Drekavac
Ship: Hydra
Ship: Kikimora
Ship: Leshak
Ship: Tiamat
Ship: Vedmak
"""
type = 'passive'
@@ -35218,7 +35213,7 @@ class Effect7154(BaseEffect):
shipBonusPD1DisintegratorDamage
Used by:
Ship: Kikimora
Variations of ship: Kikimora (2 of 2)
"""
type = 'passive'
@@ -35252,7 +35247,7 @@ class Effect7156(BaseEffect):
smallDisintegratorMaxRangeBonus
Used by:
Ship: Kikimora
Variations of ship: Kikimora (2 of 2)
"""
type = 'passive'
@@ -35268,7 +35263,7 @@ class Effect7157(BaseEffect):
shipBonusPD2DisintegratorMaxRange
Used by:
Ship: Kikimora
Variations of ship: Kikimora (2 of 2)
"""
type = 'passive'
@@ -35624,3 +35619,255 @@ class Effect7186(BaseEffect):
def handler(fit, ship, context):
fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill('Medium Drone Operation'),
'armorHP', ship.getModifiedItemAttr('shipBonusRole8'))
class Effect7193(BaseEffect):
"""
systemMiningCycleTimeBonus
Used by:
Celestials named like: Invasion Effects (3 of 3)
"""
runTime = 'early'
type = ('projected', 'passive')
@staticmethod
def handler(fit, beacon, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill('Mining'),
'duration', beacon.getModifiedItemAttr('miningDurationMultiplier'),
stackingPenalties=True)
fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill('Mining'),
'duration', beacon.getModifiedItemAttr('miningDurationMultiplier'),
stackingPenalties=True)
class Effect7194(BaseEffect):
"""
systemHullHPBonusPercent
Used by:
Celestials named like: Invasion Effects (3 of 3)
"""
runTime = 'early'
type = ('projected', 'passive')
@staticmethod
def handler(fit, beacon, context):
fit.ship.boostItemAttr('hp', beacon.getModifiedItemAttr('hullHpBonus'), stackingPenalties=True)
class Effect7195(BaseEffect):
"""
systemAgilityBonusPercent
Used by:
Celestials named like: Invasion Effects (3 of 3)
"""
runTime = 'early'
type = ('projected', 'passive')
@staticmethod
def handler(fit, beacon, context):
fit.ship.boostItemAttr('agility', beacon.getModifiedItemAttr('agilityBonus'), stackingPenalties=True)
class Effect7202(BaseEffect):
"""
systemDroneSpeedBonusPercent
Used by:
Celestials named like: Invasion Effects (3 of 3)
"""
runTime = 'early'
type = ('projected', 'passive')
@staticmethod
def handler(fit, beacon, context):
fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill('Drones'),
'maxVelocity', beacon.getModifiedItemAttr('droneMaxVelocityBonus'),
stackingPenalties=True)
class Effect7203(BaseEffect):
"""
systemDroneDamageBonusPercent
Used by:
Celestials named like: Invasion Effects (3 of 3)
"""
runTime = 'early'
type = ('projected', 'passive')
@staticmethod
def handler(fit, beacon, context):
fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill('Drones'),
'damageMultiplier', beacon.getModifiedItemAttr('droneDamageBonus'),
stackingPenalties=True)
class Effect7204(BaseEffect):
"""
shipArmorEMResistancePF2
Used by:
Variations of ship: Damavik (2 of 2)
"""
type = 'passive'
@staticmethod
def handler(fit, ship, context):
fit.ship.boostItemAttr('armorEmDamageResonance', ship.getModifiedItemAttr('shipBonusPF2'), skill='Precursor Frigate')
class Effect7205(BaseEffect):
"""
shipArmorKinResistancePF2
Used by:
Variations of ship: Damavik (2 of 2)
"""
type = 'passive'
@staticmethod
def handler(fit, ship, context):
fit.ship.boostItemAttr('armorKineticDamageResonance', ship.getModifiedItemAttr('shipBonusPF2'), skill='Precursor Frigate')
class Effect7206(BaseEffect):
"""
shipArmorThermResistancePF2
Used by:
Variations of ship: Damavik (2 of 2)
"""
type = 'passive'
@staticmethod
def handler(fit, ship, context):
fit.ship.boostItemAttr('armorThermalDamageResonance', ship.getModifiedItemAttr('shipBonusPF2'), skill='Precursor Frigate')
class Effect7207(BaseEffect):
"""
shipArmorExpResistancePF2
Used by:
Variations of ship: Damavik (2 of 2)
"""
type = 'passive'
@staticmethod
def handler(fit, ship, context):
fit.ship.boostItemAttr('armorExplosiveDamageResonance', ship.getModifiedItemAttr('shipBonusPF2'), skill='Precursor Frigate')
class Effect7210(BaseEffect):
"""
shipBonusCommandDestroyerRole2DefenderBonus
Used by:
Ship: Draugur
"""
type = 'passive'
@staticmethod
def handler(fit, ship, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill('Defender Missiles'),
'moduleReactivationDelay', ship.getModifiedItemAttr('shipBonusRole2'))
class Effect7211(BaseEffect):
"""
shipDmgMultiMaxEliteHeavyGunship1
Used by:
Ship: Ikitursa
"""
type = 'passive'
@staticmethod
def handler(fit, ship, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill('Medium Precursor Weapon'),
'damageMultiplierBonusMax', ship.getModifiedItemAttr('eliteBonusHeavyGunship1'),
skill='Heavy Assault Cruisers')
class Effect7216(BaseEffect):
"""
shipDmgMultiMaxEliteGunship1
Used by:
Ship: Nergal
"""
type = 'passive'
@staticmethod
def handler(fit, ship, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill('Small Precursor Weapon'),
'damageMultiplierBonusMax', ship.getModifiedItemAttr('eliteBonusGunship1'),
skill='Assault Frigates')
class Effect7217(BaseEffect):
"""
shipNosNeutSmartPowerReductionEliteGunship2
Used by:
Ship: Nergal
"""
type = 'passive'
@staticmethod
def handler(fit, ship, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == 'Energy Neutralizer',
'power', ship.getModifiedItemAttr('eliteBonusGunship2'),
skill='Assault Frigates')
class Effect7219(BaseEffect):
"""
shipSmartBombPowerReductionEliteGunship2
Used by:
Ship: Ikitursa
Ship: Nergal
"""
type = 'passive'
@staticmethod
def handler(fit, ship, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == 'Smart Bomb',
'power', ship.getModifiedItemAttr('eliteBonusGunship2'),
skill='Assault Frigates')
class Effect7222(BaseEffect):
"""
shipNeutPowerReductionEliteHeavyGunship2
Used by:
Ship: Ikitursa
"""
type = 'passive'
@staticmethod
def handler(fit, ship, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == 'Energy Neutralizer',
'power', ship.getModifiedItemAttr('eliteBonusHeavyGunship2'),
skill='Heavy Assault Cruisers')

View File

@@ -250,6 +250,17 @@ class Item(EqBase):
return self.__attributes
@property
def attribsWithOverrides(self):
overrides = self.overrides
attribs = {}
for aname, attr in self.attributes.items():
if aname in overrides:
attribs[aname] = overrides[aname]
else:
attribs[aname] = attr
return attribs
def getAttribute(self, key, default=None):
if key in self.attributes:
return self.attributes[key].value
@@ -479,7 +490,7 @@ class Item(EqBase):
@property
def isModule(self):
return self.category.name == 'Module'
return self.category.name in ('Module', 'Structure Module')
@property
def isSubsystem(self):

View File

@@ -218,13 +218,13 @@ class ModifiedAttributeDict(collections.MutableMapping):
if attrInfo is None:
cappingId = cappingAttrKeyCache[key] = None
else:
# see GH issue #620
cappingId = cappingAttrKeyCache[key] = attrInfo.maxAttributeID
cappingId = attrInfo.maxAttributeID
if cappingId is None:
cappingKey = None
else:
cappingAttrInfo = getAttributeInfo(cappingId)
cappingKey = None if cappingAttrInfo is None else cappingAttrInfo.name
cappingAttrKeyCache[key] = cappingKey
if cappingKey:
cappingValue = self.original.get(cappingKey, self.__calculateValue(cappingKey))
@@ -238,7 +238,7 @@ class ModifiedAttributeDict(collections.MutableMapping):
if force is not None:
if cappingValue is not None:
force = min(force, cappingValue)
if key in (50, 30, 48, 11):
if key in ("cpu", "power", "cpuOutput", "powerOutput"):
force = round(force, 2)
return force
# Grab our values if they're there, otherwise we'll take default values
@@ -259,11 +259,7 @@ class ModifiedAttributeDict(collections.MutableMapping):
dv = attrInfo.defaultValue
default = defaultValuesCache[key] = dv if dv is not None else 0.0
val = self.__intermediary.get(key,
self.__preAssigns.get(key,
self.getOriginal(key, default)
)
)
val = self.__intermediary.get(key, self.__preAssigns.get(key, self.getOriginal(key, default)))
# We'll do stuff in the following order:
# preIncrease > multiplier > stacking penalized multipliers > postIncrease
@@ -293,7 +289,7 @@ class ModifiedAttributeDict(collections.MutableMapping):
# Cap value if we have cap defined
if cappingValue is not None:
val = min(val, cappingValue)
if key in (50, 30, 48, 11):
if key in ("cpu", "power", "cpuOutput", "powerOutput"):
val = round(val, 2)
return val

View File

@@ -17,26 +17,27 @@
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
import datetime
import time
from copy import deepcopy
from itertools import chain
from math import sqrt, log, asinh
import datetime
from sqlalchemy.orm import validates, reconstructor
from logbook import Logger
from math import asinh, log, sqrt
from sqlalchemy.orm import reconstructor, validates
import eos.db
from eos import capSim
from eos.effectHandlerHelpers import HandledModuleList, HandledDroneCargoList, HandledImplantList, HandledBoosterList, HandledProjectedDroneList, HandledProjectedModList
from eos.const import ImplantLocation, CalcType, FittingSlot
from eos.saveddata.ship import Ship
from eos.saveddata.drone import Drone
from eos.const import CalcType, FitSystemSecurity, FittingHardpoint, FittingModuleState, FittingSlot, ImplantLocation
from eos.effectHandlerHelpers import (
HandledBoosterList, HandledDroneCargoList, HandledImplantList,
HandledModuleList, HandledProjectedDroneList, HandledProjectedModList)
from eos.saveddata.character import Character
from eos.saveddata.citadel import Citadel
from eos.const import FittingModuleState, FittingHardpoint
from eos.saveddata.module import Module
from eos.saveddata.ship import Ship
from eos.utils.stats import DmgTypes
from logbook import Logger
pyfalog = Logger(__name__)
@@ -924,8 +925,10 @@ class Fit(object):
recalc. Figure out a way to keep track of any changes to slot layout and call this automatically
"""
if self.ship is None:
return
return {}
# Look for any dummies of that type to remove
posToRemove = {}
for slotType in (FittingSlot.LOW.value, FittingSlot.MED.value, FittingSlot.HIGH.value, FittingSlot.RIG.value, FittingSlot.SUBSYSTEM.value, FittingSlot.SERVICE.value):
amount = self.getSlotsFree(slotType, True)
if amount > 0:
@@ -933,16 +936,17 @@ class Fit(object):
self.modules.append(Module.buildEmpty(slotType))
if amount < 0:
# Look for any dummies of that type to remove
toRemove = []
for mod in self.modules:
if mod.isEmpty and mod.slot == slotType:
toRemove.append(mod)
pos = self.modules.index(mod)
posToRemove[pos] = slotType
amount += 1
if amount == 0:
break
for mod in toRemove:
self.modules.remove(mod)
for pos in sorted(posToRemove, reverse=True):
mod = self.modules[pos]
self.modules.remove(mod)
return posToRemove
def unfill(self):
for i in range(len(self.modules) - 1, -1, -1):
@@ -1507,6 +1511,28 @@ class Fit(object):
return True
def getReleaseLimitForDrone(self, item):
if not item.isDrone:
return 0
bw = round(self.ship.getModifiedItemAttr("droneBandwidth"))
volume = round(item.attribsWithOverrides['volume'].value)
return int(bw / volume)
def getStoreLimitForDrone(self, item):
if not item.isDrone:
return 0
bayTotal = round(self.ship.getModifiedItemAttr("droneCapacity"))
bayUsed = round(self.droneBayUsed)
volume = item.attribsWithOverrides['volume'].value
return int((bayTotal - bayUsed) / volume)
def getSystemSecurity(self):
secstatus = self.systemSecurity
# Default to nullsec
if secstatus is None:
secstatus = FitSystemSecurity.NULLSEC
return secstatus
def __deepcopy__(self, memo=None):
fitCopy = Fit()
# Character and owner are not copied
@@ -1518,6 +1544,7 @@ class Fit(object):
fitCopy.damagePattern = self.damagePattern
fitCopy.targetResists = self.targetResists
fitCopy.implantLocation = self.implantLocation
fitCopy.systemSecurity = self.systemSecurity
fitCopy.notes = self.notes
toCopy = (

View File

@@ -550,6 +550,8 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
if not fits and fit.ignoreRestrictions:
self.restrictionOverridden = True
fits = True
elif fits and fit.ignoreRestrictions:
self.restrictionOverridden = False
return fits
@@ -567,6 +569,8 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
if self.slot == FittingSlot.SUBSYSTEM:
subSlot = self.getModifiedItemAttr("subSystemSlot")
for mod in fit.modules:
if mod is self:
continue
if mod.getModifiedItemAttr("subSystemSlot") == subSlot:
return False
@@ -601,7 +605,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
# Check if we're within bounds
if state < -1 or state > 2:
return False
elif state >= FittingModuleState.ACTIVE and not self.item.isType("active"):
elif state >= FittingModuleState.ACTIVE and (not self.item.isType("active") or self.getModifiedItemAttr('activationBlocked') > 0):
return False
elif state == FittingModuleState.OVERHEATED and not self.item.isType("overheat"):
return False
@@ -771,28 +775,26 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
context = ("module",)
projected = False
# if gang:
# context += ("commandRun",)
if self.charge is not None:
# fix for #82 and it's regression #106
if not projected or (self.projected and not forceProjected) or gang:
for effect in self.charge.effects.values():
if effect.runTime == runTime and \
effect.activeByDefault and \
(effect.isType("offline") or
(effect.isType("passive") and self.state >= FittingModuleState.ONLINE) or
(effect.isType("active") and self.state >= FittingModuleState.ACTIVE)) and \
(not gang or (gang and effect.isType("gang"))):
chargeContext = ("moduleCharge",)
# For gang effects, we pass in the effect itself as an argument. However, to avoid going through
# all the effect files and defining this argument, do a simple try/catch here and be done with it.
if (
effect.runTime == runTime and
effect.activeByDefault and (
effect.isType("offline") or
(effect.isType("passive") and self.state >= FittingModuleState.ONLINE) or
(effect.isType("active") and self.state >= FittingModuleState.ACTIVE)) and
(not gang or (gang and effect.isType("gang")))
):
contexts = ("moduleCharge",)
# For gang effects, we pass in the effect itself as an argument. However, to avoid going through all
# the effect definitions and defining this argument, do a simple try/catch here and be done with it.
# @todo: possibly fix this
try:
effect.handler(fit, self, chargeContext, effect=effect)
effect.handler(fit, self, contexts, effect=effect)
except:
effect.handler(fit, self, chargeContext)
effect.handler(fit, self, contexts)
if self.item:
if self.state >= FittingModuleState.OVERHEATED:
@@ -894,7 +896,6 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
@staticmethod
def getProposedState(mod, click, proposedState=None):
# todo: instead of passing in module, make this a instanced function.
pyfalog.debug("Get proposed state for module.")
if mod.slot == FittingSlot.SUBSYSTEM or mod.isEmpty:
return FittingModuleState.ONLINE
@@ -914,13 +915,12 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
state = FittingModuleState.OFFLINE
else:
state = transitionMap[currState]
if not mod.isValidState(state):
state = -1
# If passive module tries to transition into online and fails,
# put it to passive instead
if not mod.isValidState(state) and currState == FittingModuleState.ONLINE:
state = FittingModuleState.OFFLINE
if mod.isValidState(state):
return state
else:
return currState
return mod.getMaxState(proposedState=state)
def __deepcopy__(self, memo):
item = self.item

BIN
eve.db

Binary file not shown.

View File

@@ -34,6 +34,7 @@ from gui.toggle_panel import TogglePanel
class AdditionsPane(TogglePanel):
def __init__(self, parent):
TogglePanel.__init__(self, parent, force_layout=1)
@@ -86,8 +87,8 @@ class AdditionsPane(TogglePanel):
PANES = ["Drones", "Fighters", "Cargo", "Implants", "Boosters", "Projected", "Command", "Notes"]
def select(self, name):
self.notebook.SetSelection(self.PANES.index(name))
def select(self, name, focus=True):
self.notebook.SetSelection(self.PANES.index(name), focus=focus)
def getName(self, idx):
return self.PANES[idx]

View File

@@ -50,8 +50,11 @@ class BitmapLoader(object):
@classmethod
def getStaticBitmap(cls, name, parent, location):
bitmap = cls.getBitmap(name or 0, location)
if bitmap is None:
return None
static = wx.StaticBitmap(parent)
static.SetBitmap(cls.getBitmap(name or 0, location))
static.SetBitmap(bitmap)
return static
@classmethod

View File

@@ -48,6 +48,7 @@ class BoosterViewDrop(wx.DropTarget):
class BoosterView(d.Display):
DEFAULT_COLS = [
"State",
"attr:boosterness",
@@ -58,14 +59,14 @@ class BoosterView(d.Display):
]
def __init__(self, parent):
d.Display.__init__(self, parent, style=wx.LC_SINGLE_SEL | wx.BORDER_NONE)
d.Display.__init__(self, parent, style=wx.BORDER_NONE)
self.lastFitId = None
self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged)
self.mainFrame.Bind(ITEM_SELECTED, self.addItem)
self.Bind(wx.EVT_LEFT_DCLICK, self.removeItem)
self.Bind(wx.EVT_LEFT_DCLICK, self.onLeftDoubleClick)
self.Bind(wx.EVT_LEFT_DOWN, self.click)
self.Bind(wx.EVT_KEY_UP, self.kbEvent)
@@ -87,11 +88,14 @@ class BoosterView(d.Display):
def kbEvent(self, event):
keycode = event.GetKeyCode()
if keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE):
row = self.GetFirstSelected()
if row != -1:
self.removeBooster(self.boosters[self.GetItemData(row)])
mstate = wx.GetMouseState()
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
self.unselectAll()
elif keycode == 65 and mstate.GetModifiers() == wx.MOD_CONTROL:
self.selectAll()
elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and mstate.GetModifiers() == wx.MOD_NONE:
boosters = self.getSelectedBoosters()
self.removeBoosters(boosters)
event.Skip()
def fitChanged(self, event):
@@ -120,7 +124,7 @@ class BoosterView(d.Display):
if item != -1:
self.EnsureVisible(item)
self.deselectItems()
self.unselectAll()
self.update(self.boosters)
event.Skip()
@@ -143,32 +147,78 @@ class BoosterView(d.Display):
self.mainFrame.additionsPane.select('Boosters')
event.Skip()
def removeItem(self, event):
def onLeftDoubleClick(self, event):
row, _ = self.HitTest(event.Position)
if row != -1:
col = self.getColumn(event.Position)
if col != self.getColIndex(State):
self.removeBooster(self.boosters[self.GetItemData(row)])
try:
booster = self.boosters[row]
except IndexError:
return
self.removeBoosters([booster])
def removeBooster(self, booster):
def removeBoosters(self, boosters):
fitID = self.mainFrame.getActiveFit()
self.mainFrame.command.Submit(cmd.GuiRemoveBoosterCommand(fitID=fitID, position=self.original.index(booster)))
positions = []
for booster in boosters:
if booster in self.original:
positions.append(self.original.index(booster))
self.mainFrame.command.Submit(cmd.GuiRemoveBoostersCommand(fitID=fitID, positions=positions))
def click(self, event):
event.Skip()
row, _ = self.HitTest(event.Position)
if row != -1:
mainRow, _ = self.HitTest(event.Position)
if mainRow != -1:
col = self.getColumn(event.Position)
if col == self.getColIndex(State):
fitID = self.mainFrame.getActiveFit()
booster = self.boosters[self.GetItemData(row)]
self.mainFrame.command.Submit(cmd.GuiToggleBoosterStateCommand(fitID=fitID, position=self.original.index(booster)))
try:
mainBooster = self.boosters[mainRow]
except IndexError:
return
if mainBooster in self.original:
mainPosition = self.original.index(mainBooster)
positions = []
for row in self.getSelectedRows():
try:
booster = self.boosters[row]
except IndexError:
continue
if booster in self.original:
positions.append(self.original.index(booster))
if mainPosition not in positions:
positions = [mainPosition]
self.mainFrame.command.Submit(cmd.GuiToggleBoosterStatesCommand(
fitID=fitID,
mainPosition=mainPosition,
positions=positions))
return
event.Skip()
def spawnMenu(self, event):
sel = self.GetFirstSelected()
if sel != -1:
booster = self.boosters[sel]
srcContext = "boosterItem"
itemContext = "Booster"
menu = ContextMenu.getMenu((booster,), (srcContext, itemContext))
selection = self.getSelectedBoosters()
clickedPos = self.getRowByAbs(event.Position)
mainBooster = None
if clickedPos != -1:
try:
booster = self.boosters[clickedPos]
except IndexError:
pass
else:
if booster in self.original:
mainBooster = booster
sourceContext = "boosterItem"
itemContext = None if mainBooster is None else "Booster"
menu = ContextMenu.getMenu(mainBooster, selection, (sourceContext, itemContext))
if menu:
self.PopupMenu(menu)
def getSelectedBoosters(self):
boosters = []
for row in self.getSelectedRows():
try:
booster = self.boosters[row]
except IndexError:
continue
boosters.append(booster)
return boosters

View File

@@ -19,14 +19,14 @@
# noinspection PyPackageRequirements
import wx
import gui.display as d
from gui.builtinViewColumns.state import State
from gui.contextMenu import ContextMenu
import gui.fitCommands as cmd
import gui.globalEvents as GE
from gui.contextMenu import ContextMenu
from gui.utils.staticHelpers import DragDropHelper
from service.fit import Fit
from service.market import Market
import gui.fitCommands as cmd
class CargoViewDrop(wx.DropTarget):
@@ -53,12 +53,12 @@ class CargoView(d.Display):
"Price"]
def __init__(self, parent):
d.Display.__init__(self, parent, style=wx.LC_SINGLE_SEL | wx.BORDER_NONE)
d.Display.__init__(self, parent, style=wx.BORDER_NONE)
self.lastFitId = None
self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged)
self.Bind(wx.EVT_LEFT_DCLICK, self.removeItem)
self.Bind(wx.EVT_LEFT_DCLICK, self.onLeftDoubleClick)
self.Bind(wx.EVT_KEY_UP, self.kbEvent)
self.SetDropTarget(CargoViewDrop(self.handleListDrag))
@@ -78,18 +78,25 @@ class CargoView(d.Display):
if data[0] == "fitting":
self.swapModule(x, y, int(data[1]))
elif data[0] == "market":
fit = self.mainFrame.getActiveFit()
if fit:
self.mainFrame.command.Submit(cmd.GuiAddCargoCommand(fit, int(data[1]), 1))
fitID = self.mainFrame.getActiveFit()
if fitID:
self.mainFrame.command.Submit(cmd.GuiAddCargoCommand(
fitID=fitID, itemID=int(data[1]), amount=1))
def startDrag(self, event):
row = event.GetIndex()
if row != -1:
data = wx.TextDataObject()
dataStr = "cargo:{}".format(self.cargo[row].itemID)
try:
dataStr = "cargo:{}".format(self.cargo[row].itemID)
except IndexError:
return
data.SetText(dataStr)
self.unselectAll()
self.Select(row, True)
dropSource = wx.DropSource(self)
dropSource.SetData(data)
DragDropHelper.data = dataStr
@@ -97,12 +104,14 @@ class CargoView(d.Display):
def kbEvent(self, event):
keycode = event.GetKeyCode()
if keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE):
row = self.GetFirstSelected()
if row != -1:
fitID = self.mainFrame.getActiveFit()
cargo = self.cargo[self.GetItemData(row)]
self.mainFrame.command.Submit(cmd.GuiRemoveCargoCommand(fitID=fitID, itemID=cargo.itemID))
mstate = wx.GetMouseState()
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
self.unselectAll()
elif keycode == 65 and mstate.GetModifiers() == wx.MOD_CONTROL:
self.selectAll()
elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and mstate.GetModifiers() == wx.MOD_NONE:
cargos = self.getSelectedCargos()
self.removeCargos(cargos)
event.Skip()
def swapModule(self, x, y, modIdx):
@@ -111,11 +120,19 @@ class CargoView(d.Display):
fit = sFit.getFit(self.mainFrame.getActiveFit())
dstRow, _ = self.HitTest((x, y))
if dstRow > -1:
try:
dstCargoItemID = getattr(self.cargo[dstRow], 'itemID', None)
except IndexError:
dstCargoItemID = None
else:
dstCargoItemID = None
self.mainFrame.command.Submit(cmd.GuiLocalModuleToCargoCommand(
fitID=self.mainFrame.getActiveFit(),
modPosition=fit.modules[modIdx].modPosition,
cargoItemID=self.cargo[dstRow].itemID if dstRow > -1 else None,
copy=wx.GetMouseState().CmdDown()))
modPosition=modIdx,
cargoItemID=dstCargoItemID,
copy=wx.GetMouseState().GetModifiers() == wx.MOD_CONTROL))
def fitChanged(self, event):
sFit = Fit.getInstance()
@@ -143,28 +160,53 @@ class CargoView(d.Display):
if item != -1:
self.EnsureVisible(item)
self.deselectItems()
self.unselectAll()
self.populate(self.cargo)
self.refresh(self.cargo)
event.Skip()
def removeItem(self, event):
def onLeftDoubleClick(self, event):
row, _ = self.HitTest(event.Position)
if row != -1:
col = self.getColumn(event.Position)
if col != self.getColIndex(State):
fitID = self.mainFrame.getActiveFit()
cargo = self.cargo[self.GetItemData(row)]
self.mainFrame.command.Submit(cmd.GuiRemoveCargoCommand(fitID, cargo.itemID))
try:
cargo = self.cargo[row]
except IndexError:
return
self.removeCargos([cargo])
def removeCargos(self, cargos):
fitID = self.mainFrame.getActiveFit()
itemIDs = []
for cargo in cargos:
if cargo in self.original:
itemIDs.append(cargo.itemID)
self.mainFrame.command.Submit(cmd.GuiRemoveCargosCommand(fitID=fitID, itemIDs=itemIDs))
def spawnMenu(self, event):
sel = self.GetFirstSelected()
if sel != -1:
cargo = self.cargo[sel]
sMkt = Market.getInstance()
sourceContext = "cargoItem"
itemContext = sMkt.getCategoryByItem(cargo.item).name
menu = ContextMenu.getMenu((cargo,), (sourceContext, itemContext))
selection = self.getSelectedCargos()
clickedPos = self.getRowByAbs(event.Position)
mainCargo = None
if clickedPos != -1:
try:
cargo = self.cargo[clickedPos]
except IndexError:
pass
else:
if cargo in self.original:
mainCargo = cargo
sourceContext = "cargoItem"
itemContext = None if mainCargo is None else Market.getInstance().getCategoryByItem(mainCargo.item).name
menu = ContextMenu.getMenu(mainCargo, selection, (sourceContext, itemContext))
if menu:
self.PopupMenu(menu)
def getSelectedCargos(self):
cargos = []
for row in self.getSelectedRows():
try:
cargo = self.cargo[row]
except IndexError:
continue
cargos.append(cargo)
return cargos

View File

@@ -22,24 +22,25 @@ import wx
import gui.builtinAdditionPanes.droneView
import gui.display as d
import gui.fitCommands as cmd
import gui.globalEvents as GE
from gui.builtinShipBrowser.events import EVT_FIT_REMOVED
from eos.saveddata.drone import Drone as es_Drone
from gui.builtinContextMenus.commandFitAdd import AddCommandFit
from gui.builtinShipBrowser.events import EVT_FIT_REMOVED
from gui.builtinViewColumns.state import State
from gui.contextMenu import ContextMenu
from gui.utils.staticHelpers import DragDropHelper
from service.fit import Fit
import gui.fitCommands as cmd
class DummyItem(object):
class DummyItem:
def __init__(self, txt):
self.name = txt
self.iconID = None
class DummyEntry(object):
class DummyEntry:
def __init__(self, txt):
self.item = DummyItem(txt)
@@ -61,10 +62,11 @@ class CommandViewDrop(wx.DropTarget):
class CommandView(d.Display):
DEFAULT_COLS = ["State", "Base Name"]
def __init__(self, parent):
d.Display.__init__(self, parent, style=wx.LC_SINGLE_SEL | wx.BORDER_NONE)
d.Display.__init__(self, parent, style=wx.BORDER_NONE)
self.lastFitId = None
@@ -72,14 +74,13 @@ class CommandView(d.Display):
self.mainFrame.Bind(EVT_FIT_REMOVED, AddCommandFit.populateFits)
self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged)
self.Bind(wx.EVT_LEFT_DOWN, self.click)
self.Bind(wx.EVT_LEFT_DCLICK, self.remove)
self.Bind(wx.EVT_LEFT_DCLICK, self.onLeftDoubleClick)
self.Bind(wx.EVT_KEY_UP, self.kbEvent)
self.droneView = gui.builtinAdditionPanes.droneView.DroneView
self.Bind(wx.EVT_RIGHT_UP, self.spawnMenu)
self.Bind(wx.EVT_CONTEXT_MENU, self.spawnMenu)
self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.startDrag)
self.SetDropTarget(CommandViewDrop(self.handleListDrag))
@staticmethod
@@ -95,30 +96,22 @@ class CommandView(d.Display):
def kbEvent(self, event):
keycode = event.GetKeyCode()
if keycode == wx.WXK_DELETE or keycode == wx.WXK_NUMPAD_DELETE:
fitID = self.mainFrame.getActiveFit()
row = self.GetFirstSelected()
if row != -1:
self.mainFrame.command.Submit(cmd.GuiRemoveCommandFitCommand(fitID, self.get(row).ID))
mstate = wx.GetMouseState()
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
self.unselectAll()
elif keycode == 65 and mstate.GetModifiers() == wx.MOD_CONTROL:
self.selectAll()
elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and mstate.GetModifiers() == wx.MOD_NONE:
commandFits = self.getSelectedCommandFits()
self.removeCommandFits(commandFits)
event.Skip()
def handleDrag(self, type, fitID):
# Those are drags coming from pyfa sources, NOT builtin wx drags
if type == "fit":
activeFit = self.mainFrame.getActiveFit()
if activeFit:
self.mainFrame.command.Submit(cmd.GuiAddCommandFitCommand(activeFit, fitID))
def startDrag(self, event):
row = event.GetIndex()
if row != -1 and isinstance(self.get(row), es_Drone):
data = wx.TextDataObject()
dataStr = "command:" + str(self.GetItemData(row))
data.SetText(dataStr)
dropSource = wx.DropSource(self)
dropSource.SetData(data)
DragDropHelper.data = dataStr
dropSource.DoDragDrop()
self.mainFrame.command.Submit(cmd.GuiAddCommandFitCommand(fitID=activeFit, commandFitID=fitID))
@staticmethod
def fitSort(fit):
@@ -153,9 +146,8 @@ class CommandView(d.Display):
if item != -1:
self.EnsureVisible(item)
self.deselectItems()
self.unselectAll()
# todo: verify
if not stuff:
stuff = [DummyEntry("Drag a fit to this area")]
@@ -163,52 +155,68 @@ class CommandView(d.Display):
event.Skip()
def get(self, row):
if row == -1:
return None
numFits = len(self.fits)
if numFits == 0:
return None
return self.fits[row]
def click(self, event):
event.Skip()
row, _ = self.HitTest(event.Position)
if row != -1:
item = self.get(row)
mainRow, _ = self.HitTest(event.Position)
if mainRow != -1:
col = self.getColumn(event.Position)
if col == self.getColIndex(State):
fitID = self.mainFrame.getActiveFit()
self.mainFrame.command.Submit(cmd.GuiToggleCommandFitStateCommand(fitID, item.ID))
try:
mainCommandFitID = self.fits[mainRow].ID
except IndexError:
return
commandFitIDs = []
for commandFit in self.getSelectedCommandFits():
commandFitIDs.append(commandFit.ID)
if mainCommandFitID not in commandFitIDs:
commandFitIDs = [mainCommandFitID]
self.mainFrame.command.Submit(cmd.GuiToggleCommandFitStatesCommand(
fitID=fitID,
mainCommandFitID=mainCommandFitID,
commandFitIDs=commandFitIDs))
return
event.Skip()
def spawnMenu(self, event):
fitID = self.mainFrame.getActiveFit()
if fitID is None:
return
sel = self.GetFirstSelected()
context = ()
item = self.get(sel)
if item is not None:
fitSrcContext = "commandFit"
fitItemContext = item.name
context = ((fitSrcContext, fitItemContext),)
context += (("commandView",),)
menu = ContextMenu.getMenu((item,) if item is not None else [], *context)
if menu is not None:
selection = self.getSelectedCommandFits()
clickedPos = self.getRowByAbs(event.Position)
mainCommandFit = None
if clickedPos != -1:
try:
mainCommandFit = self.fits[clickedPos]
except IndexError:
pass
contexts = []
if mainCommandFit is not None:
contexts.append(('commandFit', 'Command Fit'))
contexts.append(('commandView',))
menu = ContextMenu.getMenu(mainCommandFit, selection, *contexts)
if menu:
self.PopupMenu(menu)
def remove(self, event):
def onLeftDoubleClick(self, event):
row, _ = self.HitTest(event.Position)
if row != -1:
col = self.getColumn(event.Position)
if col != self.getColIndex(State):
fitID = self.mainFrame.getActiveFit()
thing = self.get(row)
if thing: # thing doesn't exist if it's the dummy value
self.mainFrame.command.Submit(cmd.GuiRemoveCommandFitCommand(fitID, thing.ID))
try:
commandFit = self.fits[row]
except IndexError:
return
self.removeCommandFits([commandFit])
def removeCommandFits(self, commandFits):
fitID = self.mainFrame.getActiveFit()
commandFitIDs = []
for commandFit in commandFits:
if commandFit in self.fits:
commandFitIDs.append(commandFit.ID)
self.mainFrame.command.Submit(cmd.GuiRemoveCommandFitsCommand(fitID=fitID, commandFitIDs=commandFitIDs))
def getSelectedCommandFits(self):
commandFits = []
for row in self.getSelectedRows():
try:
commandFit = self.fits[row]
except IndexError:
continue
commandFits.append(commandFit)
return commandFits

View File

@@ -33,6 +33,7 @@ from gui.utils.staticHelpers import DragDropHelper
from service.fit import Fit
from service.market import Market
import gui.fitCommands as cmd
from gui.fitCommands.helpers import droneStackLimit
class DroneViewDrop(wx.DropTarget):
@@ -64,7 +65,7 @@ class DroneView(Display):
]
def __init__(self, parent):
Display.__init__(self, parent, style=wx.LC_SINGLE_SEL | wx.BORDER_NONE)
Display.__init__(self, parent, style=wx.BORDER_NONE)
self.lastFitId = None
@@ -75,7 +76,7 @@ class DroneView(Display):
self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged)
self.mainFrame.Bind(ITEM_SELECTED, self.addItem)
self.Bind(wx.EVT_LEFT_DCLICK, self.removeItem)
self.Bind(wx.EVT_LEFT_DCLICK, self.onLeftDoubleClick)
self.Bind(wx.EVT_LEFT_DOWN, self.click)
self.Bind(wx.EVT_KEY_UP, self.kbEvent)
self.Bind(wx.EVT_MOTION, self.OnMouseMove)
@@ -101,7 +102,10 @@ class DroneView(Display):
self.hoveredRow = row
self.hoveredColumn = col
if row != -1 and col != -1 and col < len(self.DEFAULT_COLS):
mod = self.drones[self.GetItemData(row)]
try:
mod = self.drones[row]
except IndexError:
return
if self.DEFAULT_COLS[col] == "Miscellanea":
tooltip = self.activeColumns[col].getToolTip(mod)
if tooltip is not None:
@@ -116,17 +120,22 @@ class DroneView(Display):
def kbEvent(self, event):
keycode = event.GetKeyCode()
if keycode == wx.WXK_DELETE or keycode == wx.WXK_NUMPAD_DELETE:
row = self.GetFirstSelected()
if row != -1:
drone = self.drones[self.GetItemData(row)]
self.removeDroneStack(drone)
mstate = wx.GetMouseState()
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
self.unselectAll()
elif keycode == 65 and mstate.GetModifiers() == wx.MOD_CONTROL:
self.selectAll()
elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and mstate.GetModifiers() == wx.MOD_NONE:
drones = self.getSelectedDrones()
self.removeDroneStacks(drones)
event.Skip()
def startDrag(self, event):
row = event.GetIndex()
if row != -1:
self.unselectAll()
self.Select(row, True)
data = wx.TextDataObject()
dataStr = "drone:" + str(row)
data.SetText(dataStr)
@@ -146,19 +155,36 @@ class DroneView(Display):
"""
if data[0] == "drone":
srcRow = int(data[1])
dstRow, _ = self.HitTest((x, y))
if srcRow != -1 and dstRow != -1:
self._merge(srcRow, dstRow)
if srcRow != -1:
if wx.GetMouseState().GetModifiers() == wx.MOD_CONTROL:
try:
srcDrone = self.drones[srcRow]
except IndexError:
return
if srcDrone not in self.original:
return
self.mainFrame.command.Submit(cmd.GuiCloneLocalDroneCommand(
fitID=self.mainFrame.getActiveFit(),
position=self.original.index(srcDrone)))
else:
dstRow, _ = self.HitTest((x, y))
if dstRow != -1:
self._merge(srcRow, dstRow)
elif data[0] == "market":
wx.PostEvent(self.mainFrame, ItemSelected(itemID=int(data[1])))
def _merge(self, srcRow, dstRow):
fitID = self.mainFrame.getActiveFit()
fit = Fit.getInstance().getFit(fitID)
self.mainFrame.command.Submit(cmd.GuiMergeLocalDroneStacksCommand(
fitID=fitID,
srcPosition=fit.drones.index(self.drones[srcRow]),
dstPosition=fit.drones.index(self.drones[dstRow])))
try:
srcDrone = self.drones[srcRow]
dstDrone = self.drones[dstRow]
except IndexError:
return
if srcDrone in self.original and dstDrone in self.original:
srcPosition = self.original.index(srcDrone)
dstPosition = self.original.index(dstDrone)
self.mainFrame.command.Submit(cmd.GuiMergeLocalDroneStacksCommand(
fitID=fitID, srcPosition=srcPosition, dstPosition=dstPosition))
DRONE_ORDER = ('Light Scout Drones', 'Medium Scout Drones',
'Heavy Attack Drones', 'Sentry Drones', 'Combat Utility Drones',
@@ -199,7 +225,7 @@ class DroneView(Display):
if item != -1:
self.EnsureVisible(item)
self.deselectItems()
self.unselectAll()
self.update(self.drones)
event.Skip()
@@ -216,44 +242,94 @@ class DroneView(Display):
event.Skip()
return
if self.mainFrame.command.Submit(cmd.GuiAddLocalDroneCommand(fitID=fitID, itemID=event.itemID, amount=1)):
amount = droneStackLimit(fit, event.itemID) if wx.GetMouseState().GetModifiers() == wx.MOD_ALT else 1
if self.mainFrame.command.Submit(cmd.GuiAddLocalDroneCommand(fitID=fitID, itemID=event.itemID, amount=amount)):
self.mainFrame.additionsPane.select('Drones')
event.Skip()
def removeItem(self, event):
def onLeftDoubleClick(self, event):
row, _ = self.HitTest(event.Position)
if row != -1:
col = self.getColumn(event.Position)
if col != self.getColIndex(State):
drone = self.drones[self.GetItemData(row)]
self.removeDrone(drone)
try:
drone = self.drones[row]
except IndexError:
return
if wx.GetMouseState().GetModifiers() == wx.MOD_ALT:
self.removeDroneStacks([drone])
else:
self.removeDrone(drone)
def removeDrone(self, drone):
fitID = self.mainFrame.getActiveFit()
self.mainFrame.command.Submit(cmd.GuiRemoveLocalDroneCommand(fitID, self.original.index(drone), 1))
if drone in self.original:
position = self.original.index(drone)
self.mainFrame.command.Submit(cmd.GuiRemoveLocalDronesCommand(
fitID=fitID, positions=[position], amount=1))
def removeDroneStack(self, drone):
def removeDroneStacks(self, drones):
fitID = self.mainFrame.getActiveFit()
self.mainFrame.command.Submit(cmd.GuiRemoveLocalDroneCommand(fitID, self.original.index(drone), math.inf))
positions = []
for drone in drones:
if drone in self.original:
positions.append(self.original.index(drone))
self.mainFrame.command.Submit(cmd.GuiRemoveLocalDronesCommand(
fitID=fitID, positions=positions, amount=math.inf))
def click(self, event):
event.Skip()
row, _ = self.HitTest(event.Position)
if row != -1:
mainRow, _ = self.HitTest(event.Position)
if mainRow != -1:
col = self.getColumn(event.Position)
if col == self.getColIndex(State):
fitID = self.mainFrame.getActiveFit()
drone = self.drones[row]
self.mainFrame.command.Submit(cmd.GuiToggleLocalDroneStateCommand(fitID, self.original.index(drone)))
try:
mainDrone = self.drones[mainRow]
except IndexError:
return
if mainDrone in self.original:
mainPosition = self.original.index(mainDrone)
positions = []
for row in self.getSelectedRows():
try:
drone = self.drones[row]
except IndexError:
continue
if drone in self.original:
positions.append(self.original.index(drone))
if mainPosition not in positions:
positions = [mainPosition]
self.mainFrame.command.Submit(cmd.GuiToggleLocalDroneStatesCommand(
fitID=self.mainFrame.getActiveFit(),
mainPosition=mainPosition,
positions=positions))
return
event.Skip()
def spawnMenu(self, event):
sel = self.GetFirstSelected()
if sel != -1:
drone = self.drones[sel]
sMkt = Market.getInstance()
sourceContext = "droneItem"
itemContext = sMkt.getCategoryByItem(drone.item).name
menu = ContextMenu.getMenu((drone,), (sourceContext, itemContext))
clickedPos = self.getRowByAbs(event.Position)
mainDrone = None
if clickedPos != -1:
try:
drone = self.drones[clickedPos]
except IndexError:
pass
else:
if drone in self.original:
mainDrone = drone
selection = self.getSelectedDrones()
sourceContext = "droneItem"
itemContext = None if mainDrone is None else Market.getInstance().getCategoryByItem(mainDrone.item).name
menu = ContextMenu.getMenu(mainDrone, selection, (sourceContext, itemContext))
if menu:
self.PopupMenu(menu)
def getSelectedDrones(self):
drones = []
for row in self.getSelectedRows():
try:
drone = self.drones[row]
except IndexError:
continue
drones.append(drone)
return drones

View File

@@ -20,17 +20,18 @@
# noinspection PyPackageRequirements
import wx
import gui.globalEvents as GE
from gui.builtinMarketBrowser.events import ItemSelected, ITEM_SELECTED
import gui.mainFrame
import gui.display as d
from gui.builtinViewColumns.state import State
import gui.fitCommands as cmd
import gui.globalEvents as GE
import gui.mainFrame
from eos.const import FittingSlot
from gui.builtinMarketBrowser.events import ItemSelected, ITEM_SELECTED
from gui.builtinViewColumns.state import State
from gui.contextMenu import ContextMenu
from gui.fitCommands.helpers import getSimilarFighters
from gui.utils.staticHelpers import DragDropHelper
from service.fit import Fit
from service.market import Market
import gui.fitCommands as cmd
class FighterViewDrop(wx.DropTarget):
@@ -50,6 +51,7 @@ class FighterViewDrop(wx.DropTarget):
class FighterView(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, style=wx.TAB_TRAVERSAL)
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
@@ -115,6 +117,7 @@ class FighterView(wx.Panel):
class FighterDisplay(d.Display):
DEFAULT_COLS = ["State",
# "Base Icon",
"Base Name",
@@ -127,7 +130,7 @@ class FighterDisplay(d.Display):
]
def __init__(self, parent):
d.Display.__init__(self, parent, style=wx.LC_SINGLE_SEL | wx.BORDER_NONE)
d.Display.__init__(self, parent, style=wx.BORDER_NONE)
self.lastFitId = None
@@ -136,7 +139,7 @@ class FighterDisplay(d.Display):
self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged)
self.mainFrame.Bind(ITEM_SELECTED, self.addItem)
self.Bind(wx.EVT_LEFT_DCLICK, self.removeItem)
self.Bind(wx.EVT_LEFT_DCLICK, self.onLeftDoubleClick)
self.Bind(wx.EVT_LEFT_DOWN, self.click)
self.Bind(wx.EVT_KEY_UP, self.kbEvent)
self.Bind(wx.EVT_MOTION, self.OnMouseMove)
@@ -162,7 +165,10 @@ class FighterDisplay(d.Display):
self.hoveredRow = row
self.hoveredColumn = col
if row != -1 and col != -1 and col < len(self.DEFAULT_COLS):
mod = self.fighters[self.GetItemData(row)]
try:
mod = self.fighters[row]
except IndexError:
return
if self.DEFAULT_COLS[col] == "Miscellanea":
tooltip = self.activeColumns[col].getToolTip(mod)
if tooltip is not None:
@@ -177,17 +183,22 @@ class FighterDisplay(d.Display):
def kbEvent(self, event):
keycode = event.GetKeyCode()
if keycode == wx.WXK_DELETE or keycode == wx.WXK_NUMPAD_DELETE:
row = self.GetFirstSelected()
if row != -1:
fighter = self.fighters[self.GetItemData(row)]
self.removeFighter(fighter)
mstate = wx.GetMouseState()
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
self.unselectAll()
elif keycode == 65 and mstate.GetModifiers() == wx.MOD_CONTROL:
self.selectAll()
elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and mstate.GetModifiers() == wx.MOD_NONE:
fighters = self.getSelectedFighters()
self.removeFighters(fighters)
event.Skip()
def startDrag(self, event):
row = event.GetIndex()
if row != -1:
self.unselectAll()
self.Select(row, True)
data = wx.TextDataObject()
dataStr = "fighter:" + str(row)
data.SetText(dataStr)
@@ -217,12 +228,18 @@ class FighterDisplay(d.Display):
def _merge(src, dst):
return
FIGHTER_ORDER = ('Heavy Fighter', 'Light Fighter', 'Support Fighter')
FIGHTER_ORDER = ('Light Fighter', 'Heavy Fighter', 'Support Fighter')
def fighterKey(self, fighter):
sMkt = Market.getInstance()
groupName = sMkt.getGroupByItem(fighter.item).name
return (self.FIGHTER_ORDER.index(groupName), fighter.item.name)
groupName = Market.getInstance().getGroupByItem(fighter.item).name
orderPos = self.FIGHTER_ORDER.index(groupName)
# Sort support fighters by name, ignore their abilities
if groupName == 'Support Fighter':
abilityEffectIDs = ()
# Group up fighters from various roles
else:
abilityEffectIDs = sorted(a.effectID for a in fighter.abilities)
return orderPos, abilityEffectIDs, fighter.item.name
def fitChanged(self, event):
sFit = Fit.getInstance()
@@ -251,7 +268,7 @@ class FighterDisplay(d.Display):
if item != -1:
self.EnsureVisible(item)
self.deselectItems()
self.unselectAll()
self.update(self.fighters)
event.Skip()
@@ -268,35 +285,87 @@ class FighterDisplay(d.Display):
event.Skip()
def removeItem(self, event):
def onLeftDoubleClick(self, event):
row, _ = self.HitTest(event.Position)
if row != -1:
col = self.getColumn(event.Position)
if col != self.getColIndex(State):
fighter = self.fighters[self.GetItemData(row)]
self.removeFighter(fighter)
mstate = wx.GetMouseState()
try:
fighter = self.fighters[row]
except IndexError:
return
if mstate.GetModifiers() == wx.MOD_ALT:
fighters = getSimilarFighters(self.original, fighter)
else:
fighters = [fighter]
self.removeFighters(fighters)
def removeFighter(self, fighter):
def removeFighters(self, fighters):
fitID = self.mainFrame.getActiveFit()
self.mainFrame.command.Submit(cmd.GuiRemoveLocalFighterCommand(fitID, self.original.index(fighter)))
positions = []
for fighter in fighters:
if fighter in self.original:
positions.append(self.original.index(fighter))
self.mainFrame.command.Submit(cmd.GuiRemoveLocalFightersCommand(fitID=fitID, positions=positions))
def click(self, event):
event.Skip()
row, _ = self.HitTest(event.Position)
if row != -1:
mainRow, _ = self.HitTest(event.Position)
if mainRow != -1:
col = self.getColumn(event.Position)
if col == self.getColIndex(State):
fitID = self.mainFrame.getActiveFit()
fighter = self.fighters[row]
self.mainFrame.command.Submit(cmd.GuiToggleLocalFighterStateCommand(fitID, self.original.index(fighter)))
try:
mainFighter = self.fighters[mainRow]
except IndexError:
return
if mainFighter in self.original:
mainPosition = self.original.index(mainFighter)
positions = []
if event.GetModifiers() == wx.MOD_ALT:
for fighter in getSimilarFighters(self.original, mainFighter):
positions.append(self.original.index(fighter))
else:
for row in self.getSelectedRows():
try:
fighter = self.fighters[row]
except IndexError:
continue
if fighter in self.original:
positions.append(self.original.index(fighter))
if mainPosition not in positions:
positions = [mainPosition]
self.mainFrame.command.Submit(cmd.GuiToggleLocalFighterStatesCommand(
fitID=fitID,
mainPosition=mainPosition,
positions=positions))
return
event.Skip()
def spawnMenu(self, event):
sel = self.GetFirstSelected()
if sel != -1:
fighter = self.fighters[sel]
sMkt = Market.getInstance()
sourceContext = "fighterItem"
itemContext = sMkt.getCategoryByItem(fighter.item).name
menu = ContextMenu.getMenu((fighter,), (sourceContext, itemContext))
selection = self.getSelectedFighters()
clickedPos = self.getRowByAbs(event.Position)
mainFighter = None
if clickedPos != -1:
try:
fighter = self.fighters[clickedPos]
except IndexError:
pass
else:
if fighter in self.original:
mainFighter = fighter
sourceContext = "fighterItem"
itemContext = None if mainFighter is None else Market.getInstance().getCategoryByItem(mainFighter.item).name
menu = ContextMenu.getMenu(mainFighter, selection, (sourceContext, itemContext))
if menu:
self.PopupMenu(menu)
def getSelectedFighters(self):
fighters = []
for row in self.getSelectedRows():
try:
fighter = self.fighters[row]
except IndexError:
continue
fighters.append(fighter)
return fighters

View File

@@ -19,17 +19,18 @@
# noinspection PyPackageRequirements
import wx
import gui.display as d
from gui.builtinMarketBrowser.events import ITEM_SELECTED
import gui.mainFrame
from gui.builtinViewColumns.state import State
from gui.utils.staticHelpers import DragDropHelper
from gui.contextMenu import ContextMenu
import gui.fitCommands as cmd
import gui.globalEvents as GE
import gui.mainFrame
from eos.const import ImplantLocation
from gui.builtinMarketBrowser.events import ITEM_SELECTED
from gui.builtinViewColumns.state import State
from gui.contextMenu import ContextMenu
from gui.utils.staticHelpers import DragDropHelper
from service.fit import Fit
from service.market import Market
import gui.fitCommands as cmd
class ImplantViewDrop(wx.DropTarget):
@@ -49,6 +50,7 @@ class ImplantViewDrop(wx.DropTarget):
class ImplantView(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, style=wx.TAB_TRAVERSAL)
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
@@ -99,6 +101,7 @@ class ImplantView(wx.Panel):
class ImplantDisplay(d.Display):
DEFAULT_COLS = [
"State",
"attr:implantness",
@@ -108,13 +111,13 @@ class ImplantDisplay(d.Display):
]
def __init__(self, parent):
d.Display.__init__(self, parent, style=wx.LC_SINGLE_SEL | wx.BORDER_NONE)
d.Display.__init__(self, parent, style=wx.BORDER_NONE)
self.lastFitId = None
self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged)
self.mainFrame.Bind(ITEM_SELECTED, self.addItem)
self.Bind(wx.EVT_LEFT_DCLICK, self.removeItem)
self.Bind(wx.EVT_LEFT_DCLICK, self.onLeftDoubleClick)
self.Bind(wx.EVT_LEFT_DOWN, self.click)
self.Bind(wx.EVT_KEY_UP, self.kbEvent)
self.SetDropTarget(ImplantViewDrop(self.handleListDrag))
@@ -132,15 +135,20 @@ class ImplantDisplay(d.Display):
"""
if data[0] == "market":
if self.mainFrame.command.Submit(cmd.GuiAddImplantCommand(self.mainFrame.getActiveFit(), int(data[1]))):
if self.mainFrame.command.Submit(cmd.GuiAddImplantCommand(
fitID=self.mainFrame.getActiveFit(), itemID=int(data[1]))):
self.mainFrame.additionsPane.select("Implants")
def kbEvent(self, event):
keycode = event.GetKeyCode()
if keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE):
row = self.GetFirstSelected()
if row != -1:
self.removeImplant(self.implants[self.GetItemData(row)])
mstate = wx.GetMouseState()
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
self.unselectAll()
elif keycode == 65 and mstate.GetModifiers() == wx.MOD_CONTROL:
self.selectAll()
elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and mstate.GetModifiers() == wx.MOD_NONE:
implants = self.getSelectedImplants()
self.removeImplants(implants)
event.Skip()
def fitChanged(self, event):
@@ -169,7 +177,7 @@ class ImplantDisplay(d.Display):
if item != -1:
self.EnsureVisible(item)
self.deselectItems()
self.unselectAll()
self.update(self.implants)
event.Skip()
@@ -187,67 +195,94 @@ class ImplantDisplay(d.Display):
event.Skip()
return
self.mainFrame.command.Submit(cmd.GuiAddImplantCommand(fitID, event.itemID))
self.mainFrame.command.Submit(cmd.GuiAddImplantCommand(
fitID=fitID, itemID=event.itemID))
# Select in any case - as we might've added implant which has been there already and command failed
self.mainFrame.additionsPane.select('Implants')
event.Skip()
def removeItem(self, event):
# Character implants can't be changed here...
if self.Parent.source == ImplantLocation.CHARACTER:
return
def onLeftDoubleClick(self, event):
row, _ = self.HitTest(event.Position)
if row != -1:
col = self.getColumn(event.Position)
if col != self.getColIndex(State):
self.removeImplant(self.implants[self.GetItemData(row)])
try:
implant = self.implants[row]
except IndexError:
return
self.removeImplants([implant])
def removeImplant(self, implant):
def removeImplants(self, implants):
fitID = self.mainFrame.getActiveFit()
sFit = Fit.getInstance()
fit = sFit.getFit(fitID)
if fit.implantLocation == ImplantLocation.FIT:
self.mainFrame.command.Submit(cmd.GuiRemoveImplantCommand(fitID, self.original.index(implant)))
fit = Fit.getInstance().getFit(fitID)
if fit.implantLocation != ImplantLocation.FIT:
return
positions = []
for implant in implants:
if implant in self.original:
positions.append(self.original.index(implant))
self.mainFrame.command.Submit(cmd.GuiRemoveImplantsCommand(fitID=fitID, positions=positions))
def click(self, event):
fitID = self.mainFrame.getActiveFit()
fit = Fit.getInstance().getFit(fitID)
if fit.implantLocation == ImplantLocation.FIT:
mainRow, _ = self.HitTest(event.Position)
if mainRow != -1:
col = self.getColumn(event.Position)
if col == self.getColIndex(State):
fitID = self.mainFrame.getActiveFit()
try:
mainImplant = self.implants[mainRow]
except IndexError:
return
if mainImplant in self.original:
mainPosition = self.original.index(mainImplant)
positions = []
for row in self.getSelectedRows():
try:
implant = self.implants[row]
except IndexError:
continue
if implant in self.original:
positions.append(self.original.index(implant))
if mainPosition not in positions:
positions = [mainPosition]
self.mainFrame.command.Submit(cmd.GuiToggleImplantStatesCommand(
fitID=fitID,
mainPosition=mainPosition,
positions=positions))
return
event.Skip()
# Character implants can't be changed here...
if self.Parent.source == ImplantLocation.CHARACTER:
return
row, _ = self.HitTest(event.Position)
if row != -1:
col = self.getColumn(event.Position)
if col == self.getColIndex(State):
fitID = self.mainFrame.getActiveFit()
implant = self.implants[self.GetItemData(row)]
self.mainFrame.command.Submit(cmd.GuiToggleImplantStateCommand(fitID=fitID, position=self.original.index(implant)))
def spawnMenu(self, event):
sel = self.GetFirstSelected()
menu = None
sFit = Fit.getInstance()
fit = sFit.getFit(self.mainFrame.getActiveFit())
if not fit:
return
if sel != -1:
implant = self.implants[sel]
sMkt = Market.getInstance()
sourceContext = "implantItem" if fit.implantSource == ImplantLocation.FIT else "implantItemChar"
itemContext = sMkt.getCategoryByItem(implant.item).name
fullContext = ((sourceContext, itemContext), ("implantView",))
menu = ContextMenu.getMenu((implant,), *fullContext)
elif sel == -1 and fit.implantSource == ImplantLocation.FIT:
fitID = self.mainFrame.getActiveFit()
if fitID is None:
return
context = ("implantView",)
menu = ContextMenu.getMenu([], context)
if menu is not None:
selection = self.getSelectedImplants()
clickedPos = self.getRowByAbs(event.Position)
mainImplant = None
if clickedPos != -1:
try:
implant = self.implants[clickedPos]
except IndexError:
pass
else:
if implant in self.original:
mainImplant = implant
fitID = self.mainFrame.getActiveFit()
fit = Fit.getInstance().getFit(fitID)
sourceContext1 = "implantItem" if fit.implantSource == ImplantLocation.FIT else "implantItemChar"
sourceContext2 = "implantView" if fit.implantSource == ImplantLocation.FIT else "implantViewChar"
itemContext = None if mainImplant is None else Market.getInstance().getCategoryByItem(mainImplant.item).name
menu = ContextMenu.getMenu(mainImplant, selection, (sourceContext1, itemContext), (sourceContext2, itemContext))
if menu:
self.PopupMenu(menu)
def getSelectedImplants(self):
implants = []
for row in self.getSelectedRows():
try:
implant = self.implants[row]
except IndexError:
continue
implants.append(implant)
return implants

View File

@@ -25,17 +25,19 @@ from logbook import Logger
import gui.builtinAdditionPanes.droneView
import gui.display as d
import gui.fitCommands as cmd
import gui.globalEvents as GE
from eos.saveddata.drone import Drone as es_Drone
from eos.saveddata.fighter import Fighter as es_Fighter
from eos.saveddata.fit import Fit as es_Fit
from eos.saveddata.module import Module as es_Module
from eos.saveddata.drone import Drone as EosDrone
from eos.saveddata.fighter import Fighter as EosFighter
from eos.saveddata.fit import Fit as EosFit
from eos.saveddata.module import Module as EosModule
from gui.builtinViewColumns.state import State
from gui.contextMenu import ContextMenu
from gui.fitCommands.helpers import getSimilarFighters, getSimilarModPositions
from gui.utils.staticHelpers import DragDropHelper
from service.fit import Fit
from service.market import Market
import gui.fitCommands as cmd
pyfalog = Logger(__name__)
@@ -68,28 +70,27 @@ class ProjectedViewDrop(wx.DropTarget):
class ProjectedView(d.Display):
DEFAULT_COLS = ["State",
"Ammo Icon",
"Base Icon",
"Base Name",
"Ammo"]
DEFAULT_COLS = ['State',
'Ammo Icon',
'Base Icon',
'Base Name',
'Ammo']
def __init__(self, parent):
d.Display.__init__(self, parent, style=wx.LC_SINGLE_SEL | wx.BORDER_NONE)
d.Display.__init__(self, parent, style=wx.BORDER_NONE)
self.lastFitId = None
self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged)
self.Bind(wx.EVT_LEFT_DOWN, self.click)
self.Bind(wx.EVT_RIGHT_DOWN, self.click)
self.Bind(wx.EVT_LEFT_DCLICK, self.remove)
self.Bind(wx.EVT_LEFT_DCLICK, self.onLeftDoubleClick)
self.Bind(wx.EVT_KEY_UP, self.kbEvent)
self.droneView = gui.builtinAdditionPanes.droneView.DroneView
self.Bind(wx.EVT_CONTEXT_MENU, self.spawnMenu)
self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.startDrag)
self.SetDropTarget(ProjectedViewDrop(self.handleListDrag))
def handleListDrag(self, x, y, data):
@@ -109,7 +110,7 @@ class ProjectedView(d.Display):
fitID=fitID, itemID=fit.modules[int(data[1])].itemID))
elif data[0] == 'market':
itemID = int(data[1])
category = Market.getInstance().getItem(itemID, eager=("group.category")).category.name
category = Market.getInstance().getItem(itemID, eager=('group.category')).category.name
if category == 'Module':
self.mainFrame.command.Submit(cmd.GuiAddProjectedModuleCommand(fitID=fitID, itemID=itemID))
elif category == 'Drone':
@@ -119,42 +120,25 @@ class ProjectedView(d.Display):
def kbEvent(self, event):
keycode = event.GetKeyCode()
if keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE):
row = self.GetFirstSelected()
if row != -1:
fitID = self.mainFrame.getActiveFit()
thing = self.get(row)
if isinstance(thing, es_Fit):
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedFitCommand(
fitID=fitID, projectedFitID=thing.ID))
elif isinstance(thing, es_Module):
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedModuleCommand(
fitID=fitID, position=Fit.getInstance().getFit(fitID).projectedModules.index(thing)))
elif isinstance(thing, es_Drone):
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedDroneCommand(
fitID=fitID, itemID=thing.itemID, amount=math.inf))
elif isinstance(thing, es_Fighter):
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedFighterCommand(
fitID=fitID, position=Fit.getInstance().getFit(fitID).projectedFighters.index(thing)))
mstate = wx.GetMouseState()
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
self.unselectAll()
elif keycode == 65 and mstate.GetModifiers() == wx.MOD_CONTROL:
self.selectAll()
elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and mstate.GetModifiers() == wx.MOD_NONE:
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedItemsCommand(
fitID=self.mainFrame.getActiveFit(),
items=self.getSelectedProjectors(),
amount=math.inf))
event.Skip()
def handleDrag(self, type, fitID):
# Those are drags coming from pyfa sources, NOT builtin wx drags
if type == "fit":
if type == 'fit':
activeFit = self.mainFrame.getActiveFit()
if activeFit:
self.mainFrame.command.Submit(cmd.GuiAddProjectedFitCommand(fitID=activeFit, projectedFitID=fitID))
def startDrag(self, event):
row = event.GetIndex()
if row != -1 and isinstance(self.get(row), es_Drone):
data = wx.TextDataObject()
dataStr = "projected:" + str(self.GetItemData(row))
data.SetText(dataStr)
dropSource = wx.DropSource(self)
dropSource.SetData(data)
DragDropHelper.data = dataStr
dropSource.DoDragDrop()
self.mainFrame.command.Submit(cmd.GuiAddProjectedFitCommand(
fitID=activeFit, projectedFitID=fitID, amount=1))
@staticmethod
def moduleSort(module):
@@ -179,7 +163,7 @@ class ProjectedView(d.Display):
def fitChanged(self, event):
sFit = Fit.getInstance()
fit = sFit.getFit(event.fitID)
# pyfalog.debug("ProjectedView::fitChanged: {}", repr(fit))
# pyfalog.debug('ProjectedView::fitChanged: {}', repr(fit))
self.Parent.Parent.DisablePage(self, not fit or fit.isStructure)
@@ -192,10 +176,13 @@ class ProjectedView(d.Display):
stuff = []
if fit is not None:
# pyfalog.debug(" Collecting list of stuff to display in ProjectedView")
self.originalFits = fit.projectedFits
self.fits = fit.projectedFits[:]
self.originalModules = fit.projectedModules
self.modules = fit.projectedModules[:]
self.originalDrones = fit.projectedDrones
self.drones = fit.projectedDrones[:]
self.originalFighters = fit.projectedFighters
self.fighters = fit.projectedFighters[:]
self.fits.sort(key=self.fitSort)
@@ -216,10 +203,10 @@ class ProjectedView(d.Display):
if item != -1:
self.EnsureVisible(item)
self.deselectItems()
self.unselectAll()
if not stuff:
stuff = [DummyEntry("Drag an item or fit, or use right-click menu for wormhole effects")]
stuff = [DummyEntry('Drag an item or fit, or use right-click menu for wormhole effects')]
self.update(stuff)
@@ -238,101 +225,140 @@ class ProjectedView(d.Display):
return None
if row < numFits:
stuff = self.fits[row]
fit = self.fits[row]
if fit in self.originalFits:
return fit
elif row - numFits < numMods:
stuff = self.modules[row - numFits]
mod = self.modules[row - numFits]
if mod in self.originalModules:
return mod
elif row - numFits - numMods < numDrones:
stuff = self.drones[row - numFits - numMods]
drone = self.drones[row - numFits - numMods]
if drone in self.originalDrones:
return drone
else:
stuff = self.fighters[row - numFits - numMods - numDrones]
return stuff
fighter = self.fighters[row - numFits - numMods - numDrones]
if fighter in self.originalFighters:
return fighter
return None
def click(self, event):
event.Skip()
row, _ = self.HitTest(event.Position)
if row != -1:
mainRow, _ = self.HitTest(event.Position)
if mainRow != -1:
col = self.getColumn(event.Position)
if col == self.getColIndex(State):
mainItem = self.get(mainRow)
if mainItem is None:
return
selection = self.getSelectedProjectors()
if mainItem not in selection:
selection = [mainItem]
modPressed = wx.GetMouseState().GetModifiers() == wx.MOD_ALT
fitID = self.mainFrame.getActiveFit()
thing = self.get(row)
button = event.GetButton()
if isinstance(thing, es_Fit) and button != 3:
self.mainFrame.command.Submit(cmd.GuiToggleProjectedFitStateCommand(
fitID=fitID, projectedFitID=thing.ID))
elif isinstance(thing, es_Module):
self.mainFrame.command.Submit(cmd.GuiChangeProjectedModuleStateCommand(
fitID=fitID,
position=Fit.getInstance().getFit(fitID).projectedModules.index(thing),
click='right' if button == 3 else 'left'))
elif isinstance(thing, es_Drone) and button != 3:
self.mainFrame.command.Submit(cmd.GuiToggleProjectedDroneStateCommand(
fitID=fitID, itemID=thing.itemID))
elif isinstance(thing, es_Fighter) and button != 3:
self.mainFrame.command.Submit(cmd.GuiToggleProjectedFighterStateCommand(
fitID=fitID, position=Fit.getInstance().getFit(fitID).projectedFighters.index(thing)))
if isinstance(mainItem, EosModule) and modPressed:
fit = Fit.getInstance().getFit(fitID)
positions = getSimilarModPositions(fit.projectedModules, mainItem)
selection = [fit.projectedModules[p] for p in positions]
elif isinstance(mainItem, EosFighter) and modPressed:
fit = Fit.getInstance().getFit(fitID)
selection = getSimilarFighters(fit.projectedFighters, mainItem)
self.mainFrame.command.Submit(cmd.GuiChangeProjectedItemStatesCommand(
fitID=fitID,
mainItem=mainItem,
items=selection,
click='right' if event.GetButton() == 3 else 'left'))
return
event.Skip()
def spawnMenu(self, event):
fitID = self.mainFrame.getActiveFit()
if fitID is None:
return
if self.getColumn(self.ScreenToClient(event.Position)) == self.getColIndex(State):
if self.getColumn(self.screenToClientFixed(event.Position)) == self.getColIndex(State):
return
sel = self.GetFirstSelected()
context = ()
item = self.get(sel)
clickedPos = self.getRowByAbs(event.Position)
mainItem = self.get(clickedPos)
if item is not None:
contexts = []
if mainItem is not None:
sMkt = Market.getInstance()
if isinstance(item, es_Drone):
srcContext = "projectedDrone"
itemContext = sMkt.getCategoryByItem(item.item).name
context = ((srcContext, itemContext),)
elif isinstance(item, es_Fighter):
srcContext = "projectedFighter"
itemContext = sMkt.getCategoryByItem(item.item).name
context = ((srcContext, itemContext),)
elif isinstance(item, es_Module):
modSrcContext = "projectedModule"
modItemContext = sMkt.getCategoryByItem(item.item).name
if isinstance(mainItem, EosModule):
modSrcContext = 'projectedModule'
modItemContext = 'Projected Item'
modFullContext = (modSrcContext, modItemContext)
if item.charge is not None:
chgSrcContext = "projectedCharge"
chgItemContext = sMkt.getCategoryByItem(item.charge).name
chgFullContext = (chgSrcContext, chgItemContext)
context = (modFullContext, chgFullContext)
else:
context = (modFullContext,)
contexts.append(modFullContext)
if mainItem.charge is not None:
chargeSrcContext = 'projectedCharge'
chargeItemContext = sMkt.getCategoryByItem(mainItem.charge).name
chargeFullContext = (chargeSrcContext, chargeItemContext)
contexts.append(chargeFullContext)
elif isinstance(mainItem, EosDrone):
srcContext = 'projectedDrone'
itemContext = 'Projected Item'
droneFullContext = (srcContext, itemContext)
contexts.append(droneFullContext)
elif isinstance(mainItem, EosFighter):
srcContext = 'projectedFighter'
itemContext = 'Projected Item'
fighterFullContext = (srcContext, itemContext)
contexts.append(fighterFullContext)
else:
fitSrcContext = "projectedFit"
fitItemContext = item.name
context = ((fitSrcContext, fitItemContext),)
context += (("projected",),)
menu = ContextMenu.getMenu((item,) if item is not None else [], *context)
fitSrcContext = 'projectedFit'
fitItemContext = 'Projected Item'
fitFullContext = (fitSrcContext, fitItemContext)
contexts.append(fitFullContext)
contexts.append(('projected',))
selection = self.getSelectedProjectors()
menu = ContextMenu.getMenu(mainItem, selection, *contexts)
if menu is not None:
self.PopupMenu(menu)
def remove(self, event):
def onLeftDoubleClick(self, event):
row, _ = self.HitTest(event.Position)
if row != -1:
col = self.getColumn(event.Position)
if col != self.getColIndex(State):
mainItem = self.get(row)
if mainItem is None:
return
fitID = self.mainFrame.getActiveFit()
thing = self.get(row)
if isinstance(thing, es_Fit):
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedFitCommand(
fitID=fitID, projectedFitID=thing.ID))
elif isinstance(thing, es_Module):
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedModuleCommand(
fitID=fitID, position=Fit.getInstance().getFit(fitID).projectedModules.index(thing)))
elif isinstance(thing, es_Drone):
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedDroneCommand(
fitID=fitID, itemID=thing.itemID, amount=1))
elif isinstance(thing, es_Fighter):
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedFighterCommand(
fitID=fitID, position=Fit.getInstance().getFit(fitID).projectedFighters.index(thing)))
modPressed = wx.GetMouseState().GetModifiers() == wx.MOD_ALT
if isinstance(mainItem, EosFit):
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedItemsCommand(
fitID=fitID, items=[mainItem], amount=math.inf if modPressed else 1))
elif isinstance(mainItem, EosModule):
if modPressed:
fit = Fit.getInstance().getFit(fitID)
positions = getSimilarModPositions(fit.projectedModules, mainItem)
items = [fit.projectedModules[p] for p in positions]
else:
items = [mainItem]
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedItemsCommand(
fitID=fitID, items=items, amount=1))
elif isinstance(mainItem, EosDrone):
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedItemsCommand(
fitID=fitID, items=[mainItem], amount=math.inf if modPressed else 1))
elif isinstance(mainItem, EosFighter):
if modPressed:
fit = Fit.getInstance().getFit(fitID)
items = getSimilarFighters(fit.projectedFighters, mainItem)
else:
items = [mainItem]
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedItemsCommand(
fitID=fitID, items=items, amount=1))
else:
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedItemsCommand(
fitID=fitID, items=[mainItem], amount=math.inf if modPressed else 1))
def getSelectedProjectors(self):
projectors = []
for row in self.getSelectedRows():
projector = self.get(row)
if projector is None:
continue
projectors.append(projector)
return projectors

View File

@@ -15,16 +15,18 @@ from gui.builtinContextMenus import ( # noqa: E402,F401
# Item info
itemStats,
itemMarketJump,
fitSystemSecurity, # Not really an item info but want to keep it here
shipJump,
# Generic item manipulations
itemRemove,
moduleMutations,
itemAmountChange,
droneSplitStack,
itemVariationChange,
moduleMutations,
moduleFill,
skillAffectors,
# Market stuff
itemFill,
droneAddStack,
cargoAdd,
cargoAddAmmo,

View File

@@ -1,42 +1,44 @@
from gui.contextMenu import ContextMenu
import gui.mainFrame
# noinspection PyPackageRequirements
import wx
import gui.globalEvents as GE
import gui.mainFrame
from gui.contextMenu import ContextMenuSingle
from service.fit import Fit
from service.settings import ContextMenuSettings
class AmmoToDmgPattern(ContextMenu):
class AmmoToDmgPattern(ContextMenuSingle):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
def display(self, srcContext, mainItem):
if not self.settings.get('ammoPattern'):
return False
if srcContext not in ("marketItemGroup", "marketItemMisc") or self.mainFrame.getActiveFit() is None:
return False
item = selection[0]
if mainItem is None:
return False
for attr in ("emDamage", "thermalDamage", "explosiveDamage", "kineticDamage"):
if item.getAttribute(attr) is not None:
if mainItem.getAttribute(attr) is not None:
return True
return False
def getText(self, itmContext, selection):
return "Set {0} as Damage Pattern".format(itmContext if itmContext is not None else "Item")
def getText(self, itmContext, mainItem):
return "Set {} as Damage Pattern".format(itmContext if itmContext is not None else "Item")
def activate(self, fullContext, selection, i):
item = selection[0]
fit = self.mainFrame.getActiveFit()
sFit = Fit.getInstance()
sFit.setAsPattern(fit, item)
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fit))
def activate(self, fullContext, mainItem, i):
fitID = self.mainFrame.getActiveFit()
Fit.getInstance().setAsPattern(fitID, mainItem)
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
def getBitmap(self, context, selection):
def getBitmap(self, context, mainItem):
return None

View File

@@ -3,24 +3,25 @@ import wx
import gui.mainFrame
from gui import fitCommands as cmd
from gui.contextMenu import ContextMenu
from gui.contextMenu import ContextMenuSingle
from service.fit import Fit
from service.settings import ContextMenuSettings
class BoosterSideEffect(ContextMenu):
class BoosterSideEffects(ContextMenuSingle):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
# if not self.settings.get('fighterAbilities'):
# return False
def display(self, srcContext, mainItem):
if self.mainFrame.getActiveFit() is None or srcContext not in "boosterItem":
return False
self.booster = selection[0]
if mainItem is None:
return False
self.booster = mainItem
for effect in self.booster.sideEffects:
if effect.effect.isImplemented:
@@ -28,19 +29,19 @@ class BoosterSideEffect(ContextMenu):
return False
def getText(self, itmContext, selection):
def getText(self, itmContext, mainItem):
return "Side Effects"
def addEffect(self, menu, ability):
label = ability.name
id = ContextMenu.nextID()
id = ContextMenuSingle.nextID()
self.effectIds[id] = ability
menuItem = wx.MenuItem(menu, id, label, kind=wx.ITEM_CHECK)
menu.Bind(wx.EVT_MENU, self.handleMode, menuItem)
return menuItem
def getSubMenu(self, context, selection, rootMenu, i, pitem):
def getSubMenu(self, context, mainItem, rootMenu, i, pitem):
msw = True if "wxMSW" in wx.PlatformInfo else False
self.context = context
self.effectIds = {}
@@ -58,14 +59,17 @@ class BoosterSideEffect(ContextMenu):
def handleMode(self, event):
effect = self.effectIds[event.Id]
if effect is False or effect not in self.booster.sideEffects:
booster = self.booster
if effect is False or effect not in booster.sideEffects:
event.Skip()
return
fitID = self.mainFrame.getActiveFit()
fit = Fit.getInstance().getFit(fitID)
index = fit.boosters.index(self.booster)
self.mainFrame.command.Submit(cmd.GuiToggleBoosterSideEffectStateCommand(fitID, index, effect.effectID))
if booster in fit.boosters:
index = fit.boosters.index(booster)
self.mainFrame.command.Submit(cmd.GuiToggleBoosterSideEffectStateCommand(
fitID=fitID, position=index, effectID=effect.effectID))
BoosterSideEffect.register()
BoosterSideEffects.register()

View File

@@ -1,19 +1,23 @@
import gui.fitCommands as cmd
import gui.mainFrame
from gui.contextMenu import ContextMenu
from gui.contextMenu import ContextMenuSingle
from service.fit import Fit
from service.settings import ContextMenuSettings
class AddToCargo(ContextMenu):
class AddToCargo(ContextMenuSingle):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
def display(self, srcContext, mainItem):
if srcContext not in ("marketItemGroup", "marketItemMisc"):
return False
if mainItem is None:
return False
sFit = Fit.getInstance()
fitID = self.mainFrame.getActiveFit()
fit = sFit.getFit(fitID)
@@ -23,16 +27,15 @@ class AddToCargo(ContextMenu):
return True
def getText(self, itmContext, selection):
def getText(self, itmContext, mainItem):
return "Add {} to Cargo".format(itmContext)
def activate(self, fullContext, selection, i):
def activate(self, fullContext, mainItem, i):
fitID = self.mainFrame.getActiveFit()
typeID = int(selection[0].ID)
self.mainFrame.command.Submit(cmd.GuiAddCargoCommand(fitID, typeID, 1))
self.mainFrame.additionsPane.select("Cargo")
typeID = int(mainItem.ID)
command = cmd.GuiAddCargoCommand(fitID=fitID, itemID=typeID, amount=1)
if self.mainFrame.command.Submit(command):
self.mainFrame.additionsPane.select("Cargo", focus=False)
AddToCargo.register()

View File

@@ -1,32 +1,36 @@
import gui.fitCommands as cmd
import gui.mainFrame
from gui.contextMenu import ContextMenu
from gui.contextMenu import ContextMenuSingle
from service.settings import ContextMenuSettings
class AddToCargoAmmo(ContextMenu):
class AddToCargoAmmo(ContextMenuSingle):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
def display(self, srcContext, mainItem):
if srcContext not in ("marketItemGroup", "marketItemMisc") or self.mainFrame.getActiveFit() is None:
return False
for selected_item in selection:
if selected_item.category.ID in (
8, # Charge
):
return True
if mainItem is None:
return False
def getText(self, itmContext, selection):
if mainItem.category.ID != 8:
return False
return True
def getText(self, itmContext, mainItem):
return "Add {0} to Cargo (x1000)".format(itmContext)
def activate(self, fullContext, selection, i):
def activate(self, fullContext, mainItem, i):
fitID = self.mainFrame.getActiveFit()
typeID = int(selection[0].ID)
self.mainFrame.command.Submit(cmd.GuiAddCargoCommand(fitID, typeID, 1000))
self.mainFrame.additionsPane.select("Cargo")
typeID = int(mainItem.ID)
command = cmd.GuiAddCargoCommand(fitID=fitID, itemID=typeID, amount=1000)
if self.mainFrame.command.Submit(command):
self.mainFrame.additionsPane.select("Cargo", focus=False)
AddToCargoAmmo.register()

View File

@@ -3,13 +3,14 @@ import wx
import gui.fitCommands as cmd
import gui.mainFrame
from gui.contextMenu import ContextMenu
from gui.contextMenu import ContextMenuUnconditional
from service.fit import Fit
from service.market import Market
from service.settings import ContextMenuSettings
class AddCommandFit(ContextMenu):
class AddCommandFit(ContextMenuUnconditional):
# Get list of items that define a command fit
sMkt = Market.getInstance()
grp = sMkt.getGroup(1770) # Command burst group
@@ -41,24 +42,24 @@ class AddCommandFit(ContextMenu):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
def display(self, srcContext):
if self.mainFrame.getActiveFit() is None or len(self.__class__.commandFits) == 0 or srcContext != "commandView":
return False
return True
def getText(self, itmContext, selection):
def getText(self, itmContext):
return "Command Fits"
def addFit(self, menu, fit, includeShip=False):
label = fit.name if not includeShip else "({}) {}".format(fit.ship.item.name, fit.name)
id = ContextMenu.nextID()
id = ContextMenuUnconditional.nextID()
self.fitMenuItemIds[id] = fit
menuItem = wx.MenuItem(menu, id, label)
menu.Bind(wx.EVT_MENU, self.handleSelection, menuItem)
return menuItem
def getSubMenu(self, context, selection, rootMenu, i, pitem):
def getSubMenu(self, context, rootMenu, i, pitem):
msw = True if "wxMSW" in wx.PlatformInfo else False
self.context = context
self.fitMenuItemIds = {}
@@ -79,7 +80,7 @@ class AddCommandFit(ContextMenu):
typeDict[shipName].append(fit)
for ship in sorted(typeDict.keys()):
shipItem = wx.MenuItem(sub, ContextMenu.nextID(), ship)
shipItem = wx.MenuItem(sub, ContextMenuUnconditional.nextID(), ship)
grandSub = wx.Menu()
shipItem.SetSubMenu(grandSub)
@@ -98,7 +99,7 @@ class AddCommandFit(ContextMenu):
return
fitID = self.mainFrame.getActiveFit()
self.mainFrame.command.Submit(cmd.GuiAddCommandFitCommand(fitID, fit.ID))
self.mainFrame.command.Submit(cmd.GuiAddCommandFitCommand(fitID=fitID, commandFitID=fit.ID))
AddCommandFit.populateFits(None)

View File

@@ -6,25 +6,26 @@ import wx
import gui.globalEvents as GE
import gui.mainFrame
from gui.bitmap_loader import BitmapLoader
from gui.contextMenu import ContextMenu
from gui.contextMenu import ContextMenuUnconditional
from service.damagePattern import DamagePattern as import_DamagePattern
from service.fit import Fit
from service.settings import ContextMenuSettings
class ChangeDamagePattern(ContextMenu):
class ChangeDamagePattern(ContextMenuUnconditional):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
def display(self, srcContext):
return srcContext == "resistancesViewFull"
@property
def enabled(self):
return self.mainFrame.getActiveFit() is not None
def getText(self, itmContext, selection):
def getText(self, itmContext):
sDP = import_DamagePattern.getInstance()
sFit = Fit.getInstance()
fitID = self.mainFrame.getActiveFit()
@@ -55,7 +56,7 @@ class ChangeDamagePattern(ContextMenu):
return self.m
def addPattern(self, rootMenu, pattern):
id = ContextMenu.nextID()
id = ContextMenuUnconditional.nextID()
name = getattr(pattern, "_name", pattern.name) if pattern is not None else "No Profile"
self.patternIds[id] = pattern
@@ -76,7 +77,7 @@ class ChangeDamagePattern(ContextMenu):
menuItem.SetBitmap(bitmap)
return menuItem
def getSubMenu(self, context, selection, rootMenu, i, pitem):
def getSubMenu(self, context, rootMenu, i, pitem):
msw = True if "wxMSW" in wx.PlatformInfo else False
if self.m[i] not in self.subMenus:

View File

@@ -1,34 +1,50 @@
import gui.fitCommands as cmd
import gui.mainFrame
from gui.contextMenu import ContextMenu
from gui.contextMenu import ContextMenuSingle
from gui.fitCommands.helpers import droneStackLimit
from service.fit import Fit
from service.settings import ContextMenuSettings
class DroneAddStack(ContextMenu):
class DroneAddStack(ContextMenuSingle):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
if srcContext not in ('marketItemGroup', 'marketItemMisc') or self.mainFrame.getActiveFit() is None:
def display(self, srcContext, mainItem):
if srcContext not in ('marketItemGroup', 'marketItemMisc'):
return False
for selected_item in selection:
if selected_item.category.ID in (
18, # Drones
):
return True
if self.mainFrame.getActiveFit() is None:
return False
return False
if mainItem is None:
return False
def getText(self, itmContext, selection):
return "Add {0} to Drone Bay (x5)".format(itmContext)
if mainItem.category.name != 'Drone':
return False
def activate(self, fullContext, selection, i):
self.mainFrame.command.Submit(cmd.GuiAddLocalDroneCommand(
fitID=self.mainFrame.getActiveFit(), itemID=int(selection[0].ID), amount=5))
self.mainFrame.additionsPane.select('Drones')
fitID = self.mainFrame.getActiveFit()
fit = Fit.getInstance().getFit(fitID)
amount = droneStackLimit(fit, mainItem)
if amount < 1:
return False
self.amount = amount
return True
def getText(self, itmContext, mainItem):
return 'Add {} to Drone Bay{}'.format(
itmContext, '' if self.amount == 1 else ' (x{})'.format(self.amount))
def activate(self, fullContext, mainItem, i):
command = cmd.GuiAddLocalDroneCommand(
fitID=self.mainFrame.getActiveFit(),
itemID=int(mainItem.ID),
amount=self.amount)
if self.mainFrame.command.Submit(command):
self.mainFrame.additionsPane.select('Drones', focus=False)
DroneAddStack.register()

View File

@@ -5,26 +5,31 @@ import wx
import gui.fitCommands as cmd
import gui.mainFrame
from gui.contextMenu import ContextMenu
from gui.contextMenu import ContextMenuSingle
from service.fit import Fit
from service.settings import ContextMenuSettings
class DroneSplitStack(ContextMenu):
class DroneSplitStack(ContextMenuSingle):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
return srcContext == "droneItem" and selection[0].amount > 1
def display(self, srcContext, mainItem):
if srcContext != "droneItem":
return False
def getText(self, itmContext, selection):
return "Split {0} Stack".format(itmContext)
if mainItem is None:
return False
def activate(self, fullContext, selection, i):
srcContext = fullContext[0]
drone = selection[0]
dlg = DroneStackSplit(self.mainFrame, drone.amount)
return mainItem.amount > 1
def getText(self, itmContext, mainItem):
return "Split {} Stack".format(itmContext)
def activate(self, fullContext, mainItem, i):
dlg = DroneStackSplit(self.mainFrame, mainItem.amount)
if dlg.ShowModal() == wx.ID_OK:
@@ -35,10 +40,10 @@ class DroneSplitStack(ContextMenu):
fit = Fit.getInstance().getFit(fitID)
cleanInput = re.sub(r'[^0-9.]', '', dlg.input.GetLineText(0).strip())
self.mainFrame.command.Submit(cmd.GuiSplitLocalDroneStackCommand(
fitID=fitID,
position=fit.drones.index(drone),
amount=int(cleanInput)))
if mainItem in fit.drones:
position = fit.drones.index(mainItem)
self.mainFrame.command.Submit(cmd.GuiSplitLocalDroneStackCommand(
fitID=fitID, position=position, amount=int(cleanInput)))
DroneSplitStack.register()

View File

@@ -6,12 +6,12 @@ import wx
import gui.fitCommands as cmd
import gui.mainFrame
from gui.contextMenu import ContextMenu
from gui.contextMenu import ContextMenuUnconditional
from service.market import Market
from service.settings import ContextMenuSettings
class AddEnvironmentEffect(ContextMenu):
class AddEnvironmentEffect(ContextMenuUnconditional):
# CCP doesn't currently provide a mapping between the general Environment, and the specific environment effect
# (which can be random when going into Abyssal space). This is how we currently define it:
@@ -28,13 +28,13 @@ class AddEnvironmentEffect(ContextMenu):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
def display(self, srcContext):
return srcContext == "projected"
def getText(self, itmContext, selection):
def getText(self, itmContext):
return "Add Environmental Effect"
def getSubMenu(self, context, selection, rootMenu, i, pitem):
def getSubMenu(self, context, rootMenu, i, pitem):
msw = True if "wxMSW" in wx.PlatformInfo else False
# Wormholes
@@ -92,7 +92,7 @@ class AddEnvironmentEffect(ContextMenu):
def processFlat(data, root, sub):
for swData in sorted(data, key=lambda tpl: tpl[2]):
wxid = ContextMenu.nextID()
wxid = ContextMenuUnconditional.nextID()
swObj, swName, swClass = swData
self.idmap[wxid] = (swObj, swName)
subItem = wx.MenuItem(sub, wxid, swClass)
@@ -123,7 +123,8 @@ class AddEnvironmentEffect(ContextMenu):
# Expressions for matching when detecting effects we're looking for
if incursions:
validgroups = ("Incursion ship attributes effects",)
validgroups = ("Incursion ship attributes effects",
"Invasion Effects")
else:
validgroups = ("Black Hole Effect Beacon",
"Cataclysmic Variable Effect Beacon",
@@ -133,7 +134,7 @@ class AddEnvironmentEffect(ContextMenu):
"Wolf Rayet Effect Beacon")
# Stuff we don't want to see in names
garbages = ("Effect", "Beacon", "ship attributes effects")
garbages = ("Effects?", "Beacon", "ship attributes effects")
# Get group with all the system-wide beacons
grp = sMkt.getGroup("Effect Beacon")

View File

@@ -3,27 +3,28 @@ import wx
import gui.globalEvents as GE
import gui.mainFrame
from gui.contextMenu import ContextMenu
from gui.contextMenu import ContextMenuUnconditional
from service.fit import Fit
from service.settings import ContextMenuSettings
class FactorReload(ContextMenu):
class FactorReload(ContextMenuUnconditional):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
def display(self, srcContext):
return srcContext == "firepowerViewFull"
@property
def enabled(self):
return self.mainFrame.getActiveFit() is not None
def getText(self, itmContext, selection):
def getText(self, itmContext):
return "Factor in Reload Time"
def activate(self, fullContext, selection, i):
def activate(self, fullContext, i):
sFit = Fit.getInstance()
sFit.serviceFittingOptions["useGlobalForceReload"] = not sFit.serviceFittingOptions["useGlobalForceReload"]
fitID = self.mainFrame.getActiveFit()

View File

@@ -3,37 +3,43 @@ import wx
import gui.mainFrame
from gui import fitCommands as cmd
from gui.contextMenu import ContextMenu
from gui.fitCommands.helpers import getSimilarFighters
from gui.contextMenu import ContextMenuCombined
from service.fit import Fit
from service.settings import ContextMenuSettings
class FighterAbilities(ContextMenu):
class FighterAbilities(ContextMenuCombined):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
self.isProjected = None
def display(self, srcContext, selection):
def display(self, srcContext, mainItem, selection):
if self.mainFrame.getActiveFit() is None or srcContext not in ("fighterItem", "projectedFighter"):
return False
self.fighter = selection[0]
if mainItem is None:
return False
self.fighter = mainItem
self.selection = selection
self.isProjected = True if srcContext == "projectedFighter" else False
return True
def getText(self, itmContext, selection):
def getText(self, itmContext, mainItem, selection):
return "Abilities"
def addAbility(self, menu, ability):
label = ability.name
id = ContextMenu.nextID()
id = ContextMenuCombined.nextID()
self.abilityIds[id] = ability
menuItem = wx.MenuItem(menu, id, label, kind=wx.ITEM_CHECK)
menu.Bind(wx.EVT_MENU, self.handleMode, menuItem)
return menuItem
def getSubMenu(self, context, selection, rootMenu, i, pitem):
def getSubMenu(self, context, mainItem, selection, rootMenu, i, pitem):
msw = True if "wxMSW" in wx.PlatformInfo else False
self.context = context
self.abilityIds = {}
@@ -58,12 +64,26 @@ class FighterAbilities(ContextMenu):
fitID = self.mainFrame.getActiveFit()
fit = Fit.getInstance().getFit(fitID)
if self.isProjected:
self.mainFrame.command.Submit(cmd.GuiToggleProjectedFighterAbilityStateCommand(
fitID=fitID, position=fit.projectedFighters.index(self.fighter), effectID=ability.effectID))
container = fit.projectedFighters
command = cmd.GuiToggleProjectedFighterAbilityStateCommand
else:
self.mainFrame.command.Submit(cmd.GuiToggleLocalFighterAbilityStateCommand(
fitID=fitID, position=fit.fighters.index(self.fighter), effectID=ability.effectID))
container = fit.fighters
command = cmd.GuiToggleLocalFighterAbilityStateCommand
if self.fighter in container:
mainPosition = container.index(self.fighter)
if wx.GetMouseState().GetModifiers() == wx.MOD_ALT:
fighters = getSimilarFighters(container, self.fighter)
else:
fighters = self.selection
positions = []
for fighter in fighters:
if fighter in container:
positions.append(container.index(fighter))
self.mainFrame.command.Submit(command(
fitID=fitID,
mainPosition=mainPosition,
positions=positions,
effectID=ability.effectID))
FighterAbilities.register()

View File

@@ -4,25 +4,26 @@ import wx
import gui.fitCommands as cmd
import gui.mainFrame
from gui.builtinViews.emptyView import BlankPage
from gui.contextMenu import ContextMenu
from gui.contextMenu import ContextMenuUnconditional
from service.fit import Fit
class AddCurrentlyOpenFit(ContextMenu):
class AddCurrentlyOpenFit(ContextMenuUnconditional):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
def display(self, srcContext, selection):
def display(self, srcContext):
if self.mainFrame.getActiveFit() is None or srcContext not in ('projected', 'commandView'):
return False
return True
def getText(self, itmContext, selection):
def getText(self, itmContext):
return 'Add Currently Open Fit'
def getSubMenu(self, context, selection, rootMenu, i, pitem):
def getSubMenu(self, context, rootMenu, i, pitem):
self.fitLookup = {}
self.context = context
sFit = Fit.getInstance()
@@ -40,7 +41,7 @@ class AddCurrentlyOpenFit(ContextMenu):
if isinstance(page, BlankPage):
continue
fit = sFit.getFit(page.activeFitID, basic=True)
id = ContextMenu.nextID()
id = ContextMenuUnconditional.nextID()
mitem = wx.MenuItem(rootMenu, id, "{}: {}".format(fit.ship.item.name, fit.name))
bindmenu.Bind(wx.EVT_MENU, self.handleSelection, mitem)
self.fitLookup[id] = fit
@@ -56,7 +57,7 @@ class AddCurrentlyOpenFit(ContextMenu):
if self.context == 'commandView':
self.mainFrame.command.Submit(cmd.GuiAddCommandFitCommand(fitID=fitID, commandFitID=fit.ID))
elif self.context == 'projected':
self.mainFrame.command.Submit(cmd.GuiAddProjectedFitCommand(fitID=fitID, projectedFitID=fit.ID))
self.mainFrame.command.Submit(cmd.GuiAddProjectedFitCommand(fitID=fitID, projectedFitID=fit.ID, amount=1))
AddCurrentlyOpenFit.register()

View File

@@ -3,30 +3,34 @@ import wx
import gui.mainFrame
from gui.builtinShipBrowser.events import FitSelected
from gui.contextMenu import ContextMenu
from gui.contextMenu import ContextMenuSingle
from service.settings import ContextMenuSettings
class OpenFitInNewTab(ContextMenu):
class OpenFitInNewTab(ContextMenuSingle):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
def display(self, srcContext, mainItem):
if srcContext not in ("projectedFit", "commandFit"):
return False
if mainItem is None:
return False
currentFitID = self.mainFrame.getActiveFit()
selectedFitID = selection[0].ID
selectedFitID = mainItem.ID
if currentFitID == selectedFitID:
return False
return True
def getText(self, itmContext, selection):
def getText(self, itmContext, mainItem):
return "Open Fit in New Tab"
def activate(self, fullContext, selection, i):
fit = selection[0]
wx.PostEvent(self.mainFrame, FitSelected(fitID=fit.ID, startup=2))
def activate(self, fullContext, mainItem, i):
wx.PostEvent(self.mainFrame, FitSelected(fitID=mainItem.ID, startup=2))
OpenFitInNewTab.register()

View File

@@ -0,0 +1,67 @@
from collections import OrderedDict
import wx
import gui.fitCommands as cmd
import gui.mainFrame
from eos.const import FitSystemSecurity
from gui.contextMenu import ContextMenuUnconditional
from service.fit import Fit
optionMap = OrderedDict((
('High Security', FitSystemSecurity.HISEC),
('Low Security', FitSystemSecurity.LOWSEC),
('Null Security', FitSystemSecurity.NULLSEC),
('W-Space', FitSystemSecurity.WSPACE)))
class FitSystemSecurityMenu(ContextMenuUnconditional):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
def display(self, srcContext):
if srcContext != "fittingShip":
return False
fitID = self.mainFrame.getActiveFit()
fit = Fit.getInstance().getFit(fitID)
if not fit.isStructure:
return
return True
def getText(self, itmContext):
return "Citadel System Security"
def addOption(self, menu, optionLabel):
id = ContextMenuUnconditional.nextID()
self.optionIds[id] = optionLabel
menuItem = wx.MenuItem(menu, id, optionLabel, kind=wx.ITEM_CHECK)
menu.Bind(wx.EVT_MENU, self.handleMode, menuItem)
return menuItem
def getSubMenu(self, context, rootMenu, i, pitem):
fitID = self.mainFrame.getActiveFit()
fit = Fit.getInstance().getFit(fitID)
msw = True if "wxMSW" in wx.PlatformInfo else False
self.optionIds = {}
sub = wx.Menu()
for optionLabel, optionValue in optionMap.items():
menuItem = self.addOption(rootMenu if msw else sub, optionLabel)
sub.Append(menuItem)
menuItem.Check(fit.getSystemSecurity() == optionValue)
return sub
def handleMode(self, event):
optionLabel = self.optionIds[event.Id]
optionValue = optionMap[optionLabel]
self.mainFrame.command.Submit(cmd.GuiChangeFitSystemSecurityCommand(
fitID=self.mainFrame.getActiveFit(),
secStatus=optionValue))
FitSystemSecurityMenu.register()

View File

@@ -4,18 +4,19 @@ import wx
import gui.fitCommands as cmd
import gui.globalEvents as GE
import gui.mainFrame
from gui.contextMenu import ContextMenu
from gui.contextMenu import ContextMenuSingle
from service.character import Character
from service.implantSet import ImplantSets as s_ImplantSets
from service.settings import ContextMenuSettings
class addImplantSet(ContextMenu):
class AddImplantSet(ContextMenuSingle):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
def display(self, srcContext, mainItem):
sIS = s_ImplantSets.getInstance()
implantSets = sIS.getImplantSetList()
@@ -24,17 +25,17 @@ class addImplantSet(ContextMenu):
return False
return srcContext in ("implantView", "implantEditor")
def getText(self, itmContext, selection):
def getText(self, itmContext, mainItem):
return "Add Implant Set"
def getSubMenu(self, context, selection, rootMenu, i, pitem):
def getSubMenu(self, context, mainItem, rootMenu, i, pitem):
"""
A note on the selection here: Most context menus act on a fit, so it's easy enough to get the active fit from
A note on the mainItem here: Most context menus act on a fit, so it's easy enough to get the active fit from
the MainFrame instance. There's never been a reason to get info from another window, so there's not common
way of doing this. However, we use this context menu within the Character Editor to apply implant sets to a
character, so we need to access the character editor.
It is for these reasons that I hijack the selection parameter when calling the menu and pass a pointer to the
It is for these reasons that I hijack the mainItem parameter when calling the menu and pass a pointer to the
Character Editor. This way we can use it to get current editing character ID and apply the implants.
It would probably be better to have a function on the MainFrame to get the currently open Character Editor (as
@@ -49,13 +50,12 @@ class addImplantSet(ContextMenu):
implantSets = sIS.getImplantSetList()
self.context = context
if len(selection) == 1:
self.selection = selection[0] # dirty hack here
self.mainItem = mainItem # dirty hack here
self.idmap = {}
for set in implantSets:
id = ContextMenu.nextID()
id = ContextMenuSingle.nextID()
mitem = wx.MenuItem(rootMenu, id, set.name)
bindmenu.Bind(wx.EVT_MENU, self.handleSelection, mitem)
self.idmap[id] = set
@@ -73,16 +73,16 @@ class addImplantSet(ContextMenu):
if self.context == "implantEditor":
# we are calling from character editor, the implant source is different
sChar = Character.getInstance()
char = self.selection.entityEditor.getActiveEntity()
char = self.mainItem.entityEditor.getActiveEntity()
for implant in set.implants:
sChar.addImplant(char.ID, implant.item.ID)
wx.PostEvent(self.selection, GE.CharChanged())
wx.PostEvent(self.mainItem, GE.CharChanged())
else:
self.mainFrame.command.Submit(cmd.GuiAddImplantSetCommand(
fitID=self.mainFrame.getActiveFit(),
itemIDs=[i.itemID for i in set.implants]))
addImplantSet.register()
AddImplantSet.register()

View File

@@ -9,35 +9,43 @@ from eos.saveddata.cargo import Cargo as es_Cargo
from eos.saveddata.drone import Drone
from eos.saveddata.fighter import Fighter as es_Fighter
from eos.saveddata.fit import Fit as es_Fit
from gui.contextMenu import ContextMenu
from gui.contextMenu import ContextMenuSingle
from service.fit import Fit
from service.settings import ContextMenuSettings
class ChangeItemAmount(ContextMenu):
class ChangeItemAmount(ContextMenuSingle):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
return srcContext in ("droneItem", "projectedDrone", "cargoItem", "projectedFit", "fighterItem", "projectedFighter")
def display(self, srcContext, mainItem):
if srcContext not in ("droneItem", "projectedDrone", "cargoItem", "projectedFit", "fighterItem", "projectedFighter"):
return False
def getText(self, itmContext, selection):
return u"Change {0} Quantity".format(itmContext)
if mainItem is None:
return False
def activate(self, fullContext, selection, i):
thing = selection[0]
mainFrame = gui.mainFrame.MainFrame.getInstance()
fitID = mainFrame.getActiveFit()
return True
def getText(self, itmContext, mainItem):
return "Change {0} Quantity".format(itmContext)
def activate(self, fullContext, mainItem, i):
fitID = self.mainFrame.getActiveFit()
srcContext = fullContext[0]
if isinstance(thing, es_Fit):
value = thing.getProjectionInfo(fitID).amount
elif isinstance(thing, es_Fighter):
value = thing.amountActive
if isinstance(mainItem, es_Fit):
try:
value = mainItem.getProjectionInfo(fitID).amount
except AttributeError:
return
elif isinstance(mainItem, es_Fighter):
value = mainItem.amountActive
else:
value = thing.amount
value = mainItem.amount
dlg = AmountChanger(self.mainFrame, value, (0, 20)) if isinstance(thing, es_Fit) else AmountChanger(self.mainFrame, value)
dlg = AmountChanger(self.mainFrame, value, (0, 20)) if isinstance(mainItem, es_Fit) else AmountChanger(self.mainFrame, value)
if dlg.ShowModal() == wx.ID_OK:
if dlg.input.GetLineText(0).strip() == '':
@@ -47,20 +55,32 @@ class ChangeItemAmount(ContextMenu):
fit = sFit.getFit(fitID)
cleanInput = int(float(re.sub(r'[^0-9.]', '', dlg.input.GetLineText(0).strip())))
if isinstance(thing, es_Cargo):
self.mainFrame.command.Submit(cmd.GuiChangeCargoAmountCommand(fitID, thing.itemID, cleanInput))
elif isinstance(thing, Drone):
if isinstance(mainItem, es_Cargo):
self.mainFrame.command.Submit(cmd.GuiChangeCargoAmountCommand(
fitID=fitID, itemID=mainItem.itemID, amount=cleanInput))
elif isinstance(mainItem, Drone):
if srcContext == "projectedDrone":
self.mainFrame.command.Submit(cmd.GuiChangeProjectedDroneAmountCommand(fitID, thing.itemID, cleanInput))
self.mainFrame.command.Submit(cmd.GuiChangeProjectedDroneAmountCommand(
fitID=fitID, itemID=mainItem.itemID, amount=cleanInput))
else:
self.mainFrame.command.Submit(cmd.GuiChangeLocalDroneAmountCommand(fitID, fit.drones.index(thing), cleanInput))
elif isinstance(thing, es_Fit):
self.mainFrame.command.Submit(cmd.GuiChangeProjectedFitAmountCommand(fitID, thing.ID, cleanInput))
elif isinstance(thing, es_Fighter):
if mainItem in fit.drones:
position = fit.drones.index(mainItem)
self.mainFrame.command.Submit(cmd.GuiChangeLocalDroneAmountCommand(
fitID=fitID, position=position, amount=cleanInput))
elif isinstance(mainItem, es_Fit):
self.mainFrame.command.Submit(cmd.GuiChangeProjectedFitAmountCommand(
fitID=fitID, projectedFitID=mainItem.ID, amount=cleanInput))
elif isinstance(mainItem, es_Fighter):
if srcContext == "projectedFighter":
self.mainFrame.command.Submit(cmd.GuiChangeProjectedFighterAmountCommand(fitID, fit.projectedFighters.index(thing), cleanInput))
if mainItem in fit.projectedFighters:
position = fit.projectedFighters.index(mainItem)
self.mainFrame.command.Submit(cmd.GuiChangeProjectedFighterAmountCommand(
fitID=fitID, position=position, amount=cleanInput))
else:
self.mainFrame.command.Submit(cmd.GuiChangeLocalFighterAmountCommand(fitID, fit.fighters.index(thing), cleanInput))
if mainItem in fit.fighters:
position = fit.fighters.index(mainItem)
self.mainFrame.command.Submit(cmd.GuiChangeLocalFighterAmountCommand(
fitID=fitID, position=position, amount=cleanInput))
ChangeItemAmount.register()

View File

@@ -0,0 +1,40 @@
import gui.fitCommands as cmd
import gui.mainFrame
from gui.contextMenu import ContextMenuSingle
from service.settings import ContextMenuSettings
class FillWithItem(ContextMenuSingle):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, mainItem):
if not self.settings.get('moduleFill'):
return False
if srcContext not in ('marketItemGroup', 'marketItemMisc'):
return False
if self.mainFrame.getActiveFit() is None:
return False
if mainItem is None:
return False
if mainItem.category.name != 'Module':
return False
return True
def getText(self, itmContext, mainItem):
return "Fill With Module"
def activate(self, fullContext, mainItem, i):
self.mainFrame.command.Submit(cmd.GuiFillWithNewLocalModulesCommand(
fitID=self.mainFrame.getActiveFit(),
itemID=int(mainItem.ID)))
FillWithItem.register()

View File

@@ -1,15 +1,15 @@
import gui.mainFrame
from gui.contextMenu import ContextMenu
from gui.contextMenu import ContextMenuSingle
from service.market import Market
from service.settings import ContextMenuSettings
class JumpToMarketItem(ContextMenu):
class JumpToMarketItem(ContextMenuSingle):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
def display(self, srcContext, mainItem):
validContexts = ("marketItemMisc", "fittingModule",
"fittingCharge", "droneItem",
"implantItem", "boosterItem",
@@ -18,37 +18,40 @@ class JumpToMarketItem(ContextMenu):
"implantItemChar", "fighterItem",
"projectedFighter")
if srcContext not in validContexts or selection is None or len(selection) < 1:
if srcContext not in validContexts or mainItem is None:
return False
if mainItem is None or getattr(mainItem, "isEmpty", False):
return False
sMkt = Market.getInstance()
item = getattr(selection[0], "item", selection[0])
isMutated = getattr(selection[0], "isMutated", False)
item = getattr(mainItem, "item", mainItem)
isMutated = getattr(mainItem, "isMutated", False)
mktGrp = sMkt.getMarketGroupByItem(item)
if mktGrp is None and isMutated:
mktGrp = sMkt.getMarketGroupByItem(selection[0].baseItem)
mktGrp = sMkt.getMarketGroupByItem(mainItem.baseItem)
# 1663 is Special Edition Festival Assets, we don't have root group for it
if mktGrp is None or mktGrp.ID == 1663:
return False
doit = not selection[0].isEmpty if srcContext == "fittingModule" else True
doit = not mainItem.isEmpty if srcContext == "fittingModule" else True
return doit
def getText(self, itmContext, selection):
def getText(self, itmContext, mainItem):
return "{0} Market Group".format(itmContext if itmContext is not None else "Item")
def activate(self, fullContext, selection, i):
def activate(self, fullContext, mainItem, i):
srcContext = fullContext[0]
if srcContext in ("fittingCharge", "projectedCharge"):
item = selection[0].charge
elif hasattr(selection[0], "item"):
if getattr(selection[0], "isMutated", False):
item = selection[0].baseItem
item = mainItem.charge
elif hasattr(mainItem, "item"):
if getattr(mainItem, "isMutated", False):
item = mainItem.baseItem
else:
item = selection[0].item
item = mainItem.item
else:
item = selection[0]
item = mainItem
self.mainFrame.notebookBrowsers.SetSelection(0)
self.mainFrame.marketBrowser.jump(item)

View File

@@ -1,22 +1,26 @@
import gui.fitCommands as cmd
import gui.mainFrame
from gui.contextMenu import ContextMenu
from gui.contextMenu import ContextMenuSingle
from service.fit import Fit
from service.settings import ContextMenuSettings
class ProjectItem(ContextMenu):
class ProjectItem(ContextMenuSingle):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
def display(self, srcContext, mainItem):
if not self.settings.get('project'):
return False
if srcContext not in ("marketItemGroup", "marketItemMisc") or self.mainFrame.getActiveFit() is None:
return False
if mainItem is None:
return False
sFit = Fit.getInstance()
fitID = self.mainFrame.getActiveFit()
fit = sFit.getFit(fitID)
@@ -24,25 +28,23 @@ class ProjectItem(ContextMenu):
if fit.isStructure:
return False
item = selection[0]
return item.isType("projected")
return mainItem.isType("projected")
def getText(self, itmContext, selection):
def getText(self, itmContext, mainItem):
return "Project {0} onto Fit".format(itmContext)
def activate(self, fullContext, selection, i):
def activate(self, fullContext, mainItem, i):
fitID = self.mainFrame.getActiveFit()
category = selection[0].category.name
if category == 'Module':
success = self.mainFrame.command.Submit(cmd.GuiAddProjectedModuleCommand(fitID=fitID, itemID=selection[0].ID))
elif category == 'Drone':
success = self.mainFrame.command.Submit(cmd.GuiAddProjectedDroneCommand(fitID=fitID, itemID=selection[0].ID))
elif category == 'Fighter':
success = self.mainFrame.command.Submit(cmd.GuiAddProjectedFighterCommand(fitID=fitID, itemID=selection[0].ID))
if mainItem.isModule:
success = self.mainFrame.command.Submit(cmd.GuiAddProjectedModuleCommand(fitID=fitID, itemID=mainItem.ID))
elif mainItem.isDrone:
success = self.mainFrame.command.Submit(cmd.GuiAddProjectedDroneCommand(fitID=fitID, itemID=mainItem.ID))
elif mainItem.isFighter:
success = self.mainFrame.command.Submit(cmd.GuiAddProjectedFighterCommand(fitID=fitID, itemID=mainItem.ID))
else:
success = False
if success:
self.mainFrame.additionsPane.select('Projected')
self.mainFrame.additionsPane.select('Projected', focus=False)
ProjectItem.register()

View File

@@ -1,71 +1,163 @@
import math
import wx
import gui.fitCommands as cmd
import gui.mainFrame
from gui.contextMenu import ContextMenu
from eos.saveddata.drone import Drone as EosDrone
from eos.saveddata.fighter import Fighter as EosFighter
from eos.saveddata.fit import Fit as EosFit
from eos.saveddata.module import Module as EosModule
from gui.contextMenu import ContextMenuCombined
from gui.fitCommands.helpers import getSimilarFighters, getSimilarModPositions
from service.fit import Fit
from service.settings import ContextMenuSettings
class RemoveItem(ContextMenu):
class RemoveItem(ContextMenuCombined):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
return srcContext in ("fittingModule", "droneItem",
"implantItem", "boosterItem",
"projectedModule", "cargoItem",
"projectedFit", "projectedDrone",
"fighterItem", "projectedFighter",
"commandFit")
def display(self, srcContext, mainItem, selection):
if srcContext not in (
"fittingModule", "droneItem",
"implantItem", "boosterItem",
"projectedModule", "cargoItem",
"projectedFit", "projectedDrone",
"fighterItem", "projectedFighter",
"commandFit"
):
return False
def getText(self, itmContext, selection):
return u"Remove {0}".format(itmContext if itmContext is not None else "Item")
if mainItem is None or getattr(mainItem, "isEmpty", False):
return False
def activate(self, fullContext, selection, i):
self.srcContext = srcContext
return True
def getText(self, itmContext, mainItem, selection):
return 'Remove {}{}'.format(
itmContext if itmContext is not None else 'Item',
' Stack' if self.srcContext in ('droneItem', 'projectedDrone', 'cargoItem', 'projectedFit') else '')
def activate(self, fullContext, mainItem, selection, i):
handlerMap = {
'fittingModule': self.__handleModule,
'droneItem': self.__handleDrone,
'fighterItem': self.__handleFighter,
'implantItem': self.__handleImplant,
'boosterItem': self.__handleBooster,
'cargoItem': self.__handleCargo,
'projectedFit': self.__handleProjectedItem,
'projectedModule': self.__handleProjectedItem,
'projectedDrone': self.__handleProjectedItem,
'projectedFighter': self.__handleProjectedItem,
'commandFit': self.__handleCommandFit}
srcContext = fullContext[0]
sFit = Fit.getInstance()
fitID = self.mainFrame.getActiveFit()
fit = sFit.getFit(fitID)
handler = handlerMap.get(srcContext)
if handler is None:
return
handler(mainItem, selection)
if srcContext == "fittingModule":
self.mainFrame.command.Submit(cmd.GuiRemoveLocalModuleCommand(
fitID=fitID, modules=[module for module in selection if module is not None]))
elif srcContext == "fittingCharge":
self.mainFrame.command.Submit(cmd.GuiChangeLocalModuleChargesCommand(
fitID=fitID, modules=selection, chargeItemID=None))
elif srcContext == "droneItem":
self.mainFrame.command.Submit(cmd.GuiRemoveLocalDroneCommand(
fitID=fitID, position=fit.drones.index(selection[0]), amount=math.inf))
elif srcContext == "fighterItem":
self.mainFrame.command.Submit(cmd.GuiRemoveLocalFighterCommand(
fitID=fitID, position=fit.fighters.index(selection[0])))
elif srcContext == "implantItem":
self.mainFrame.command.Submit(cmd.GuiRemoveImplantCommand(
fitID=fitID, position=fit.implants.index(selection[0])))
elif srcContext == "boosterItem":
self.mainFrame.command.Submit(cmd.GuiRemoveBoosterCommand(
fitID=fitID, position=fit.boosters.index(selection[0])))
elif srcContext == "cargoItem":
self.mainFrame.command.Submit(cmd.GuiRemoveCargoCommand(
fitID=fitID, itemID=selection[0].itemID))
elif srcContext == "projectedFit":
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedFitCommand(
fitID=fitID, projectedFitID=selection[0].ID))
elif srcContext == "projectedModule":
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedModuleCommand(
fitID=fitID, position=fit.projectedModules.index(selection[0])))
elif srcContext == "projectedDrone":
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedDroneCommand(
fitID=fitID, itemID=selection[0].itemID, amount=math.inf))
elif srcContext == "projectedFighter":
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedFighterCommand(
fitID=fitID, position=fit.projectedFighters.index(selection[0])))
elif srcContext == "commandFit":
self.mainFrame.command.Submit(cmd.GuiRemoveCommandFitCommand(
fitID=fitID, commandFitID=selection[0].ID))
def __handleModule(self, mainItem, selection):
fitID = self.mainFrame.getActiveFit()
fit = Fit.getInstance().getFit(fitID)
if wx.GetMouseState().GetModifiers() == wx.MOD_ALT:
positions = getSimilarModPositions(fit.modules, mainItem)
else:
positions = []
for mod in selection:
if mod in fit.modules:
positions.append(fit.modules.index(mod))
self.mainFrame.command.Submit(cmd.GuiRemoveLocalModuleCommand(
fitID=fitID, positions=positions))
def __handleDrone(self, mainItem, selection):
fitID = self.mainFrame.getActiveFit()
fit = Fit.getInstance().getFit(fitID)
positions = []
for drone in selection:
if drone in fit.drones:
positions.append(fit.drones.index(drone))
self.mainFrame.command.Submit(cmd.GuiRemoveLocalDronesCommand(
fitID=fitID, positions=positions, amount=math.inf))
def __handleFighter(self, mainItem, selection):
fitID = self.mainFrame.getActiveFit()
fit = Fit.getInstance().getFit(fitID)
if wx.GetMouseState().GetModifiers() == wx.MOD_ALT:
fighters = getSimilarFighters(fit.fighters, mainItem)
else:
fighters = selection
positions = []
for fighter in fighters:
if fighter in fit.fighters:
positions.append(fit.fighters.index(fighter))
self.mainFrame.command.Submit(cmd.GuiRemoveLocalFightersCommand(
fitID=fitID, positions=positions))
def __handleImplant(self, mainItem, selection):
fitID = self.mainFrame.getActiveFit()
fit = Fit.getInstance().getFit(fitID)
positions = []
for implant in selection:
if implant in fit.implants:
positions.append(fit.implants.index(implant))
self.mainFrame.command.Submit(cmd.GuiRemoveImplantsCommand(
fitID=fitID, positions=positions))
def __handleBooster(self, mainItem, selection):
fitID = self.mainFrame.getActiveFit()
fit = Fit.getInstance().getFit(fitID)
positions = []
for booster in selection:
if booster in fit.boosters:
positions.append(fit.boosters.index(booster))
self.mainFrame.command.Submit(cmd.GuiRemoveBoostersCommand(
fitID=fitID, positions=positions))
def __handleCargo(self, mainItem, selection):
fitID = self.mainFrame.getActiveFit()
itemIDs = [c.itemID for c in selection]
self.mainFrame.command.Submit(cmd.GuiRemoveCargosCommand(
fitID=fitID, itemIDs=itemIDs))
def __handleProjectedItem(self, mainItem, selection):
fitID = self.mainFrame.getActiveFit()
if isinstance(mainItem, EosFit):
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedItemsCommand(
fitID=fitID, items=selection, amount=math.inf))
elif isinstance(mainItem, EosModule):
if wx.GetMouseState().GetModifiers() == wx.MOD_ALT:
fit = Fit.getInstance().getFit(fitID)
positions = getSimilarModPositions(fit.projectedModules, mainItem)
items = [fit.projectedModules[p] for p in positions]
else:
items = selection
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedItemsCommand(
fitID=fitID, items=items, amount=math.inf))
elif isinstance(mainItem, EosDrone):
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedItemsCommand(
fitID=fitID, items=selection, amount=math.inf))
elif isinstance(mainItem, EosFighter):
if wx.GetMouseState().GetModifiers() == wx.MOD_ALT:
fit = Fit.getInstance().getFit(fitID)
items = getSimilarFighters(fit.projectedFighters, mainItem)
else:
items = selection
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedItemsCommand(
fitID=fitID, items=items, amount=math.inf))
else:
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedItemsCommand(
fitID=fitID, items=selection, amount=math.inf))
def __handleCommandFit(self, mainItem, selection):
fitID = self.mainFrame.getActiveFit()
commandFitIDs = [cf.ID for cf in selection]
self.mainFrame.command.Submit(cmd.GuiRemoveCommandFitsCommand(
fitID=fitID, commandFitIDs=commandFitIDs))
RemoveItem.register()

View File

@@ -2,42 +2,51 @@
import wx
import gui.mainFrame
from gui.contextMenu import ContextMenu
from gui.contextMenu import ContextMenuSingle
from gui.itemStats import ItemStatsDialog
from service.fit import Fit
from eos.saveddata.mode import Mode
from service.settings import ContextMenuSettings
class ItemStats(ContextMenu):
class ItemStats(ContextMenuSingle):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
return srcContext in ("marketItemGroup", "marketItemMisc",
"fittingModule", "fittingCharge",
"fittingShip", "baseShip",
"cargoItem", "droneItem",
"implantItem", "boosterItem",
"skillItem", "projectedModule",
"projectedDrone", "projectedCharge",
"itemStats", "fighterItem",
"implantItemChar", "projectedFighter",
"fittingMode")
def display(self, srcContext, mainItem):
if srcContext not in (
"marketItemGroup", "marketItemMisc",
"fittingModule", "fittingCharge",
"fittingShip", "baseShip",
"cargoItem", "droneItem",
"implantItem", "boosterItem",
"skillItem", "projectedModule",
"projectedDrone", "projectedCharge",
"itemStats", "fighterItem",
"implantItemChar", "projectedFighter",
"fittingMode"
):
return False
def getText(self, itmContext, selection):
return "{0} Stats".format(itmContext if itmContext is not None else "Item")
if (mainItem is None or getattr(mainItem, "isEmpty", False)) and srcContext != "fittingShip":
return False
def activate(self, fullContext, selection, i):
return True
def getText(self, itmContext, mainItem):
return "{} Stats".format(itmContext if itmContext is not None else "Item")
def activate(self, fullContext, mainItem, i):
srcContext = fullContext[0]
if srcContext == "fittingShip":
fitID = self.mainFrame.getActiveFit()
sFit = Fit.getInstance()
stuff = sFit.getFit(fitID).ship
elif srcContext == "fittingMode":
stuff = selection[0].item
stuff = mainItem.item
else:
stuff = selection[0]
stuff = mainItem
if srcContext == "fittingModule" and stuff.isEmpty:
return
@@ -45,7 +54,7 @@ class ItemStats(ContextMenu):
mstate = wx.GetMouseState()
reuse = False
if mstate.cmdDown:
if mstate.GetModifiers() == wx.MOD_SHIFT:
reuse = True
if self.mainFrame.GetActiveStatsWindow() is None and reuse:

View File

@@ -3,18 +3,20 @@ import wx
import gui.fitCommands as cmd
import gui.mainFrame
from gui.contextMenu import ContextMenu
from gui.contextMenu import ContextMenuCombined
from gui.fitCommands.helpers import getSimilarModPositions, getSimilarFighters
from service.fit import Fit
from service.market import Market
from service.settings import ContextMenuSettings
class ChangeItemToVariation(ContextMenu):
class ChangeItemToVariation(ContextMenuCombined):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
def display(self, srcContext, mainItem, selection):
if not self.settings.get('metaSwap'):
return False
@@ -31,29 +33,22 @@ class ChangeItemToVariation(ContextMenu):
):
return False
# Check if list of variations is same for all of selection
# If not - don't show the menu
mkt = Market.getInstance()
self.variations = None
for i in selection:
variations = mkt.getVariationsByItems([i.item])
if self.variations is None:
self.variations = variations
else:
if variations != self.variations:
return False
if mainItem is None or getattr(mainItem, 'isEmpty', False):
return False
self.mainVariations = Market.getInstance().getVariationsByItems((mainItem.item,))
# No variations from current module
if len(self.mainVariations) < 2:
return False
self.mainItem = mainItem
self.selection = selection
if len(self.variations) == 1:
return False # no variations from current module
return True
def getText(self, itmContext, selection):
def getText(self, itmContext, mainItem, selection):
return 'Variations'
def getSubMenu(self, context, selection, rootMenu, i, pitem):
def getSubMenu(self, context, mainItem, selection, rootMenu, i, pitem):
self.moduleLookup = {}
sFit = Fit.getInstance()
fit = sFit.getFit(self.mainFrame.getActiveFit())
@@ -64,11 +59,13 @@ class ChangeItemToVariation(ContextMenu):
return x.attributes['metaLevel'].value
def get_metagroup(x):
return x.metaGroup.ID if x.metaGroup is not None else 0
# We want deadspace before officer mods
remap = {5: 6, 6: 5}
return remap.get(x.metaGroup.ID, x.metaGroup.ID) if x.metaGroup is not None else 0
def get_boosterrank(x):
# If we're returning a lot of items, sort my name
if len(self.variations) > 7:
if len(self.mainVariations) > 7:
return x.name
# Sort by booster chance to get some sort of pseudorank.
elif 'boosterEffectChance1' in x.attributes:
@@ -87,13 +84,14 @@ class ChangeItemToVariation(ContextMenu):
bindmenu = m
# Sort items by metalevel, and group within that metalevel
items = list(self.variations)
items = list(self.mainVariations)
# Sort all items by name first
items.sort(key=lambda x: x.name)
# Do not do any extra sorting for implants
if 'implantItem' in context:
# sort implants based on name
items.sort(key=lambda x: x.name)
pass
# Boosters don't have meta or anything concrete that we can rank by. Go by chance to inflict side effect
elif 'boosterItem' in context:
# boosters don't have meta or anything concrete that we can rank by. Go by chance to inflict side effect
items.sort(key=get_boosterrank)
else:
# sort by group and meta level
@@ -112,13 +110,13 @@ class ChangeItemToVariation(ContextMenu):
if thisgroup != group and context not in ('implantItem', 'boosterItem'):
group = thisgroup
id = ContextMenu.nextID()
id = ContextMenuCombined.nextID()
m.Append(id, '%s' % group)
m.Enable(id, False)
id = ContextMenu.nextID()
id = ContextMenuCombined.nextID()
mitem = wx.MenuItem(rootMenu, id, item.name)
bindmenu.Bind(wx.EVT_MENU, self.handleModule, mitem)
bindmenu.Bind(wx.EVT_MENU, self.handleSwitch, mitem)
self.moduleLookup[id] = item, context
m.Append(mitem)
@@ -126,48 +124,175 @@ class ChangeItemToVariation(ContextMenu):
return m
def handleModule(self, event):
def handleSwitch(self, event):
item, context = self.moduleLookup.get(event.Id, None)
if item is None:
event.Skip()
return
handlerMap = {
'fittingModule': self.__handleModule,
'droneItem': self.__handleDrone,
'fighterItem': self.__handleFighter,
'cargoItem': self.__handleCargo,
'implantItem': self.__handleImplant,
'boosterItem': self.__handleBooster,
'projectedModule': self.__handleProjectedModule,
'projectedDrone': self.__handleProjectedDrone,
'projectedFighter': self.__handleProjectedFighter}
handler = handlerMap.get(context)
if handler is None:
return
handler(item)
def __handleModule(self, varItem):
fitID = self.mainFrame.getActiveFit()
fit = Fit.getInstance().getFit(fitID)
if context == 'fittingModule':
positions = [mod.modPosition for mod in self.selection]
self.mainFrame.command.Submit(cmd.GuiChangeLocalModuleMetasCommand(
fitID=fitID, positions=positions, newItemID=item.ID))
elif context == 'droneItem':
position = fit.drones.index(self.selection[0])
self.mainFrame.command.Submit(cmd.GuiChangeLocalDroneMetaCommand(
fitID=fitID, position=position, newItemID=item.ID))
elif context == 'fighterItem':
position = fit.fighters.index(self.selection[0])
self.mainFrame.command.Submit(cmd.GuiChangeLocalFighterMetaCommand(
fitID=fitID, position=position, newItemID=item.ID))
elif context == 'implantItem':
position = fit.implants.index(self.selection[0])
if wx.GetMouseState().GetModifiers() == wx.MOD_ALT:
positions = getSimilarModPositions(fit.modules, self.mainItem)
else:
sMkt = Market.getInstance()
positions = []
for mod in self.selection:
if mod.isEmpty:
continue
if mod is self.mainItem:
positions.append(fit.modules.index(mod))
continue
if mod not in fit.modules:
continue
modVariations = sMkt.getVariationsByItems((mod.item,))
if modVariations == self.mainVariations:
positions.append(fit.modules.index(mod))
self.mainFrame.command.Submit(cmd.GuiChangeLocalModuleMetasCommand(
fitID=fitID, positions=positions, newItemID=varItem.ID))
def __handleDrone(self, varItem):
fitID = self.mainFrame.getActiveFit()
fit = Fit.getInstance().getFit(fitID)
sMkt = Market.getInstance()
positions = []
for drone in self.selection:
if drone not in fit.drones:
continue
if drone is self.mainItem:
positions.append(fit.drones.index(drone))
continue
droneVariations = sMkt.getVariationsByItems((drone.item,))
if droneVariations == self.mainVariations:
positions.append(fit.drones.index(drone))
self.mainFrame.command.Submit(cmd.GuiChangeLocalDroneMetasCommand(
fitID=fitID, positions=positions, newItemID=varItem.ID))
def __handleFighter(self, varItem):
fitID = self.mainFrame.getActiveFit()
fit = Fit.getInstance().getFit(fitID)
if wx.GetMouseState().GetModifiers() == wx.MOD_ALT:
fighters = getSimilarFighters(fit.fighters, self.mainItem)
else:
fighters = self.selection
sMkt = Market.getInstance()
positions = []
for fighter in fighters:
if fighter not in fit.fighters:
continue
if fighter is self.mainItem:
positions.append(fit.fighters.index(fighter))
continue
fighterVariations = sMkt.getVariationsByItems((fighter.item,))
if fighterVariations == self.mainVariations:
positions.append(fit.fighters.index(fighter))
self.mainFrame.command.Submit(cmd.GuiChangeLocalFighterMetasCommand(
fitID=fitID, positions=positions, newItemID=varItem.ID))
def __handleCargo(self, varItem):
fitID = self.mainFrame.getActiveFit()
sMkt = Market.getInstance()
itemIDs = []
for cargo in self.selection:
if cargo is self.mainItem:
itemIDs.append(cargo.itemID)
continue
cargoVariations = sMkt.getVariationsByItems((cargo.item,))
if cargoVariations == self.mainVariations:
itemIDs.append(cargo.itemID)
self.mainFrame.command.Submit(cmd.GuiChangeCargoMetasCommand(
fitID=fitID, itemIDs=itemIDs, newItemID=varItem.ID))
def __handleImplant(self, varItem):
fitID = self.mainFrame.getActiveFit()
fit = Fit.getInstance().getFit(fitID)
implant = self.mainItem
if implant in fit.implants:
position = fit.implants.index(implant)
self.mainFrame.command.Submit(cmd.GuiChangeImplantMetaCommand(
fitID=fitID, position=position, newItemID=item.ID))
elif context == 'boosterItem':
position = fit.boosters.index(self.selection[0])
fitID=fitID, position=position, newItemID=varItem.ID))
def __handleBooster(self, varItem):
fitID = self.mainFrame.getActiveFit()
fit = Fit.getInstance().getFit(fitID)
booster = self.mainItem
if booster in fit.boosters:
position = fit.boosters.index(booster)
self.mainFrame.command.Submit(cmd.GuiChangeBoosterMetaCommand(
fitID=fitID, position=position, newItemID=item.ID))
elif context == 'cargoItem':
self.mainFrame.command.Submit(cmd.GuiChangeCargoMetaCommand(
fitID=fitID, itemID=self.selection[0].itemID, newItemID=item.ID))
elif context == 'projectedModule':
position = fit.projectedModules.index(self.selection[0])
self.mainFrame.command.Submit(cmd.GuiChangeProjectedModuleMetaCommand(
fitID=fitID, position=position, newItemID=item.ID))
elif context == 'projectedDrone':
self.mainFrame.command.Submit(cmd.GuiChangeProjectedDroneMetaCommand(
fitID=fitID, itemID=self.selection[0].itemID, newItemID=item.ID))
elif context == 'projectedFighter':
position = fit.projectedFighters.index(self.selection[0])
self.mainFrame.command.Submit(cmd.GuiChangeProjectedFighterMetaCommand(
fitID=fitID, position=position, newItemID=item.ID))
fitID=fitID, position=position, newItemID=varItem.ID))
def __handleProjectedModule(self, varItem):
fitID = self.mainFrame.getActiveFit()
fit = Fit.getInstance().getFit(fitID)
if wx.GetMouseState().GetModifiers() == wx.MOD_ALT:
positions = getSimilarModPositions(fit.projectedModules, self.mainItem)
else:
sMkt = Market.getInstance()
positions = []
for mod in self.selection:
if mod is self.mainItem:
positions.append(fit.projectedModules.index(mod))
continue
if mod not in fit.projectedModules:
continue
modVariations = sMkt.getVariationsByItems((mod.item,))
if modVariations == self.mainVariations:
positions.append(fit.projectedModules.index(mod))
self.mainFrame.command.Submit(cmd.GuiChangeProjectedModuleMetasCommand(
fitID=fitID, positions=positions, newItemID=varItem.ID))
def __handleProjectedDrone(self, varItem):
fitID = self.mainFrame.getActiveFit()
fit = Fit.getInstance().getFit(fitID)
sMkt = Market.getInstance()
itemIDs = []
for drone in self.selection:
if drone not in fit.projectedDrones:
continue
if drone is self.mainItem:
itemIDs.append(drone.itemID)
continue
droneVariations = sMkt.getVariationsByItems((drone.item,))
if droneVariations == self.mainVariations:
itemIDs.append(drone.itemID)
self.mainFrame.command.Submit(cmd.GuiChangeProjectedDroneMetasCommand(
fitID=fitID, itemIDs=itemIDs, newItemID=varItem.ID))
def __handleProjectedFighter(self, varItem):
fitID = self.mainFrame.getActiveFit()
fit = Fit.getInstance().getFit(fitID)
if wx.GetMouseState().GetModifiers() == wx.MOD_ALT:
fighters = getSimilarFighters(fit.projectedFighters, self.mainItem)
else:
fighters = self.selection
sMkt = Market.getInstance()
positions = []
for fighter in fighters:
if fighter not in fit.projectedFighters:
continue
if fighter is self.mainItem:
positions.append(fit.projectedFighters.index(fighter))
continue
fighterVariations = sMkt.getVariationsByItems((fighter.item,))
if fighterVariations == self.mainVariations:
positions.append(fit.projectedFighters.index(fighter))
self.mainFrame.command.Submit(cmd.GuiChangeProjectedFighterMetasCommand(
fitID=fitID, positions=positions, newItemID=varItem.ID))
ChangeItemToVariation.register()

View File

@@ -4,54 +4,60 @@ import wx
import gui.fitCommands as cmd
import gui.mainFrame
from eos.const import FittingHardpoint
from eos.saveddata.module import Module
from gui.bitmap_loader import BitmapLoader
from gui.contextMenu import ContextMenu
from gui.contextMenu import ContextMenuCombined
from gui.fitCommands.helpers import getSimilarModPositions
from service.fit import Fit
from service.market import Market
from service.settings import ContextMenuSettings
class ChangeModuleAmmo(ContextMenu):
class ChangeModuleAmmo(ContextMenuCombined):
DAMAGE_TYPES = ("em", "explosive", "kinetic", "thermal")
MISSILE_ORDER = ("em", "thermal", "kinetic", "explosive", "mixed")
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
# Format: {type ID: set(loadable, charges)}
self.loadableCharges = {}
def display(self, srcContext, selection):
if self.mainFrame.getActiveFit() is None or srcContext not in ("fittingModule", "projectedModule"):
def display(self, srcContext, mainItem, selection):
if srcContext not in ("fittingModule", "projectedModule"):
return False
modules = selection if srcContext == "fittingModule" else (selection[0],)
validCharges = None
checkedTypes = set()
for mod in modules:
# loop through modules and gather list of valid charges
if mod.item.ID in checkedTypes:
continue
checkedTypes.add(mod.item.ID)
currCharges = mod.getValidCharges()
if len(currCharges) > 0:
if validCharges is not None and validCharges != currCharges:
return False
validCharges = currCharges
self.module = mod
if validCharges is None:
if self.mainFrame.getActiveFit() is None:
return False
self.modules = modules
self.charges = list([charge for charge in validCharges if Market.getInstance().getPublicityByItem(charge)])
self.context = srcContext
return len(self.charges) > 0
self.mainCharges = self.getChargesForMod(mainItem)
if not self.mainCharges:
return False
def getText(self, itmContext, selection):
self.module = mainItem
self.selection = selection
self.srcContext = srcContext
return True
def getText(self, itmContext, mainItem, selection):
return "Charge"
def getChargesForMod(self, mod):
sMkt = Market.getInstance()
if mod is None or mod.isEmpty:
return set()
typeID = mod.item.ID
if typeID in self.loadableCharges:
return self.loadableCharges[typeID]
chargeSet = self.loadableCharges.setdefault(typeID, set())
# Do not try to grab it for modes which can also be passed as part of selection
if isinstance(mod, Module):
for charge in mod.getValidCharges():
if sMkt.getPublicityByItem(charge):
chargeSet.add(charge)
return chargeSet
def turretSorter(self, charge):
damage = 0
range_ = (self.module.item.getAttribute("maxRange")) * \
@@ -107,7 +113,7 @@ class ChangeModuleAmmo(ContextMenu):
return list(map(self.numericConverter, parts))
def addCharge(self, menu, charge):
id_ = ContextMenu.nextID()
id_ = ContextMenuCombined.nextID()
name = charge.name if charge is not None else "Empty"
self.chargeIds[id_] = charge
item = wx.MenuItem(menu, id_, name)
@@ -122,11 +128,11 @@ class ChangeModuleAmmo(ContextMenu):
@staticmethod
def addSeperator(m, text):
id_ = ContextMenu.nextID()
id_ = ContextMenuCombined.nextID()
m.Append(id_, '%s' % text)
m.Enable(id_, False)
def getSubMenu(self, context, selection, rootMenu, i, pitem):
def getSubMenu(self, context, mainItem, selection, rootMenu, i, pitem):
msw = True if "wxMSW" in wx.PlatformInfo else False
m = wx.Menu()
self.chargeIds = {}
@@ -139,8 +145,8 @@ class ChangeModuleAmmo(ContextMenu):
range_ = None
nameBase = None
sub = None
self.charges.sort(key=self.turretSorter)
for charge in self.charges:
chargesSorted = sorted(self.mainCharges, key=self.turretSorter)
for charge in chargesSorted:
if "civilian" in charge.name.lower():
continue
currBase = charge.name.rsplit()[-2:]
@@ -173,11 +179,11 @@ class ChangeModuleAmmo(ContextMenu):
self.addSeperator(m, "Short Range")
elif hardpoint == FittingHardpoint.MISSILE and moduleName != 'Festival Launcher':
self.charges.sort(key=self.missileSorter)
type_ = None
sub = None
defender = None
for charge in self.charges:
chargesSorted = sorted(self.mainCharges, key=self.missileSorter)
for charge in chargesSorted:
currType = self.damageInfo(charge)[0]
if currType != type_ or type_ is None:
@@ -206,8 +212,8 @@ class ChangeModuleAmmo(ContextMenu):
if sub is not None:
self.addSeperator(sub, "More Damage")
else:
self.charges.sort(key=self.nameSorter)
for charge in self.charges:
chargesSorted = sorted(self.mainCharges, key=self.nameSorter)
for charge in chargesSorted:
m.Append(self.addCharge(rootMenu if msw else m, charge))
m.Append(self.addCharge(rootMenu if msw else m, None))
@@ -221,33 +227,43 @@ class ChangeModuleAmmo(ContextMenu):
fitID = self.mainFrame.getActiveFit()
sFit = Fit.getInstance()
switchAll = sFit.serviceFittingOptions['ammoChangeAll'] is not wx.GetMouseState().CmdDown()
# Switch in selection or all modules, depending on ctrl key state and settings
fit = sFit.getFit(fitID)
mstate = wx.GetMouseState()
# Switch in selection or all modules, depending on modifier key state and settings
switchAll = sFit.serviceFittingOptions['ammoChangeAll'] is not mstate.GetModifiers() in (wx.MOD_ALT, wx.MOD_CONTROL)
if switchAll:
fit = sFit.getFit(fitID)
selectedModule = self.modules[0]
if self.context == 'fittingModule':
self.mainFrame.command.Submit(cmd.GuiChangeLocalModuleChargesCommand(
fitID=fitID,
modules=[m for m in fit.modules if m.itemID is not None and m.itemID == selectedModule.itemID],
chargeItemID=charge.ID if charge is not None else None))
elif self.context == 'projectedModule':
self.mainFrame.command.Submit(cmd.GuiChangeProjectedModuleChargesCommand(
fitID=fitID,
modules=[m for m in fit.projectedModules if
m.itemID is not None and m.itemID == selectedModule.itemID],
chargeItemID=charge.ID if charge is not None else None))
if self.srcContext == 'fittingModule':
command = cmd.GuiChangeLocalModuleChargesCommand
modContainer = fit.modules
elif self.srcContext == 'projectedModule':
command = cmd.GuiChangeProjectedModuleChargesCommand
modContainer = fit.projectedModules
else:
return
positions = getSimilarModPositions(modContainer, self.module)
self.mainFrame.command.Submit(command(
fitID=fitID,
positions=positions,
chargeItemID=charge.ID if charge is not None else None))
else:
if self.context == 'fittingModule':
self.mainFrame.command.Submit(cmd.GuiChangeLocalModuleChargesCommand(
fitID=fitID,
modules=self.modules,
chargeItemID=charge.ID if charge is not None else None))
elif self.context == 'projectedModule':
self.mainFrame.command.Submit(cmd.GuiChangeProjectedModuleChargesCommand(
fitID=fitID,
modules=self.modules,
chargeItemID=charge.ID if charge is not None else None))
if self.srcContext == 'fittingModule':
command = cmd.GuiChangeLocalModuleChargesCommand
modContainer = fit.modules
elif self.srcContext == 'projectedModule':
command = cmd.GuiChangeProjectedModuleChargesCommand
modContainer = fit.projectedModules
else:
return
positions = []
for position, mod in enumerate(modContainer):
if mod in self.selection:
modCharges = self.getChargesForMod(mod)
if modCharges.issubset(self.mainCharges):
positions.append(position)
self.mainFrame.command.Submit(command(
fitID=fitID,
positions=positions,
chargeItemID=charge.ID if charge is not None else None))
ChangeModuleAmmo.register()

View File

@@ -1,35 +1,40 @@
# noinspection PyPackageRequirements
import wx
import gui.fitCommands as cmd
import gui.globalEvents as GE
import gui.mainFrame
from gui.contextMenu import ContextMenu
from gui.contextMenu import ContextMenuSingle
from service.fit import Fit
from service.settings import ContextMenuSettings
class FillWithModule(ContextMenu):
class FillWithModule(ContextMenuSingle):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
def display(self, srcContext, mainItem):
if not self.settings.get('moduleFill'):
return False
if mainItem is None or getattr(mainItem, 'isEmpty', False):
return False
return srcContext == "fittingModule"
def getText(self, itmContext, selection):
return u"Fill With {0}".format(itmContext if itmContext is not None else "Module")
def getText(self, itmContext, mainItem):
return "Fill With {0}".format(itmContext if itmContext is not None else "Module")
def activate(self, fullContext, selection, i):
def activate(self, fullContext, mainItem, i):
srcContext = fullContext[0]
fitID = self.mainFrame.getActiveFit()
if srcContext == "fittingModule":
self.mainFrame.command.Submit(cmd.GuiFillWithLocalModulesCommand(fitID, selection[0].itemID))
return # the command takes care of the PostEvent
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
fit = Fit.getInstance().getFit(fitID)
if mainItem in fit.modules:
position = fit.modules.index(mainItem)
self.mainFrame.command.Submit(cmd.GuiFillWithClonedLocalModulesCommand(
fitID=fitID, position=position))
FillWithModule.register()

View File

@@ -2,51 +2,49 @@
import wx
import gui.mainFrame
from gui.contextMenu import ContextMenu
from gui.contextMenu import ContextMenuSingle
from gui.fitCommands import GuiConvertMutatedLocalModuleCommand, GuiRevertMutatedLocalModuleCommand
from service.fit import Fit
from service.settings import ContextMenuSettings
class ChangeModuleMutation(ContextMenu):
class ChangeModuleMutation(ContextMenuSingle):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
self.eventIDs = {}
def display(self, srcContext, selection):
# if not self.settings.get('ammoPattern'):
# return False
def display(self, srcContext, mainItem):
if srcContext != "fittingModule" or self.mainFrame.getActiveFit() is None:
return False
mod = selection[0]
if len(mod.item.mutaplasmids) == 0 and not mod.isMutated:
if mainItem is None or mainItem.isEmpty:
return False
if len(mainItem.item.mutaplasmids) == 0 and not mainItem.isMutated:
return False
return True
def getText(self, itmContext, selection):
mod = selection[0]
return "Apply Mutaplasmid" if not mod.isMutated else "Revert to {}".format(mod.baseItem.name)
def getText(self, itmContext, mainItem):
return "Apply Mutaplasmid" if not mainItem.isMutated else "Revert to {}".format(mainItem.baseItem.name)
def getSubMenu(self, context, selection, rootMenu, i, pitem):
if selection[0].isMutated:
def getSubMenu(self, context, mainItem, rootMenu, i, pitem):
if mainItem.isMutated:
return None
msw = True if "wxMSW" in wx.PlatformInfo else False
self.skillIds = {}
sub = wx.Menu()
mod = selection[0]
menu = rootMenu if msw else sub
for item in mod.item.mutaplasmids:
for item in mainItem.item.mutaplasmids:
label = item.item.name
id = ContextMenu.nextID()
self.eventIDs[id] = (item, mod)
id = ContextMenuSingle.nextID()
self.eventIDs[id] = (item, mainItem)
skillItem = wx.MenuItem(menu, id, label)
menu.Bind(wx.EVT_MENU, self.handleMenu, skillItem)
sub.Append(skillItem)
@@ -55,19 +53,22 @@ class ChangeModuleMutation(ContextMenu):
def handleMenu(self, event):
mutaplasmid, mod = self.eventIDs[event.Id]
fitID = self.mainFrame.getActiveFit()
fit = Fit.getInstance().getFit(fitID)
if mod in fit.modules:
position = fit.modules.index(mod)
self.mainFrame.command.Submit(GuiConvertMutatedLocalModuleCommand(
fitID=fitID, position=position, mutaplasmid=mutaplasmid))
self.mainFrame.command.Submit(GuiConvertMutatedLocalModuleCommand(
fitID=self.mainFrame.getActiveFit(),
position=mod.modPosition,
mutaplasmid=mutaplasmid))
def activate(self, fullContext, mainItem, i):
fitID = self.mainFrame.getActiveFit()
fit = Fit.getInstance().getFit(fitID)
if mainItem in fit.modules:
position = fit.modules.index(mainItem)
self.mainFrame.command.Submit(GuiRevertMutatedLocalModuleCommand(
fitID=fitID, position=position))
def activate(self, fullContext, selection, i):
mod = selection[0]
self.mainFrame.command.Submit(GuiRevertMutatedLocalModuleCommand(
fitID=self.mainFrame.getActiveFit(),
position=mod.modPosition))
def getBitmap(self, context, selection):
def getBitmap(self, context, mainItem):
return None

View File

@@ -1,3 +1,5 @@
import math
# noinspection PyPackageRequirements
import wx
@@ -5,33 +7,38 @@ import eos.config
import gui.fitCommands as cmd
import gui.mainFrame
from eos.utils.spoolSupport import SpoolType, SpoolOptions
from gui.contextMenu import ContextMenu
from gui.contextMenu import ContextMenuSingle
from service.fit import Fit
from service.settings import ContextMenuSettings
class ChangeModuleSpool(ContextMenu):
class ChangeModuleSpool(ContextMenuSingle):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
self.cycleMap = {}
self.resetId = None
def display(self, srcContext, selection):
def display(self, srcContext, mainItem):
if not self.settings.get('spoolup'):
return False
if srcContext not in ('fittingModule', 'projectedModule') or self.mainFrame.getActiveFit() is None:
return False
self.mod = selection[0]
if mainItem is None or mainItem.isEmpty:
return False
self.mod = mainItem
self.context = srcContext
return self.mod.item.group.name in ("Precursor Weapon", "Mutadaptive Remote Armor Repairer")
def getText(self, itmContext, selection):
def getText(self, itmContext, mainItem):
return "Spoolup Cycles"
def getSubMenu(self, context, selection, rootMenu, i, pitem):
def getSubMenu(self, context, mainItem, rootMenu, i, pitem):
m = wx.Menu()
if "wxMSW" in wx.PlatformInfo:
bindmenu = rootMenu
@@ -43,15 +50,44 @@ class ChangeModuleSpool(ContextMenu):
cycleCurrent = self.mod.getSpoolData(spoolOptions=SpoolOptions(SpoolType.SCALE, eos.config.settings['globalDefaultSpoolupPercentage'], False))[0]
cycleMin = self.mod.getSpoolData(spoolOptions=SpoolOptions(SpoolType.SCALE, 0, True))[0]
cycleMax = self.mod.getSpoolData(spoolOptions=SpoolOptions(SpoolType.SCALE, 1, True))[0]
cycleTotalMin = min(cycleDefault, cycleCurrent, cycleMin)
cycleTotalMax = max(cycleDefault, cycleCurrent, cycleMax)
for cycle in range(cycleMin, cycleMax + 1):
menuId = ContextMenu.nextID()
def findCycles(val1, val2):
# Try to compose list of 21 steps max (0-20)
maxSteps = 20
valDiff = val2 - val1
valScale = valDiff / maxSteps
minStep = math.ceil(round(valScale, 9))
maxStep = math.floor(round(valDiff / 4, 9))
# Check steps from smallest to highest and see if we can go from min value
# to max value using those
for currentStep in range(minStep, maxStep + 1):
if valDiff % currentStep == 0:
return set(range(val1, val2 + currentStep, currentStep))
# Otherwise just split range in halves and go both ends using min values
else:
cycles = set()
while val2 >= val1:
cycles.add(val1)
cycles.add(val2)
val1 += minStep
val2 -= minStep
return cycles
cyclesToShow = findCycles(cycleMin, cycleMax)
for cycle in range(cycleTotalMin, cycleTotalMax + 1):
menuId = ContextMenuSingle.nextID()
# Show default only for current value and when not overriden
if not isNotDefault and cycle == cycleDefault:
text = "{} (default)".format(cycle)
else:
# Always show current selection and stuff which we decided to show via the cycles function
elif cycle == cycleCurrent or cycle in cyclesToShow:
text = "{}".format(cycle)
# Ignore the rest to not have very long menu
else:
continue
item = wx.MenuItem(m, menuId, text, kind=wx.ITEM_CHECK)
bindmenu.Bind(wx.EVT_MENU, self.handleSpoolChange, item)
@@ -59,7 +95,7 @@ class ChangeModuleSpool(ContextMenu):
item.Check(isNotDefault and cycle == cycleCurrent)
self.cycleMap[menuId] = cycle
self.resetId = ContextMenu.nextID()
self.resetId = ContextMenuSingle.nextID()
item = wx.MenuItem(m, self.resetId, "Reset")
bindmenu.Bind(wx.EVT_MENU, self.handleSpoolChange, item)
m.Append(item)
@@ -75,18 +111,18 @@ class ChangeModuleSpool(ContextMenu):
spoolAmount = self.cycleMap[event.Id]
else:
return
fitID = self.mainFrame.getActiveFit()
fit = Fit.getInstance().getFit(fitID)
if self.context == 'fittingModule':
self.mainFrame.command.Submit(cmd.GuiChangeLocalModuleSpoolCommand(
fitID=self.mainFrame.getActiveFit(),
position=self.mod.modPosition,
spoolType=spoolType,
spoolAmount=spoolAmount))
if self.mod in fit.modules:
position = fit.modules.index(self.mod)
self.mainFrame.command.Submit(cmd.GuiChangeLocalModuleSpoolCommand(
fitID=fitID, position=position, spoolType=spoolType, spoolAmount=spoolAmount))
elif self.context == 'projectedModule':
self.mainFrame.command.Submit(cmd.GuiChangeProjectedModuleSpoolCommand(
fitID=self.mainFrame.getActiveFit(),
position=self.mod.modPosition,
spoolType=spoolType,
spoolAmount=spoolAmount))
if self.mod in fit.projectedModules:
position = fit.projectedModules.index(self.mod)
self.mainFrame.command.Submit(cmd.GuiChangeProjectedModuleSpoolCommand(
fitID=fitID, position=position, spoolType=spoolType, spoolAmount=spoolAmount))
ChangeModuleSpool.register()

View File

@@ -4,11 +4,11 @@ import wx
import gui.globalEvents as GE
import gui.mainFrame
from gui.contextMenu import ContextMenu
from gui.contextMenu import ContextMenuUnconditional
from service.settings import MarketPriceSettings
class ItemGroupPrice(ContextMenu, metaclass=ABCMeta):
class ItemGroupPrice(ContextMenuUnconditional, metaclass=ABCMeta):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
@@ -24,13 +24,13 @@ class ItemGroupPrice(ContextMenu, metaclass=ABCMeta):
def optionName(self):
raise NotImplementedError()
def display(self, srcContext, selection):
def display(self, srcContext):
return srcContext in ("priceViewFull", "priceViewMinimal")
def getText(self, itmContext, selection):
def getText(self, itmContext):
return self.label
def activate(self, fullContext, selection, i):
def activate(self, fullContext, i):
self.settings.set(self.optionName, not self.settings.get(self.optionName))
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit()))

View File

@@ -3,30 +3,41 @@ import wx
import gui.mainFrame
from gui.builtinShipBrowser.events import Stage3Selected
from gui.contextMenu import ContextMenu
from gui.contextMenu import ContextMenuUnconditional
from service.fit import Fit
from service.settings import ContextMenuSettings
class JumpToShip(ContextMenu):
class JumpToShip(ContextMenuUnconditional):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
return srcContext == "fittingShip"
def display(self, srcContext):
if srcContext != "fittingShip":
return False
fitTabSelected = self.mainFrame.notebookBrowsers.GetSelection() == 1
if not fitTabSelected:
return True
browsingStage = self.mainFrame.shipBrowser.GetActiveStage()
if browsingStage != 3:
return True
fitID = self.mainFrame.getActiveFit()
ship = Fit.getInstance().getFit(fitID).ship
browsingShipID = self.mainFrame.shipBrowser.GetStageData(browsingStage)
if browsingShipID != ship.item.ID:
return True
return False
def getText(self, itmContext, selection):
def getText(self, itmContext):
return "Open in Fitting Browser"
def activate(self, fullContext, selection, i):
def activate(self, fullContext, i):
fitID = self.mainFrame.getActiveFit()
sFit = Fit.getInstance()
stuff = sFit.getFit(fitID).ship
groupID = stuff.item.group.ID
ship = Fit.getInstance().getFit(fitID).ship
self.mainFrame.notebookBrowsers.SetSelection(1)
wx.PostEvent(self.mainFrame.shipBrowser, Stage3Selected(shipID=stuff.item.ID, back=groupID))
wx.PostEvent(self.mainFrame.shipBrowser, Stage3Selected(shipID=ship.item.ID, back=True))
JumpToShip.register()

View File

@@ -3,17 +3,18 @@ import wx
import gui.fitCommands as cmd
import gui.mainFrame
from gui.contextMenu import ContextMenu
from gui.contextMenu import ContextMenuUnconditional
from service.fit import Fit
from service.settings import ContextMenuSettings
class ChangeShipTacticalMode(ContextMenu):
class ChangeShipTacticalMode(ContextMenuUnconditional):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
def display(self, srcContext):
if self.mainFrame.getActiveFit() is None or srcContext != "fittingShip":
return False
@@ -26,18 +27,18 @@ class ChangeShipTacticalMode(ContextMenu):
return srcContext == "fittingShip" and self.modes is not None
def getText(self, itmContext, selection):
def getText(self, itmContext):
return "Tactical Mode"
def addMode(self, menu, mode):
label = mode.item.name.rsplit()[-2]
id = ContextMenu.nextID()
id = ContextMenuUnconditional.nextID()
self.modeIds[id] = mode
menuItem = wx.MenuItem(menu, id, label, kind=wx.ITEM_RADIO)
menu.Bind(wx.EVT_MENU, self.handleMode, menuItem)
return menuItem
def getSubMenu(self, context, selection, rootMenu, i, pitem):
def getSubMenu(self, context, rootMenu, i, pitem):
msw = True if "wxMSW" in wx.PlatformInfo else False
self.context = context
self.modeIds = {}

View File

@@ -5,28 +5,39 @@ import gui.globalEvents as GE
import gui.mainFrame
from eos.saveddata.character import Skill
from gui.bitmap_loader import BitmapLoader
from gui.contextMenu import ContextMenu
from gui.contextMenu import ContextMenuSingle
from service.character import Character
from service.fit import Fit
from service.settings import ContextMenuSettings
class ChangeAffectingSkills(ContextMenu):
class ChangeAffectingSkills(ContextMenuSingle):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
def display(self, srcContext, mainItem):
if not self.settings.get('changeAffectingSkills'):
return False
if self.mainFrame.getActiveFit() is None or srcContext not in (
"fittingModule", "fittingCharge", "fittingShip", "droneItem", "fighterItem"):
if srcContext not in (
"fittingModule", "fittingCharge",
"fittingShip", "droneItem",
"fighterItem"
):
return False
fitID = self.mainFrame.getActiveFit()
if fitID is None:
return False
if (mainItem is None or getattr(mainItem, "isEmpty", False)) and srcContext != "fittingShip":
return False
self.sChar = Character.getInstance()
self.sFit = Fit.getInstance()
fit = self.sFit.getFit(self.mainFrame.getActiveFit())
fit = self.sFit.getFit(fitID)
self.charID = fit.character.ID
@@ -34,14 +45,13 @@ class ChangeAffectingSkills(ContextMenu):
# return False
if srcContext == "fittingShip":
fitID = self.mainFrame.getActiveFit()
sFit = Fit.getInstance()
self.stuff = sFit.getFit(fitID).ship
cont = sFit.getFit(fitID).ship.itemModifiedAttributes
elif srcContext == "fittingCharge":
cont = selection[0].chargeModifiedAttributes
cont = mainItem.chargeModifiedAttributes
else:
cont = selection[0].itemModifiedAttributes
cont = mainItem.itemModifiedAttributes
skills = set()
@@ -60,7 +70,7 @@ class ChangeAffectingSkills(ContextMenu):
self.skills = sorted(skills, key=lambda x: x.item.name)
return len(self.skills) > 0
def getText(self, itmContext, selection):
def getText(self, itmContext, mainItem):
return "Change %s Skills" % itmContext
def addSkill(self, rootMenu, skill, i):
@@ -69,19 +79,19 @@ class ChangeAffectingSkills(ContextMenu):
else:
label = "Level %s" % i
id = ContextMenu.nextID()
id = ContextMenuSingle.nextID()
self.skillIds[id] = (skill, i)
menuItem = wx.MenuItem(rootMenu, id, label, kind=wx.ITEM_RADIO)
rootMenu.Bind(wx.EVT_MENU, self.handleSkillChange, menuItem)
return menuItem
def getSubMenu(self, context, selection, rootMenu, i, pitem):
def getSubMenu(self, context, mainItem, rootMenu, i, pitem):
msw = True if "wxMSW" in wx.PlatformInfo else False
self.skillIds = {}
sub = wx.Menu()
for skill in self.skills:
skillItem = wx.MenuItem(sub, ContextMenu.nextID(), skill.item.name)
skillItem = wx.MenuItem(sub, ContextMenuSingle.nextID(), skill.item.name)
grandSub = wx.Menu()
skillItem.SetSubMenu(grandSub)
if skill.learned:

View File

@@ -6,18 +6,19 @@ import wx
import gui.globalEvents as GE
import gui.mainFrame
from gui.bitmap_loader import BitmapLoader
from gui.contextMenu import ContextMenu
from gui.contextMenu import ContextMenuUnconditional
from service.fit import Fit
from service.settings import ContextMenuSettings
from service.targetResists import TargetResists as svc_TargetResists
class TargetResists(ContextMenu):
class TargetResists(ContextMenuUnconditional):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
def display(self, srcContext):
if self.mainFrame.getActiveFit() is None or srcContext != "firepowerViewFull":
return False
@@ -27,7 +28,7 @@ class TargetResists(ContextMenu):
return len(self.patterns) > 0
def getText(self, itmContext, selection):
def getText(self, itmContext):
return "Target Resists"
def handleResistSwitch(self, event):
@@ -42,7 +43,7 @@ class TargetResists(ContextMenu):
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
def addPattern(self, rootMenu, pattern):
id = ContextMenu.nextID()
id = ContextMenuUnconditional.nextID()
name = getattr(pattern, "_name", pattern.name) if pattern is not None else "No Profile"
self.patternIds[id] = pattern
@@ -63,7 +64,7 @@ class TargetResists(ContextMenu):
item.SetBitmap(bitmap)
return item
def getSubMenu(self, context, selection, rootMenu, i, pitem):
def getSubMenu(self, context, rootMenu, i, pitem):
msw = True if "wxMSW" in wx.PlatformInfo else False
self.patternIds = {}
self.subMenus = OrderedDict()
@@ -92,7 +93,7 @@ class TargetResists(ContextMenu):
# Items that have a parent
for menuName, patterns in list(self.subMenus.items()):
# Create parent item for root menu that is simply name of parent
item = wx.MenuItem(rootMenu, ContextMenu.nextID(), menuName)
item = wx.MenuItem(rootMenu, ContextMenuUnconditional.nextID(), menuName)
# Create menu for child items
grandSub = wx.Menu()

View File

@@ -82,7 +82,8 @@ class ItemAffectedBy(wx.Panel):
# instead, we send the item.
type_ = stuff.__class__.__name__
contexts.append(("itemStats", type_))
menu = ContextMenu.getMenu(stuff if type_ != "Skill" else stuff.item, *contexts)
stuff = stuff if type_ != "Skill" else stuff.item
menu = ContextMenu.getMenu(stuff, (stuff,), *contexts)
self.PopupMenu(menu)
def ExpandCollapseTree(self):

View File

@@ -4,6 +4,7 @@ import random
import wx
from logbook import Logger
import gui.fitCommands as cmd
import gui.globalEvents as GE
import gui.mainFrame
from gui.bitmap_loader import BitmapLoader
@@ -11,44 +12,91 @@ from service.fit import Fit
from .attributeSlider import AttributeSlider, EVT_VALUE_CHANGED
from .itemAttributes import ItemParams
pyfalog = Logger(__name__)
class ItemMutator(wx.Panel):
class ItemMutatorPanel(wx.Panel):
def __init__(self, parent, stuff, item):
def __init__(self, parent, mod):
wx.Panel.__init__(self, parent)
self.stuff = stuff
self.item = item
self.timer = None
self.activeFit = gui.mainFrame.MainFrame.getInstance().getActiveFit()
font = parent.GetFont()
font.SetWeight(wx.BOLD)
self.stuff = mod
mainSizer = wx.BoxSizer(wx.VERTICAL)
sourceItemsSizer = wx.BoxSizer(wx.HORIZONTAL)
sourceItemsSizer.Add(BitmapLoader.getStaticBitmap(stuff.item.iconID, self, "icons"), 0, wx.LEFT, 5)
sourceItemsSizer.Add(BitmapLoader.getStaticBitmap(stuff.mutaplasmid.item.iconID, self, "icons"), 0, wx.LEFT, 0)
sourceItemShort = "{} {}".format(stuff.mutaplasmid.item.name.split(" ")[0], stuff.baseItem.name)
headerSizer = wx.BoxSizer(wx.HORIZONTAL)
headerSizer.AddStretchSpacer()
itemIcon = BitmapLoader.getStaticBitmap(mod.item.iconID, self, "icons")
if itemIcon is not None:
headerSizer.Add(itemIcon, 0, 0, 0)
mutaIcon = BitmapLoader.getStaticBitmap(mod.mutaplasmid.item.iconID, self, "icons")
if mutaIcon is not None:
headerSizer.Add(mutaIcon, 0, wx.LEFT, 0)
sourceItemShort = "{} {}".format(mod.mutaplasmid.item.name.split(" ")[0], mod.baseItem.name)
sourceItemText = wx.StaticText(self, wx.ID_ANY, sourceItemShort)
font = parent.GetFont()
font.SetWeight(wx.BOLD)
sourceItemText.SetFont(font)
sourceItemsSizer.Add(sourceItemText, 0, wx.LEFT, 10)
mainSizer.Add(sourceItemsSizer, 0, wx.TOP | wx.EXPAND, 10)
headerSizer.Add(sourceItemText, 0, wx.LEFT, 10)
headerSizer.AddStretchSpacer()
mainSizer.Add(headerSizer, 0, wx.ALL | wx.EXPAND, 5)
mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, wx.EXPAND, 0)
mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, wx.ALL | wx.EXPAND, 5)
self.mutaList = ItemMutatorList(self, mod)
mainSizer.Add(self.mutaList, 1, wx.EXPAND | wx.ALL, 0)
self.goodColor = wx.Colour(96, 191, 0)
self.badColor = wx.Colour(255, 64, 0)
mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, wx.EXPAND, 0)
footerSizer = wx.BoxSizer(wx.HORIZONTAL)
self.refreshBtn = wx.Button(self, wx.ID_ANY, "Reset defaults", wx.DefaultPosition, wx.DefaultSize, 0)
footerSizer.Add(self.refreshBtn, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5)
self.refreshBtn.Bind(wx.EVT_BUTTON, self.mutaList.resetMutatedValues)
self.randomBtn = wx.Button(self, wx.ID_ANY, "Random stats", wx.DefaultPosition, wx.DefaultSize, 0)
footerSizer.Add(self.randomBtn, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5)
self.randomBtn.Bind(wx.EVT_BUTTON, self.mutaList.randomMutatedValues)
self.revertBtn = wx.Button(self, wx.ID_ANY, "Revert changes", wx.DefaultPosition, wx.DefaultSize, 0)
footerSizer.Add(self.revertBtn, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5)
self.revertBtn.Bind(wx.EVT_BUTTON, self.mutaList.revertChanges)
mainSizer.Add(footerSizer, 0, wx.ALL | wx.EXPAND, 5)
self.SetSizer(mainSizer)
self.Layout()
def submitMutationChanges(self):
self.mutaList.submitMutationChanges()
class ItemMutatorList(wx.ScrolledWindow):
def __init__(self, parent, mod):
wx.ScrolledWindow.__init__(self, parent)
self.SetScrollRate(0, 15)
self.carryingFitID = gui.mainFrame.MainFrame.getInstance().getActiveFit()
self.initialMutations = {}
self.mod = mod
self.timer = None
goodColor = wx.Colour(96, 191, 0)
badColor = wx.Colour(255, 64, 0)
font = parent.GetFont()
font.SetWeight(wx.BOLD)
sizer = wx.BoxSizer(wx.VERTICAL)
self.event_mapping = {}
higOverrides = {
('Stasis Web', 'speedFactor'): False,
('Damage Control', 'duration'): True,
}
for m in sorted(stuff.mutators.values(), key=lambda x: x.attribute.displayName):
highIsGood = higOverrides.get((stuff.item.group.name, m.attribute.name), m.highIsGood)
first = True
for m in sorted(mod.mutators.values(), key=lambda x: x.attribute.displayName):
if not first:
sizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, wx.ALL | wx.EXPAND, 5)
first = False
self.initialMutations[m.attrID] = m.value
highIsGood = higOverrides.get((mod.item.group.name, m.attribute.name), m.highIsGood)
# Format: [raw value, modifier applied to base raw value, display value]
range1 = (m.minValue, m.attribute.unit.SimplifyValue(m.minValue))
range2 = (m.maxValue, m.attribute.unit.SimplifyValue(m.maxValue))
@@ -96,17 +144,17 @@ class ItemMutator(wx.Panel):
worseVal = ItemParams.FormatValue(*m.attribute.unit.PreformatValue(worseRange[0]), rounding='dec')
worseText = wx.StaticText(self, wx.ID_ANY, worseVal)
worseText.SetForegroundColour(self.badColor)
worseText.SetForegroundColour(badColor)
betterVal = ItemParams.FormatValue(*m.attribute.unit.PreformatValue(betterRange[0]), rounding='dec')
betterText = wx.StaticText(self, wx.ID_ANY, betterVal)
betterText.SetForegroundColour(self.goodColor)
betterText.SetForegroundColour(goodColor)
headingSizer.Add(worseText, 0, wx.ALL | wx.EXPAND, 0)
headingSizer.Add(wx.StaticText(self, wx.ID_ANY, ""), 0, wx.RIGHT | wx.LEFT | wx.EXPAND, 5)
headingSizer.Add(betterText, 0, wx.RIGHT | wx.EXPAND, 10)
mainSizer.Add(headingSizer, 0, wx.ALL | wx.EXPAND, 5)
sizer.Add(headingSizer, 0, wx.ALL | wx.EXPAND, 5)
slider = AttributeSlider(parent=self,
baseValue=m.attribute.unit.SimplifyValue(sliderBaseValue),
@@ -116,29 +164,9 @@ class ItemMutator(wx.Panel):
slider.SetValue(m.attribute.unit.SimplifyValue(m.value), False)
slider.Bind(EVT_VALUE_CHANGED, self.changeMutatedValue)
self.event_mapping[slider] = m
mainSizer.Add(slider, 0, wx.RIGHT | wx.LEFT | wx.EXPAND, 10)
sizer.Add(slider, 0, wx.RIGHT | wx.LEFT | wx.EXPAND, 10)
mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, wx.ALL | wx.EXPAND, 5)
mainSizer.AddStretchSpacer()
self.m_staticline = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL)
mainSizer.Add(self.m_staticline, 0, wx.EXPAND)
bSizer = wx.BoxSizer(wx.HORIZONTAL)
self.refreshBtn = wx.Button(self, wx.ID_ANY, "Reset defaults", wx.DefaultPosition, wx.DefaultSize, 0)
bSizer.Add(self.refreshBtn, 0, wx.ALIGN_CENTER_VERTICAL)
self.refreshBtn.Bind(wx.EVT_BUTTON, self.resetMutatedValues)
self.randomBtn = wx.Button(self, wx.ID_ANY, "Random stats", wx.DefaultPosition, wx.DefaultSize, 0)
bSizer.Add(self.randomBtn, 0, wx.ALIGN_CENTER_VERTICAL)
self.randomBtn.Bind(wx.EVT_BUTTON, self.randomMutatedValues)
mainSizer.Add(bSizer, 0, wx.RIGHT | wx.LEFT | wx.EXPAND, 0)
self.SetSizer(mainSizer)
self.Layout()
self.SetSizer(sizer)
def changeMutatedValue(self, evt):
m = self.event_mapping[evt.Object]
@@ -146,12 +174,12 @@ class ItemMutator(wx.Panel):
value = m.attribute.unit.ComplicateValue(value)
sFit = Fit.getInstance()
sFit.changeMutatedValue(m, value)
sFit.changeMutatedValuePrelim(m, value)
if self.timer:
self.timer.Stop()
self.timer = None
for x in self.Parent.Children:
for x in self.Parent.Parent.Children:
if isinstance(x, ItemParams):
x.RefreshValues(None)
break
@@ -159,36 +187,54 @@ class ItemMutator(wx.Panel):
def resetMutatedValues(self, evt):
sFit = Fit.getInstance()
for slider, m in self.event_mapping.items():
value = sFit.changeMutatedValue(m, m.baseValue)
value = sFit.changeMutatedValuePrelim(m, m.baseValue)
value = m.attribute.unit.SimplifyValue(value)
slider.SetValue(value)
evt.Skip()
def randomMutatedValues(self, evt):
sFit = Fit.getInstance()
for slider, m in self.event_mapping.items():
value = random.uniform(m.minValue, m.maxValue)
value = sFit.changeMutatedValue(m, value)
value = sFit.changeMutatedValuePrelim(m, value)
value = m.attribute.unit.SimplifyValue(value)
slider.SetValue(value)
evt.Skip()
def revertChanges(self, evt):
sFit = Fit.getInstance()
for slider, m in self.event_mapping.items():
if m.attrID in self.initialMutations:
value = sFit.changeMutatedValuePrelim(m, self.initialMutations[m.attrID])
value = m.attribute.unit.SimplifyValue(value)
slider.SetValue(value)
evt.Skip()
def submitMutationChanges(self):
fit = Fit.getInstance().getFit(self.carryingFitID)
if self.mod in fit.modules:
currentMutation = {}
for m in self.event_mapping.values():
currentMutation[m.attrID] = m.value
mainFrame = gui.mainFrame.MainFrame.getInstance()
mainFrame.getCommandForFit(self.carryingFitID).Submit(cmd.GuiChangeLocalModuleMutationCommand(
fitID=self.carryingFitID,
position=fit.modules.index(self.mod),
mutation=currentMutation,
oldMutation=self.initialMutations))
def callLater(self):
self.timer = None
sFit = Fit.getInstance()
# recalc the fit that this module affects. This is not necessarily the currently active fit
sFit.refreshFit(self.activeFit)
sFit.refreshFit(self.carryingFitID)
mainFrame = gui.mainFrame.MainFrame.getInstance()
activeFit = mainFrame.getActiveFit()
if activeFit != self.activeFit:
if activeFit != self.carryingFitID:
# if we're no longer on the fit this module is affecting, simulate a "switch fit" so that the active fit
# can be recalculated (if needed)
sFit.switchFit(activeFit)

View File

@@ -21,7 +21,7 @@ class ItemView(Display):
"attr:cpu,,,True"]
def __init__(self, parent, marketBrowser):
Display.__init__(self, parent)
Display.__init__(self, parent, style=wx.LC_SINGLE_SEL)
pyfalog.debug("Initialize ItemView")
marketBrowser.Bind(wx.EVT_TREE_SEL_CHANGED, self.treeSelectionChanged)
@@ -51,6 +51,7 @@ class ItemView(Display):
# Make reverse map, used by sorter
self.metaMap = self.makeReverseMetaMap()
self.active = []
# Fill up recently used modules set
pyfalog.debug("Fill up recently used modules set")
@@ -247,7 +248,7 @@ class ItemView(Display):
sourceContext = "marketItemMisc" if self.marketBrowser.mode in ("search", "recent") else "marketItemGroup"
itemContext = sMkt.getCategoryByItem(item).name
menu = ContextMenu.getMenu((item,), (sourceContext, itemContext))
menu = ContextMenu.getMenu(item, (item,), (sourceContext, itemContext))
self.PopupMenu(menu)
def populate(self, items):
@@ -258,7 +259,7 @@ class ItemView(Display):
sMkt = self.sMkt
self.metalvls = sMkt.directAttrRequest(items, attrs)
# Clear selection
self.deselectItems()
self.unselectAll()
# Perform sorting, using item's meta levels besides other stuff
items.sort(key=self.itemSort)
# Mark current item list as active

View File

@@ -37,6 +37,7 @@ class MarketTree(wx.TreeCtrl):
# Bind our lookup method to when the tree gets expanded
self.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.expandLookup)
self.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnCollapsed)
def addImage(self, iconFile, location="icons"):
if iconFile is None:
@@ -71,6 +72,10 @@ class MarketTree(wx.TreeCtrl):
self.SortChildren(root)
def OnCollapsed(self, event):
self.CollapseAllChildren(event.Item)
event.Skip()
def jump(self, item):
"""Open market group and meta tab of given item"""
sMkt = self.sMkt

View File

@@ -80,10 +80,10 @@ class PFFittingEnginePref(PreferenceView):
# Future code once new cap sim is implemented
'''
self.cbGlobalForceReactivationTimer = wx.CheckBox( panel, wx.ID_ANY, u"Factor in reactivation timer", wx.DefaultPosition, wx.DefaultSize, 0 )
self.cbGlobalForceReactivationTimer = wx.CheckBox( panel, wx.ID_ANY, "Factor in reactivation timer", wx.DefaultPosition, wx.DefaultSize, 0 )
mainSizer.Add( self.cbGlobalForceReactivationTimer, 0, wx.ALL|wx.EXPAND, 5 )
text = u" Ignores reactivation timer when calculating capacitor usage,\n damage, and tank."
text = " Ignores reactivation timer when calculating capacitor usage,\n damage, and tank."
self.cbGlobalForceReactivationTimerText = wx.StaticText( panel, wx.ID_ANY, text, wx.DefaultPosition, wx.DefaultSize, 0 )
self.cbGlobalForceReactivationTimerText.Wrap( -1 )
self.cbGlobalForceReactivationTimerText.SetFont( wx.Font( 10, 70, 90, 90, False, wx.EmptyString ) )
@@ -92,10 +92,10 @@ class PFFittingEnginePref(PreferenceView):
# Future code for mining laser crystal
'''
self.cbGlobalMiningSpecialtyCrystal = wx.CheckBox( panel, wx.ID_ANY, u"Factor in reactivation timer", wx.DefaultPosition, wx.DefaultSize, 0 )
self.cbGlobalMiningSpecialtyCrystal = wx.CheckBox( panel, wx.ID_ANY, "Factor in reactivation timer", wx.DefaultPosition, wx.DefaultSize, 0 )
mainSizer.Add( self.cbGlobalMiningSpecialtyCrystal, 0, wx.ALL|wx.EXPAND, 5 )
text = u" If enabled, displays the Specialty Crystal mining amount.\n This is the amount mined when using crystals and mining the matching asteroid."
text = " If enabled, displays the Specialty Crystal mining amount.\n This is the amount mined when using crystals and mining the matching asteroid."
self.cbGlobalMiningSpecialtyCrystalText = wx.StaticText( panel, wx.ID_ANY, text, wx.DefaultPosition, wx.DefaultSize, 0 )
self.cbGlobalMiningSpecialtyCrystalText.Wrap( -1 )
self.cbGlobalMiningSpecialtyCrystalText.SetFont( wx.Font( 10, 70, 90, 90, False, wx.EmptyString ) )

View File

@@ -75,7 +75,7 @@ class PFEsiPref(PreferenceView):
fgAddrSizer.SetFlexibleDirection(wx.BOTH)
fgAddrSizer.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED)
self.stSetID = wx.StaticText(panel, wx.ID_ANY, u"Client ID:", wx.DefaultPosition, wx.DefaultSize, 0)
self.stSetID = wx.StaticText(panel, wx.ID_ANY, "Client ID:", wx.DefaultPosition, wx.DefaultSize, 0)
self.stSetID.Wrap(-1)
fgAddrSizer.Add(self.stSetID, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
@@ -84,7 +84,7 @@ class PFEsiPref(PreferenceView):
fgAddrSizer.Add(self.inputClientID, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5)
self.stSetSecret = wx.StaticText(panel, wx.ID_ANY, u"Client Secret:", wx.DefaultPosition, wx.DefaultSize, 0)
self.stSetSecret = wx.StaticText(panel, wx.ID_ANY, "Client Secret:", wx.DefaultPosition, wx.DefaultSize, 0)
self.stSetSecret.Wrap(-1)
fgAddrSizer.Add(self.stSetSecret, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)

View File

@@ -1,18 +1,16 @@
# noinspection PyPackageRequirements
import wx
from wx.lib.intctrl import IntCtrl
from gui.preferenceView import PreferenceView
from gui.bitmap_loader import BitmapLoader
import gui.mainFrame
import gui.globalEvents as GE
from service.settings import SettingsProvider
import gui.mainFrame
from gui.bitmap_loader import BitmapLoader
from gui.preferenceView import PreferenceView
from service.fit import Fit
from service.price import Price
from service.settings import SettingsProvider
class PFGeneralPref(PreferenceView):
title = "General"
def populatePanel(self, panel):
@@ -85,7 +83,7 @@ class PFGeneralPref(PreferenceView):
if "wxGTK" not in wx.PlatformInfo:
self.cbReloadAll.SetCursor(helpCursor)
self.cbReloadAll.SetToolTip(wx.ToolTip(
'When disabled, reloads charges just in selected modules. Action can be reversed by holding Ctrl key while changing charge.'))
'When disabled, reloads charges just in selected modules. Action can be reversed by holding Ctrl or Alt key while changing charge.'))
mainSizer.Add(self.cbReloadAll, 0, wx.ALL | wx.EXPAND, 5)
self.sFit = Fit.getInstance()

View File

@@ -185,13 +185,14 @@ class FitItem(SFItem.SFBrowserItem):
if activeFit:
sFit = Fit.getInstance()
projectedFit = sFit.getFit(self.fitID)
if self.mainFrame.command.Submit(cmd.GuiAddProjectedCommand(activeFit, projectedFit.ID, 'fit')):
command = cmd.GuiAddProjectedFitCommand(fitID=activeFit, projectedFitID=projectedFit.ID, amount=1)
if self.mainFrame.command.Submit(command):
self.mainFrame.additionsPane.select("Projected")
def OnAddCommandFit(self, event):
activeFit = self.mainFrame.getActiveFit()
if activeFit:
if self.mainFrame.command.Submit(cmd.GuiAddCommandFitCommand(activeFit, self.fitID)):
if self.mainFrame.command.Submit(cmd.GuiAddCommandFitCommand(fitID=activeFit, commandFitID=self.fitID)):
self.mainFrame.additionsPane.select("Command")
def OnMouseCaptureLost(self, event):
@@ -345,7 +346,8 @@ class FitItem(SFItem.SFBrowserItem):
return
# to prevent accidental deletion, give dialog confirmation unless shift is depressed
if wx.GetMouseState().ShiftDown() or wx.GetMouseState().MiddleIsDown():
mstate = wx.GetMouseState()
if mstate.GetModifiers() == wx.MOD_SHIFT or mstate.MiddleIsDown():
self.deleteFit()
else:
dlg = wx.MessageDialog(

View File

@@ -22,6 +22,7 @@ import wx
class PFListPane(wx.ScrolledWindow):
def __init__(self, parent):
wx.ScrolledWindow.__init__(self, parent, pos=wx.DefaultPosition, style=wx.TAB_TRAVERSAL)
@@ -150,6 +151,11 @@ class PFListPane(wx.ScrolledWindow):
self._wList[i].Refresh()
self.itemsHeight = max(self.itemsHeight, iheight - 1)
# This is needed as under GTK wx does not emit scroll up/scroll down
# events, see issue #1909 for more info
if 'wxGTK' in wx.PlatformInfo:
self.SetScrollRate(0, self.itemsHeight)
def RemoveWidget(self, child):
child.Destroy()
self._wList.remove(child)

View File

@@ -108,7 +108,7 @@ class ShipItem(SFItem.SFBrowserItem):
pos = event.GetPosition()
pos = self.ScreenToClient(pos)
contexts = [("baseShip", "Ship Basic")]
menu = ContextMenu.getMenu(self.baseItem, *contexts)
menu = ContextMenu.getMenu(self.baseItem, (self.baseItem,), *contexts)
self.PopupMenu(menu, pos)
def OnTimer(self, event):

View File

@@ -49,6 +49,7 @@ class ResourcesViewFull(StatsView):
self.toggleContext("fighter")
else:
self.toggleContext("drone")
event.Skip()
def toggleContext(self, context):
# Apparently you cannot .Hide(True) on a Window, otherwise I would just .Hide(context !== x).

View File

@@ -1,259 +0,0 @@
# =============================================================================
# Copyright (C) 2010 Diego Duclos
#
# This file is part of pyfa.
#
# pyfa is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# pyfa is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
# =============================================================================
# noinspection PyPackageRequirements
import wx
from gui.statsView import StatsView
from gui.utils.numberFormatter import formatAmount
from collections import OrderedDict
class TargetingMiscViewFull(StatsView):
name = "targetingMiscViewFull"
def __init__(self, parent):
StatsView.__init__(self)
self.parent = parent
self._cachedValues = []
def getHeaderText(self, fit):
return "Targeting && Misc"
def getTextExtentW(self, text):
width, height = self.parent.GetTextExtent(text)
return width
def populatePanel(self, contentPanel, headerPanel):
contentSizer = contentPanel.GetSizer()
self.panel = contentPanel
self.headerPanel = headerPanel
gridTargetingMisc = wx.FlexGridSizer(1, 3, 0, 0)
contentSizer.Add(gridTargetingMisc, 0, wx.EXPAND | wx.ALL, 0)
gridTargetingMisc.AddGrowableCol(0)
gridTargetingMisc.AddGrowableCol(2)
# Targeting
gridTargeting = wx.FlexGridSizer(5, 2, 0, 0)
gridTargeting.AddGrowableCol(1)
gridTargetingMisc.Add(gridTargeting, 0, wx.ALIGN_LEFT | wx.ALL, 5)
labels = (("Targets", "Targets", ""),
("Range", "Range", "km"),
("Scan res.", "ScanRes", "mm"),
("Sensor str.", "SensorStr", ""),
("Drone range", "CtrlRange", "km"))
for header, labelShort, unit in labels:
gridTargeting.Add(wx.StaticText(contentPanel, wx.ID_ANY, "%s: " % header), 0, wx.ALIGN_LEFT)
box = wx.BoxSizer(wx.HORIZONTAL)
gridTargeting.Add(box, 0, wx.ALIGN_LEFT)
lbl = wx.StaticText(contentPanel, wx.ID_ANY, "0 %s" % unit)
setattr(self, "label%s" % labelShort, lbl)
box.Add(lbl, 0, wx.ALIGN_LEFT)
self._cachedValues.append({"main": 0})
# Misc
gridTargetingMisc.Add(wx.StaticLine(contentPanel, wx.ID_ANY, style=wx.VERTICAL), 0, wx.EXPAND, 3)
gridMisc = wx.FlexGridSizer(5, 2, 0, 0)
gridMisc.AddGrowableCol(1)
gridTargetingMisc.Add(gridMisc, 0, wx.ALIGN_LEFT | wx.ALL, 5)
labels = (("Speed", "Speed", "m/s"),
("Align time", "AlignTime", "s"),
("Signature", "SigRadius", "m"),
("Warp Speed", "WarpSpeed", "AU/s"),
("Cargo", "Cargo", "m\u00B3"))
for header, labelShort, unit in labels:
gridMisc.Add(wx.StaticText(contentPanel, wx.ID_ANY, "%s: " % header), 0, wx.ALIGN_LEFT)
box = wx.BoxSizer(wx.HORIZONTAL)
gridMisc.Add(box, 0, wx.ALIGN_LEFT)
lbl = wx.StaticText(contentPanel, wx.ID_ANY, "0 %s" % unit)
setattr(self, "labelFull%s" % labelShort, lbl)
box.Add(lbl, 0, wx.ALIGN_LEFT)
self._cachedValues.append({"main": 0})
def refreshPanel(self, fit):
# If we did anything interesting, we'd update our labels to reflect the new fit's stats here
cargoNamesOrder = OrderedDict((
("fleetHangarCapacity", "Fleet hangar"),
("shipMaintenanceBayCapacity", "Maintenance bay"),
("specialAmmoHoldCapacity", "Ammo hold"),
("specialFuelBayCapacity", "Fuel bay"),
("specialShipHoldCapacity", "Ship hold"),
("specialSmallShipHoldCapacity", "Small ship hold"),
("specialMediumShipHoldCapacity", "Medium ship hold"),
("specialLargeShipHoldCapacity", "Large ship hold"),
("specialIndustrialShipHoldCapacity", "Industrial ship hold"),
("specialOreHoldCapacity", "Ore hold"),
("specialMineralHoldCapacity", "Mineral hold"),
("specialMaterialBayCapacity", "Material bay"),
("specialGasHoldCapacity", "Gas hold"),
("specialSalvageHoldCapacity", "Salvage hold"),
("specialCommandCenterHoldCapacity", "Command center hold"),
("specialPlanetaryCommoditiesHoldCapacity", "Planetary goods hold"),
("specialQuafeHoldCapacity", "Quafe hold")
))
cargoValues = {
"main": lambda: fit.ship.getModifiedItemAttr("capacity"),
"fleetHangarCapacity": lambda: fit.ship.getModifiedItemAttr("fleetHangarCapacity"),
"shipMaintenanceBayCapacity": lambda: fit.ship.getModifiedItemAttr("shipMaintenanceBayCapacity"),
"specialAmmoHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialAmmoHoldCapacity"),
"specialFuelBayCapacity": lambda: fit.ship.getModifiedItemAttr("specialFuelBayCapacity"),
"specialShipHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialShipHoldCapacity"),
"specialSmallShipHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialSmallShipHoldCapacity"),
"specialMediumShipHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialMediumShipHoldCapacity"),
"specialLargeShipHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialLargeShipHoldCapacity"),
"specialIndustrialShipHoldCapacity": lambda: fit.ship.getModifiedItemAttr(
"specialIndustrialShipHoldCapacity"),
"specialOreHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialOreHoldCapacity"),
"specialMineralHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialMineralHoldCapacity"),
"specialMaterialBayCapacity": lambda: fit.ship.getModifiedItemAttr("specialMaterialBayCapacity"),
"specialGasHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialGasHoldCapacity"),
"specialSalvageHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialSalvageHoldCapacity"),
"specialCommandCenterHoldCapacity": lambda: fit.ship.getModifiedItemAttr(
"specialCommandCenterHoldCapacity"),
"specialPlanetaryCommoditiesHoldCapacity": lambda: fit.ship.getModifiedItemAttr(
"specialPlanetaryCommoditiesHoldCapacity"),
"specialQuafeHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialQuafeHoldCapacity")
}
stats = (("labelTargets", {"main": lambda: fit.maxTargets}, 3, 0, 0, ""),
("labelRange", {"main": lambda: fit.maxTargetRange / 1000}, 3, 0, 0, "km"),
("labelScanRes", {"main": lambda: fit.ship.getModifiedItemAttr("scanResolution")}, 3, 0, 0, "mm"),
("labelSensorStr", {"main": lambda: fit.scanStrength}, 3, 0, 0, ""),
("labelCtrlRange", {"main": lambda: fit.extraAttributes["droneControlRange"] / 1000}, 3, 0, 0, "km"),
("labelFullSpeed", {"main": lambda: fit.maxSpeed}, 3, 0, 0, "m/s"),
("labelFullAlignTime", {"main": lambda: fit.alignTime}, 3, 0, 0, "s"),
("labelFullSigRadius", {"main": lambda: fit.ship.getModifiedItemAttr("signatureRadius")}, 3, 0, 9, ""),
("labelFullWarpSpeed", {"main": lambda: fit.warpSpeed}, 3, 0, 0, "AU/s"),
("labelFullCargo", cargoValues, 4, 0, 9, "m\u00B3"))
counter = 0
RADII = [("Pod", 25), ("Interceptor", 33), ("Frigate", 38),
("Destroyer", 83), ("Cruiser", 130),
("Battlecruiser", 265), ("Battleship", 420),
("Carrier", 3000)]
for labelName, valueDict, prec, lowest, highest, unit in stats:
label = getattr(self, labelName)
newValues = {}
for valueAlias, value in list(valueDict.items()):
value = value() if fit is not None else 0
value = value if value is not None else 0
newValues[valueAlias] = value
if self._cachedValues[counter] != newValues:
mainValue = newValues["main"]
otherValues = dict((k, newValues[k]) for k in [k for k in newValues if k != "main"])
if labelName == "labelFullCargo":
# Get sum of all cargoholds except for maintenance bay
additionalCargo = sum(otherValues.values())
if additionalCargo > 0:
label.SetLabel("%s+%s %s" % (formatAmount(mainValue, prec, lowest, highest),
formatAmount(additionalCargo, prec, lowest, highest),
unit))
else:
label.SetLabel("%s %s" % (formatAmount(mainValue, prec, lowest, highest), unit))
else:
label.SetLabel("%s %s" % (formatAmount(mainValue, prec, lowest, highest), unit))
# Tooltip stuff
if fit:
if labelName == "labelScanRes":
lockTime = "%s\n" % "Lock Times".center(30)
for size, radius in RADII:
left = "%.1fs" % fit.calculateLockTime(radius)
right = "%s [%d]" % (size, radius)
lockTime += "%5s\t%s\n" % (left, right)
label.SetToolTip(wx.ToolTip(lockTime))
elif labelName == "labelFullSigRadius":
label.SetToolTip(wx.ToolTip("Probe Size: %.3f" % (fit.probeSize or 0)))
elif labelName == "labelFullWarpSpeed":
maxWarpDistance = "Max Warp Distance: %.1f AU" % fit.maxWarpDistance
if fit.ship.getModifiedItemAttr("warpScrambleStatus"):
warpScrambleStatus = "Warp Core Strength: %.1f" % (fit.ship.getModifiedItemAttr("warpScrambleStatus") * -1)
else:
warpScrambleStatus = "Warp Core Strength: %.1f" % 0
label.SetToolTip(wx.ToolTip("%s\n%s" % (maxWarpDistance, warpScrambleStatus)))
elif labelName == "labelSensorStr":
if fit.jamChance > 0:
label.SetToolTip(
wx.ToolTip("Type: %s\n%.1f%% Chance of Jam" % (fit.scanType, fit.jamChance)))
else:
label.SetToolTip(wx.ToolTip("Type: %s" % fit.scanType))
elif labelName == "labelFullAlignTime":
alignTime = "Align:\t%.3fs" % mainValue
mass = 'Mass:\t{:,.0f}kg'.format(fit.ship.getModifiedItemAttr("mass"))
agility = "Agility:\t%.3fx" % (fit.ship.getModifiedItemAttr("agility") or 0)
label.SetToolTip(wx.ToolTip("%s\n%s\n%s" % (alignTime, mass, agility)))
elif labelName == "labelFullCargo":
tipLines = ["Cargohold: {:,.2f}m\u00B3 / {:,.2f}m\u00B3".format(fit.cargoBayUsed, newValues["main"])]
for attrName, tipAlias in list(cargoNamesOrder.items()):
if newValues[attrName] > 0:
tipLines.append("{}: {:,.2f}m\u00B3".format(tipAlias, newValues[attrName]))
label.SetToolTip(wx.ToolTip("\n".join(tipLines)))
else:
label.SetToolTip(wx.ToolTip("%.1f" % mainValue))
else:
label.SetToolTip(wx.ToolTip(""))
self._cachedValues[counter] = newValues
elif labelName == "labelFullWarpSpeed":
if fit:
maxWarpDistance = "Max Warp Distance: %.1f AU" % fit.maxWarpDistance
if fit.ship.getModifiedItemAttr("warpScrambleStatus"):
warpScrambleStatus = "Warp Core Strength: %.1f" % (fit.ship.getModifiedItemAttr("warpScrambleStatus") * -1)
else:
warpScrambleStatus = "Warp Core Strength: %.1f" % 0
label.SetToolTip(wx.ToolTip("%s\n%s" % (maxWarpDistance, warpScrambleStatus)))
elif labelName == "labelSensorStr":
if fit:
if fit.jamChance > 0:
label.SetToolTip(wx.ToolTip("Type: %s\n%.1f%% Chance of Jam" % (fit.scanType, fit.jamChance)))
else:
label.SetToolTip(wx.ToolTip("Type: %s" % fit.scanType))
else:
label.SetToolTip(wx.ToolTip(""))
elif labelName == "labelFullCargo":
if fit:
cachedCargo = self._cachedValues[counter]
# if you add stuff to cargo, the capacity doesn't change and thus it is still cached
# This assures us that we force refresh of cargo tooltip
tipLines = ["Cargohold: {:,.2f}m\u00B3 / {:,.2f}m\u00B3".format(fit.cargoBayUsed, cachedCargo["main"])]
for attrName, tipAlias in list(cargoNamesOrder.items()):
if cachedCargo[attrName] > 0:
tipLines.append("{}: {:,.2f}m\u00B3".format(tipAlias, cachedCargo[attrName]))
label.SetToolTip(wx.ToolTip("\n".join(tipLines)))
else:
label.SetToolTip(wx.ToolTip(""))
counter += 1
self.panel.Layout()
self.headerPanel.Layout()
TargetingMiscViewFull.register()

View File

@@ -258,6 +258,35 @@ class Miscellanea(ViewColumn):
tooltip = "{0} disruption".format(formatList(ttEntries)).capitalize()
return text, tooltip
elif itemGroup in ("Gyrostabilizer", "Magnetic Field Stabilizer", "Heat Sink", "Ballistic Control system", "Entropic Radiation Sink"):
attrMap = {
"Gyrostabilizer": ("damageMultiplier", "speedMultiplier", "Projectile weapon"),
"Magnetic Field Stabilizer": ("damageMultiplier", "speedMultiplier", "Hybrid weapon"),
"Heat Sink": ("damageMultiplier", "speedMultiplier", "Energy weapon"),
"Ballistic Control system": ("missileDamageMultiplierBonus", "speedMultiplier", "Missile"),
"Entropic Radiation Sink": ("damageMultiplier", "speedMultiplier", "Precursor weapon")}
dmgAttr, rofAttr, weaponName = attrMap[itemGroup]
dmg = stuff.getModifiedItemAttr(dmgAttr)
rof = stuff.getModifiedItemAttr(rofAttr)
if not dmg or not rof:
return "", None
texts = []
tooltips = []
cumulative = (dmg / rof - 1) * 100
texts.append("{}%".format(formatAmount(cumulative, 3, 0, 3, forceSign=True)))
tooltips.append("{} DPS boost".format(weaponName))
droneDmg = stuff.getModifiedItemAttr("droneDamageBonus")
if droneDmg:
texts.append("{}%".format(formatAmount(droneDmg, 3, 0, 3, forceSign=True)))
tooltips.append("drone DPS boost".format(weaponName))
return ' | '.join(texts), ' and '.join(tooltips)
elif itemGroup == "Drone Damage Modules":
dmg = stuff.getModifiedItemAttr("droneDamageBonus")
if not dmg:
return
text = "{}%".format(formatAmount(dmg, 3, 0, 3, forceSign=True))
tooltip = "Drone DPS boost"
return text, tooltip
elif itemGroup in ("ECM", "Burst Jammer", "Burst Projectors"):
grav = stuff.getModifiedItemAttr("scanGravimetricStrengthBonus")
ladar = stuff.getModifiedItemAttr("scanLadarStrengthBonus")
@@ -499,9 +528,9 @@ class Miscellanea(ViewColumn):
text = "{0}/s".format(formatAmount(capPerSec, 3, 0, 3))
tooltip = "Energy neutralization per second"
return text, tooltip
elif itemGroup == "Micro Jump Drive":
elif itemGroup in ("Micro Jump Drive", "Micro Jump Field Generators"):
cycleTime = stuff.getModifiedItemAttr("duration") / 1000
text = "{0}s".format(cycleTime)
text = "{0}s".format(formatAmount(cycleTime, 3, 0, 3))
tooltip = "Spoolup time"
return text, tooltip
elif itemGroup in ("Siege Module", "Cynosural Field Generator"):

View File

@@ -42,6 +42,7 @@ from gui.utils.staticHelpers import DragDropHelper
from service.fit import Fit
from service.market import Market
from config import slotColourMap
from gui.fitCommands.helpers import getSimilarModPositions
pyfalog = Logger(__name__)
@@ -74,8 +75,8 @@ class FitSpawner(gui.multiSwitch.TabSpawner):
sFit = Fit.getInstance()
openFitInNew = sFit.serviceFittingOptions["openFitInNew"]
mstate = wx.GetMouseState()
if from_import or (not openFitInNew and mstate.CmdDown()) or startup or (openFitInNew and not mstate.CmdDown()):
modifierKey = mstate.GetModifiers() == wx.MOD_CONTROL
if from_import or (not openFitInNew and modifierKey) or startup or (openFitInNew and not modifierKey):
self.multiSwitch.AddPage()
view = self.multiSwitch.GetSelectedPage()
@@ -163,7 +164,7 @@ class FittingView(d.Display):
self.hoveredRow = None
self.hoveredColumn = None
self.Bind(wx.EVT_KEY_UP, self.kbEvent)
self.Bind(wx.EVT_KEY_DOWN, self.kbEvent)
self.Bind(wx.EVT_LEFT_DOWN, self.click)
self.Bind(wx.EVT_RIGHT_DOWN, self.click)
self.Bind(wx.EVT_MIDDLE_DOWN, self.click)
@@ -189,7 +190,7 @@ class FittingView(d.Display):
self.hoveredRow = row
self.hoveredColumn = col
if row != -1 and row not in self.blanks and col != -1 and col < len(self.DEFAULT_COLS):
mod = self.mods[self.GetItemData(row)]
mod = self.mods[row]
tooltip = self.activeColumns[col].getToolTip(mod)
if tooltip is not None:
self.SetToolTip(tooltip)
@@ -207,7 +208,6 @@ class FittingView(d.Display):
data[0] is hard-coded str of originating source
data[1] is typeID or index of data we want to manipulate
"""
if data[0] == "fitting":
self.swapItems(x, y, int(data[1]))
elif data[0] == "cargo":
@@ -237,44 +237,57 @@ class FittingView(d.Display):
return self.activeFitID
def startDrag(self, event):
row = event.GetIndex()
srcRow = event.GetIndex()
if row != -1 and row not in self.blanks and isinstance(self.mods[row], Module) and not self.mods[row].isEmpty:
data = wx.TextDataObject()
dataStr = "fitting:" + str(self.mods[row].modPosition)
data.SetText(dataStr)
if srcRow == -1:
return
if srcRow in self.blanks:
return
try:
mod = self.mods[srcRow]
except IndexError:
return
if not isinstance(self.mods[srcRow], Module):
return
if mod.isEmpty:
return
fit = Fit.getInstance().getFit(self.activeFitID)
if mod not in fit.modules:
return
dropSource = wx.DropSource(self)
dropSource.SetData(data)
DragDropHelper.data = dataStr
dropSource.DoDragDrop()
self.unselectAll()
self.Select(srcRow, True)
data = wx.TextDataObject()
dataStr = "fitting:" + str(fit.modules.index(mod))
data.SetText(dataStr)
dropSource = wx.DropSource(self)
dropSource.SetData(data)
DragDropHelper.data = dataStr
dropSource.DoDragDrop()
def getSelectedMods(self):
sel = []
row = self.GetFirstSelected()
while row != -1:
mod = self.mods[self.GetItemData(row)]
mods = []
for row in self.getSelectedRows():
try:
mod = self.mods[row]
except IndexError:
continue
if mod and not isinstance(mod, Rack):
sel.append(mod)
row = self.GetNextSelected(row)
return sel
mods.append(mod)
return mods
def kbEvent(self, event):
keycode = event.GetKeyCode()
if keycode == wx.WXK_DELETE or keycode == wx.WXK_NUMPAD_DELETE:
row = self.GetFirstSelected()
modules = []
while row != -1:
if row not in self.blanks:
mod = self.mods[row]
if isinstance(mod, Module) and not mod.isEmpty:
modules.append(self.mods[row])
self.Select(row, 0)
row = self.GetNextSelected(row)
mstate = wx.GetMouseState()
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
self.unselectAll()
elif keycode == 65 and mstate.GetModifiers() == wx.MOD_CONTROL:
self.selectAll()
elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and mstate.GetModifiers() == wx.MOD_NONE:
modules = [m for m in self.getSelectedMods() if not m.isEmpty]
self.removeModule(modules)
event.Skip()
def fitRemoved(self, event):
@@ -358,35 +371,54 @@ class FittingView(d.Display):
fitID = self.activeFitID
if fitID is not None:
item = Market.getInstance().getItem(itemID, eager='group.category')
if item is None or not (item.isModule or item.isSubsystem):
if item is None:
event.Skip()
return
batchOp = wx.GetMouseState().GetModifiers() == wx.MOD_ALT and getattr(event, 'allowBatch', None) is not False
# If we've selected ammo, then apply to the selected module(s)
if item.isCharge:
# If we've selected ammo, then apply to the selected module(s)
modules = []
sel = self.GetFirstSelected()
while sel != -1 and sel not in self.blanks:
mod = self.mods[self.GetItemData(sel)]
if isinstance(mod, Module) and not mod.isEmpty:
modules.append(self.mods[self.GetItemData(sel)])
sel = self.GetNextSelected(sel)
if len(modules) > 0:
self.mainFrame.command.Submit(cmd.GuiChangeLocalModuleChargesCommand(fitID, modules, itemID))
else:
self.mainFrame.command.Submit(cmd.GuiAddLocalModuleCommand(fitID, itemID))
positions = []
fit = Fit.getInstance().getFit(fitID)
if batchOp:
for position, mod in enumerate(fit.modules):
if isinstance(mod, Module) and not mod.isEmpty:
positions.append(position)
else:
for mod in self.getSelectedMods():
if mod.isEmpty or mod not in fit.modules:
continue
positions.append(fit.modules.index(mod))
if len(positions) > 0:
self.mainFrame.command.Submit(cmd.GuiChangeLocalModuleChargesCommand(
fitID=fitID, positions=positions, chargeItemID=itemID))
elif (item.isModule and not batchOp) or item.isSubsystem:
self.mainFrame.command.Submit(cmd.GuiAddLocalModuleCommand(fitID=fitID, itemID=itemID))
elif item.isModule and batchOp:
self.mainFrame.command.Submit(cmd.GuiFillWithNewLocalModulesCommand(fitID=fitID, itemID=itemID))
event.Skip()
def removeItem(self, event):
"""Double Left Click - remove module"""
if event.CmdDown():
if event.GetModifiers() == wx.MOD_CONTROL:
return
row, _ = self.HitTest(event.Position)
if row != -1 and row not in self.blanks and isinstance(self.mods[row], Module):
col = self.getColumn(event.Position)
if col != self.getColIndex(State):
self.removeModule(self.mods[row])
try:
mod = self.mods[row]
except IndexError:
return
if not isinstance(mod, Module) or mod.isEmpty:
return
if event.GetModifiers() == wx.MOD_ALT:
fit = Fit.getInstance().getFit(self.activeFitID)
positions = getSimilarModPositions(fit.modules, mod)
self.mainFrame.command.Submit(cmd.GuiRemoveLocalModuleCommand(
fitID=self.activeFitID, positions=positions))
else:
self.removeModule(mod)
else:
if "wxMSW" in wx.PlatformInfo:
self.click(event)
@@ -396,19 +428,66 @@ class FittingView(d.Display):
if not isinstance(modules, list):
modules = [modules]
self.mainFrame.command.Submit(cmd.GuiRemoveLocalModuleCommand(self.activeFitID, modules))
fit = Fit.getInstance().getFit(self.activeFitID)
positions = []
for position, mod in enumerate(fit.modules):
if mod in modules:
positions.append(position)
self.mainFrame.command.Submit(cmd.GuiRemoveLocalModuleCommand(
fitID=self.activeFitID, positions=positions))
def addModule(self, x, y, itemID):
"""Add a module from the market browser (from dragging it)"""
fitID = self.mainFrame.getActiveFit()
item = Market.getInstance().getItem(itemID)
fit = Fit.getInstance().getFit(fitID)
dstRow, _ = self.HitTest((x, y))
if dstRow != -1 and dstRow not in self.blanks:
fitID = self.mainFrame.getActiveFit()
mod = self.mods[dstRow]
if not isinstance(mod, Module): # make sure we're not adding something to a T3D Mode
return
if dstRow == -1 or dstRow in self.blanks:
dstMod = None
else:
try:
dstMod = self.mods[dstRow]
except IndexError:
dstMod = None
if not isinstance(dstMod, Module):
dstMod = None
if dstMod not in fit.modules:
dstMod = None
dstPos = fit.modules.index(dstMod) if dstMod is not None else None
mstate = wx.GetMouseState()
# If we dropping on a module, try to replace, or add if replacement fails
if item.isModule and dstMod is not None and not dstMod.isEmpty:
positions = getSimilarModPositions(fit.modules, dstMod) if mstate.GetModifiers() == wx.MOD_ALT else [dstPos]
command = cmd.GuiReplaceLocalModuleCommand(fitID=fitID, itemID=itemID, positions=positions)
if not self.mainFrame.command.Submit(command):
if mstate.GetModifiers() == wx.MOD_ALT:
self.mainFrame.command.Submit(cmd.GuiFillWithNewLocalModulesCommand(fitID=fitID, itemID=itemID))
else:
self.mainFrame.command.Submit(cmd.GuiAddLocalModuleCommand(fitID=fitID, itemID=itemID))
elif item.isModule:
if mstate.GetModifiers() == wx.MOD_ALT:
self.mainFrame.command.Submit(cmd.GuiFillWithNewLocalModulesCommand(fitID=fitID, itemID=itemID))
else:
self.mainFrame.command.Submit(cmd.GuiAddLocalModuleCommand(fitID=fitID, itemID=itemID))
elif item.isSubsystem:
self.mainFrame.command.Submit(cmd.GuiAddLocalModuleCommand(fitID=fitID, itemID=itemID))
elif item.isCharge:
failoverToAll = False
positionsAll = list(range(len(fit.modules)))
if dstMod is None or dstMod.isEmpty:
positions = positionsAll
elif mstate.GetModifiers() == wx.MOD_ALT:
positions = getSimilarModPositions(fit.modules, dstMod)
failoverToAll = True
else:
positions = [fit.modules.index(dstMod)]
if len(positions) > 0:
command = cmd.GuiChangeLocalModuleChargesCommand(fitID=fitID, positions=positions, chargeItemID=itemID)
if not self.mainFrame.command.Submit(command) and failoverToAll:
self.mainFrame.command.Submit(cmd.GuiChangeLocalModuleChargesCommand(
fitID=fitID, positions=positionsAll, chargeItemID=itemID))
self.mainFrame.command.Submit(cmd.GuiAddLocalModuleCommand(fitID, itemID, self.mods[dstRow].modPosition))
def swapCargo(self, x, y, cargoItemID):
"""Swap a module from cargo to fitting window"""
@@ -420,11 +499,15 @@ class FittingView(d.Display):
if not isinstance(mod, Module):
return
self.mainFrame.command.Submit(cmd.GuiCargoToLocalModuleCommand(
fitID=self.mainFrame.getActiveFit(),
cargoItemID=cargoItemID,
modPosition=mod.modPosition,
copy=wx.GetMouseState().CmdDown()))
fitID = self.mainFrame.getActiveFit()
fit = Fit.getInstance().getFit(fitID)
if mod in fit.modules:
position = fit.modules.index(mod)
self.mainFrame.command.Submit(cmd.GuiCargoToLocalModuleCommand(
fitID=fitID,
cargoItemID=cargoItemID,
modPosition=position,
copy=wx.GetMouseState().GetModifiers() == wx.MOD_CONTROL))
def swapItems(self, x, y, srcIdx):
"""Swap two modules in fitting window"""
@@ -434,27 +517,30 @@ class FittingView(d.Display):
dstRow, _ = self.HitTest((x, y))
if dstRow != -1 and dstRow not in self.blanks:
mod1 = fit.modules[srcIdx]
mod2 = self.mods[dstRow]
try:
mod1 = fit.modules[srcIdx]
mod2 = self.mods[dstRow]
except IndexError:
return
if not isinstance(mod2, Module):
return
# can't swap modules to different racks
if mod1.slot != mod2.slot:
return
fitID = self.mainFrame.getActiveFit()
if getattr(mod2, "modPosition") is not None:
mstate = wx.GetMouseState()
if mstate.CmdDown() and mod2.isEmpty:
self.mainFrame.command.Submit(cmd.GuiCloneLocalModuleCommand(
fitID=fitID, srcPosition=srcIdx, dstPosition=mod2.modPosition))
elif not mstate.CmdDown():
self.mainFrame.command.Submit(cmd.GuiSwapLocalModulesCommand(
fitID=fitID, position1=srcIdx, position2=mod2.modPosition))
else:
if mod2 not in fit.modules:
pyfalog.error("Missing module position for: {0}", str(getattr(mod2, "ID", "Unknown")))
return
mod2Position = fit.modules.index(mod2)
mstate = wx.GetMouseState()
if mstate.GetModifiers() == wx.MOD_CONTROL | wx.MOD_ALT:
self.mainFrame.command.Submit(cmd.GuiFillWithClonedLocalModulesCommand(
fitID=self.activeFitID, position=srcIdx))
elif mstate.GetModifiers() == wx.MOD_CONTROL and mod2.isEmpty:
self.mainFrame.command.Submit(cmd.GuiCloneLocalModuleCommand(
fitID=self.activeFitID, srcPosition=srcIdx, dstPosition=mod2Position))
elif mstate.GetModifiers() == wx.MOD_NONE:
self.mainFrame.command.Submit(cmd.GuiSwapLocalModulesCommand(
fitID=self.activeFitID, position1=srcIdx, position2=mod2Position))
def generateMods(self):
"""
@@ -518,7 +604,6 @@ class FittingView(d.Display):
self.populate(self.mods)
def fitChanged(self, event):
# print('====== Fit Changed: {} {} activeFitID: {}, eventFitID: {}'.format(repr(self), str(bool(self)), self.activeFitID, event.fitID))
if not self:
event.Skip()
return
@@ -538,52 +623,52 @@ class FittingView(d.Display):
event.Skip()
def spawnMenu(self, event):
if self.activeFitID is None or self.getColumn(self.ScreenToClient(event.Position)) == self.getColIndex(State):
if self.activeFitID is None or self.getColumn(self.screenToClientFixed(event.Position)) == self.getColIndex(State):
return
sMkt = Market.getInstance()
selection = []
sel = self.GetFirstSelected()
contexts = []
while sel != -1 and sel not in self.blanks:
mod = self.mods[self.GetItemData(sel)]
for mod in self.getSelectedMods():
# Test if this is a mode, which is a special snowflake of a Module
if isinstance(mod, Mode):
srcContext = "fittingMode"
itemContext = "Tactical Mode"
fullContext = (srcContext, itemContext)
if srcContext not in tuple(fCtxt[0] for fCtxt in contexts):
contexts.append(fullContext)
selection.append(mod)
elif not mod.isEmpty:
srcContext = "fittingModule"
itemContext = sMkt.getCategoryByItem(mod.item).name
selection.append(mod)
fit = Fit.getInstance().getFit(self.activeFitID)
clickedPos = self.getRowByAbs(event.Position)
mainMod = None
if clickedPos != -1:
try:
mod = self.mods[clickedPos]
except IndexError:
pass
else:
if mod is not None and (mod in fit.modules or mod is fit.mode):
mainMod = mod
sMkt = Market.getInstance()
contexts = []
if isinstance(mainMod, Module) and not mainMod.isEmpty:
srcContext = "fittingModule"
itemContext = sMkt.getCategoryByItem(mainMod.item).name
fullContext = (srcContext, itemContext)
if srcContext not in tuple(fCtx[0] for fCtx in contexts):
contexts.append(fullContext)
if mainMod.charge is not None:
srcContext = "fittingCharge"
itemContext = sMkt.getCategoryByItem(mainMod.charge).name
fullContext = (srcContext, itemContext)
if srcContext not in tuple(fCtxt[0] for fCtxt in contexts):
contexts.append(fullContext)
if mod.charge is not None:
srcContext = "fittingCharge"
itemContext = sMkt.getCategoryByItem(mod.charge).name
fullContext = (srcContext, itemContext)
if srcContext not in tuple(fCtxt[0] for fCtxt in contexts):
contexts.append(fullContext)
selection.append(mod)
sel = self.GetNextSelected(sel)
sFit = Fit.getInstance()
fit = sFit.getFit(self.activeFitID)
elif isinstance(mainMod, Mode):
srcContext = "fittingMode"
itemContext = "Tactical Mode"
fullContext = (srcContext, itemContext)
if srcContext not in tuple(fCtx[0] for fCtx in contexts):
contexts.append(fullContext)
contexts.append(("fittingShip", "Ship" if not fit.isStructure else "Citadel"))
menu = ContextMenu.getMenu(selection, *contexts)
menu = ContextMenu.getMenu(mainMod, selection, *contexts)
self.PopupMenu(menu)
def click(self, event):
@@ -595,34 +680,53 @@ class FittingView(d.Display):
change State
"""
row, _, col = self.HitTestSubItem(event.Position)
clickedRow, _, col = self.HitTestSubItem(event.Position)
# only do State column and ignore invalid rows
if row != -1 and row not in self.blanks and col == self.getColIndex(State):
sel = []
curr = self.GetFirstSelected()
if clickedRow != -1 and clickedRow not in self.blanks and col == self.getColIndex(State):
selectedRows = []
currentRow = self.GetFirstSelected()
while curr != -1 and row not in self.blanks:
sel.append(curr)
curr = self.GetNextSelected(curr)
while currentRow != -1 and clickedRow not in self.blanks:
selectedRows.append(currentRow)
currentRow = self.GetNextSelected(currentRow)
if row not in sel:
mods = [self.mods[self.GetItemData(row)]]
if clickedRow not in selectedRows:
try:
selectedMods = [self.mods[clickedRow]]
except IndexError:
return
else:
mods = self.getSelectedMods()
selectedMods = self.getSelectedMods()
click = "ctrl" if event.GetModifiers() == wx.MOD_CONTROL or event.middleIsDown else "right" if event.GetButton() == 3 else "left"
try:
mainMod = self.mods[clickedRow]
except IndexError:
return
if mainMod.isEmpty:
return
fitID = self.mainFrame.getActiveFit()
ctrl = event.cmdDown or event.middleIsDown
click = "ctrl" if ctrl is True else "right" if event.GetButton() == 3 else "left"
fit = Fit.getInstance().getFit(fitID)
if mainMod not in fit.modules:
return
mainPosition = fit.modules.index(mainMod)
if event.GetModifiers() == wx.MOD_ALT:
positions = getSimilarModPositions(fit.modules, mainMod)
else:
positions = []
for position, mod in enumerate(fit.modules):
if mod in selectedMods:
positions.append(position)
self.mainFrame.command.Submit(cmd.GuiChangeLocalModuleStatesCommand(
fitID=fitID,
mainPosition=self.mods[self.GetItemData(row)].modPosition,
positions=[mod.modPosition for mod in mods],
mainPosition=mainPosition,
positions=positions,
click=click))
# update state tooltip
tooltip = self.activeColumns[col].getToolTip(self.mods[self.GetItemData(row)])
tooltip = self.activeColumns[col].getToolTip(self.mods[clickedRow])
if tooltip:
self.SetToolTip(tooltip)
@@ -654,21 +758,25 @@ class FittingView(d.Display):
self.SetItemBackgroundColour(i, self.GetBackgroundColour())
# only consider changing color if we're dealing with a Module
if type(mod) is Module:
hasRestrictionOverriden = getattr(mod, 'restrictionOverridden', None)
# If module had broken fitting restrictions but now doesn't,
# ensure it is now valid, and remove restrictionOverridden
# variable. More in #1519
if not fit.ignoreRestrictions and hasRestrictionOverriden:
clean = False
if mod.fits(fit, False):
if not mod.hardpoint:
clean = True
elif fit.getHardpointsFree(mod.hardpoint) >= 0:
clean = True
if clean:
del mod.restrictionOverridden
hasRestrictionOverriden = not hasRestrictionOverriden
if isinstance(mod, Module):
hasRestrictionOverriden = False
if not mod.isEmpty:
fits = mod.fits(fit, False)
hasRestrictionOverriden = getattr(mod, 'restrictionOverridden', None)
# If module had broken fitting restrictions but now doesn't,
# ensure it is now valid, and remove restrictionOverridden
# variable. More in #1519
if not fit.ignoreRestrictions and hasRestrictionOverriden:
clean = False
if fits:
if not mod.hardpoint:
clean = True
elif fit.getHardpointsFree(mod.hardpoint) >= 0:
clean = True
if clean:
del mod.restrictionOverridden
hasRestrictionOverriden = not hasRestrictionOverriden
if slotMap[mod.slot] or hasRestrictionOverriden: # Color too many modules as red
self.SetItemBackgroundColour(i, wx.Colour(204, 51, 51))

View File

@@ -11,6 +11,7 @@ from service.market import Market
class BaseImplantEditorView(wx.Panel):
def addMarketViewImage(self, iconFile):
if iconFile is None:
return -1
@@ -37,13 +38,6 @@ class BaseImplantEditorView(wx.Panel):
availableSizer.Add(self.searchBox, 0, wx.EXPAND)
availableSizer.Add(self.itemView, 1, wx.EXPAND)
'''
self.availableImplantsSearch = wx.SearchCtrl(self, wx.ID_ANY, style=wx.TE_PROCESS_ENTER)
self.availableImplantsSearch.ShowCancelButton(True)
availableSizer.Add(self.availableImplantsSearch, 0, wx.BOTTOM | wx.EXPAND, 2)
'''
self.availableImplantsTree = wx.TreeCtrl(self, wx.ID_ANY, style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT)
root = self.availableRoot = self.availableImplantsTree.AddRoot("Available")
self.availableImplantsImageList = wx.ImageList(16, 16)
@@ -202,7 +196,6 @@ class BaseImplantEditorView(wx.Panel):
class AvailableImplantsView(d.Display):
DEFAULT_COLS = ["attr:implantness",
"Base Icon",
"Base Name"]
def __init__(self, parent):
@@ -212,9 +205,7 @@ class AvailableImplantsView(d.Display):
class ItemView(d.Display):
DEFAULT_COLS = ["Base Icon",
"Base Name",
"attr:power,,,True",
"attr:cpu,,,True"]
"Base Name"]
def __init__(self, parent):
d.Display.__init__(self, parent)
@@ -260,6 +251,7 @@ class ItemView(d.Display):
self.Show()
self.parent.Layout()
items = [i for i in items if i.group.name != 'Booster']
self.items = sorted(list(items), key=lambda i: i.name)
self.update(self.items)

View File

@@ -149,7 +149,7 @@ class CharacterEntityEditor(EntityEditor):
class CharacterEditor(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, id=wx.ID_ANY, title="pyfa: Character Editor", pos=wx.DefaultPosition,
wx.Frame.__init__(self, parent, id=wx.ID_ANY, title="Character Editor", pos=wx.DefaultPosition,
size=wx.Size(640, 600), style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER | wx.FRAME_FLOAT_ON_PARENT)
i = wx.Icon(BitmapLoader.getBitmap("character_small", "gui"))
@@ -208,6 +208,7 @@ class CharacterEditor(wx.Frame):
self.Centre(wx.BOTH)
self.Bind(wx.EVT_CLOSE, self.closeEvent)
self.Bind(wx.EVT_CHAR_HOOK, self.kbEvent)
self.Bind(GE.CHAR_LIST_UPDATED, self.refreshCharacterList)
self.entityEditor.Bind(wx.EVT_CHOICE, self.charChanged)
@@ -253,8 +254,17 @@ class CharacterEditor(wx.Frame):
sChr.revertCharacter(char.ID)
wx.PostEvent(self, GE.CharListUpdated())
def kbEvent(self, event):
keycode = event.GetKeyCode()
if keycode == wx.WXK_ESCAPE:
self.closeWindow()
return
event.Skip()
def closeEvent(self, event):
# del self.disableWin
self.closeWindow()
def closeWindow(self):
wx.PostEvent(self.mainFrame, GE.CharListUpdated())
self.Destroy()
@@ -404,31 +414,18 @@ class SkillTreeView(wx.Panel):
self.charEditor.entityEditor.Bind(wx.EVT_CHOICE, self.charChanged)
self.charEditor.Bind(GE.CHAR_LIST_UPDATED, self.populateSkillTree)
srcContext = "skillItem"
itemContext = "Skill"
context = (srcContext, itemContext)
self.statsMenu = ContextMenu.getMenu(None, context)
self.levelChangeMenu = ContextMenu.getMenu(None, context) or wx.Menu()
self.levelChangeMenu.AppendSeparator()
# Context menu stuff
self.idUnlearned = wx.NewId()
self.levelIds = {}
idUnlearned = wx.NewId()
self.levelIds[idUnlearned] = "Not learned"
self.levelChangeMenu.Append(idUnlearned, "Unlearn")
self.idLevels = {}
self.levelIds[self.idUnlearned] = "Not learned"
for level in range(6):
id = wx.NewId()
self.levelIds[id] = level
self.levelChangeMenu.Append(id, "Level %d" % level)
self.levelChangeMenu.AppendSeparator()
self.idLevels[level] = id
self.revertID = wx.NewId()
self.levelChangeMenu.Append(self.revertID, "Revert")
self.saveID = wx.NewId()
self.levelChangeMenu.Append(self.saveID, "Save")
self.levelChangeMenu.Bind(wx.EVT_MENU, self.changeLevel)
self.SetSizer(pmainSizer)
# This cuases issues with GTK, see #1866
@@ -597,15 +594,27 @@ class SkillTreeView(wx.Panel):
if thing:
return
char = self.charEditor.entityEditor.getActiveEntity()
sMkt = Market.getInstance()
id = self.skillTreeListCtrl.GetItemData(item)[1]
eveItem = Market.getInstance().getItem(id)
srcContext = "skillItem"
itemContext = "Skill"
context = (srcContext, itemContext)
menu = ContextMenu.getMenu(eveItem, [eveItem], context)
char = self.charEditor.entityEditor.getActiveEntity()
if char.name not in ("All 0", "All 5"):
self.levelChangeMenu.selection = sMkt.getItem(id)
self.PopupMenu(self.levelChangeMenu)
else:
self.statsMenu.selection = sMkt.getItem(id)
self.PopupMenu(self.statsMenu)
menu.AppendSeparator()
menu.Append(self.idUnlearned, "Unlearn")
for level in range(6):
menu.Append(self.idLevels[level], "Level %d" % level)
# Doesn't make sense to have these menu items here, as they do not revert skill changes
# done in an editor - because these changes are persisted anyway
# menu.AppendSeparator()
# menu.Append(self.revertID, "Revert")
# menu.Append(self.saveID, "Save")
menu.Bind(wx.EVT_MENU, self.changeLevel)
self.PopupMenu(menu)
def changeLevel(self, event):
level = self.levelIds.get(event.Id)
@@ -705,7 +714,8 @@ class ImplantEditorView(BaseImplantEditorView):
context = (("implantEditor",),)
# fuck good coding practices, passing a pointer to the character editor here for [reasons] =D
# (see implantSets context class for info)
menu = ContextMenu.getMenu((self.Parent.Parent,), *context)
item = self.Parent.Parent
menu = ContextMenu.getMenu(item, (item,), *context)
if menu:
self.PopupMenu(menu)

View File

@@ -64,8 +64,9 @@ class PageChanging(_PageChanging, NotebookTabChangeEvent, VetoAble):
class PageChanged(_PageChanged, NotebookTabChangeEvent):
def __init__(self, old, new):
_PageChanged.__init__(self)
def __init__(self, old, new, *args, **kwargs):
_PageChanged.__init__(self, *args, **kwargs)
NotebookTabChangeEvent.__init__(self, old, new)
@@ -236,13 +237,16 @@ class ChromeNotebook(wx.Panel):
self.tabs_container.DisableTab(idx, toggle)
def SetSelection(self, page):
def SetSelection(self, page, focus=True):
old_selection = self.GetSelection()
if old_selection != page:
self._active_page.Hide()
self._active_page = self._pages[page]
self.tabs_container.SetSelected(page)
self.ShowActive()
if focus:
self._active_page.SetFocus()
wx.PostEvent(self, PageChanged(old_selection, page))
def DeletePage(self, n):
page = self._pages[n]

View File

@@ -17,14 +17,18 @@
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
# =============================================================================
from abc import ABCMeta, abstractmethod
# noinspection PyPackageRequirements
import wx
from logbook import Logger
pyfalog = Logger(__name__)
class ContextMenu(object):
class ContextMenu(metaclass=ABCMeta):
menus = []
_ids = [] # [wx.NewId() for x in xrange(200)] # init with decent amount
_idxid = -1
@@ -34,20 +38,22 @@ class ContextMenu(object):
ContextMenu.menus.append(cls)
@classmethod
def hasMenu(cls, selection, *fullContexts):
def hasMenu(cls, mainItem, selection, *fullContexts):
for i, fullContext in enumerate(fullContexts):
srcContext = fullContext[0]
for menuHandler in cls.menus:
m = menuHandler()
if m.display(srcContext, selection):
if m._baseDisplay(srcContext, mainItem, selection):
return True
return False
@classmethod
def getMenu(cls, selection, *fullContexts):
def getMenu(cls, mainItem, selection, *fullContexts):
"""
getMenu returns a menu that is used with wx.PopupMenu.
mainItem: usually, provides item which was clicked. Useful for single-
item actions when user has multiple items selected
selection: provides a list of what was selected. If only 1 item was
selected, it's is a 1-item list or tuple. Can also be None for
contexts without selection, such as statsPane or projected view
@@ -67,6 +73,7 @@ class ContextMenu(object):
rootMenu = wx.Menu()
rootMenu.info = {}
rootMenu.selection = (selection,) if not hasattr(selection, "__iter__") else selection
rootMenu.mainItem = mainItem
empty = True
for i, fullContext in enumerate(fullContexts):
display_amount = 0
@@ -78,14 +85,14 @@ class ContextMenu(object):
for menuHandler in cls.menus:
# loop through registered menus
m = menuHandler()
if m.display(srcContext, selection):
if m._baseDisplay(srcContext, mainItem, selection):
display_amount += 1
texts = m.getText(itemContext, selection)
texts = m._baseGetText(itemContext, mainItem, selection)
if isinstance(texts, str):
texts = (texts,)
bitmap = m.getBitmap(srcContext, selection)
bitmap = m._baseGetBitmap(srcContext, mainItem, selection)
multiple = not isinstance(bitmap, wx.Bitmap)
for it, text in enumerate(texts):
id = cls.nextID()
@@ -93,7 +100,7 @@ class ContextMenu(object):
rootItem = wx.MenuItem(rootMenu, id, text, kind=wx.ITEM_NORMAL if m.checked is None else wx.ITEM_CHECK)
rootMenu.info[id] = (m, fullContext, it)
sub = m.getSubMenu(srcContext, selection, rootMenu, it, rootItem)
sub = m._baseGetSubMenu(srcContext, mainItem, selection, rootMenu, it, rootItem)
if sub is None:
# if there is no sub menu, bind the handler to the rootItem
@@ -136,7 +143,7 @@ class ContextMenu(object):
debug_end = len(cls._ids)
if debug_end - debug_start:
pyfalog.debug("{0} new IDs created for this menu", (debug_end - debug_start))
pyfalog.debug("{} new IDs created for this menu".format(debug_end - debug_start))
return rootMenu if empty is False else None
@@ -147,22 +154,14 @@ class ContextMenu(object):
if stuff is not None:
menuHandler, context, i = stuff
selection = menu.selection
mainItem = menu.mainItem
if not hasattr(selection, "__iter__"):
selection = (selection,)
menuHandler.activate(context, selection, i)
menuHandler._baseActivate(context, mainItem, selection, i)
else:
event.Skip()
def display(self, context, selection):
raise NotImplementedError()
def activate(self, fullContext, selection, i):
return None
def getSubMenu(self, context, selection, rootMenu, i, pitem):
return None
@classmethod
def nextID(cls):
"""
@@ -179,19 +178,6 @@ class ContextMenu(object):
return cls._ids[cls._idxid]
def getText(self, context, selection):
"""
getText should be implemented in child classes, and should return either
a string that will make up a menu item label or a list of strings which
will make numerous menu items.
These menu items will be added to the root menu
"""
raise NotImplementedError()
def getBitmap(self, context, selection):
return None
@property
def checked(self):
'''If menu item is toggleable, this should return bool value'''
@@ -202,4 +188,208 @@ class ContextMenu(object):
'''If menu item is enabled. Allows an item to display, but not be selected'''
return True
@abstractmethod
def _baseDisplay(self, context, mainItem, selection):
raise NotImplementedError
@abstractmethod
def _baseGetBitmap(self, context, mainItem, selection):
raise NotImplementedError
@abstractmethod
def _baseGetText(self, context, mainItem, selection):
"""
getText should be implemented in child classes, and should return either
a string that will make up a menu item label or a list of strings which
will make numerous menu items.
These menu items will be added to the root menu
"""
raise NotImplementedError
@abstractmethod
def _baseGetSubMenu(self, context, mainItem, selection, rootMenu, i, pitem):
raise NotImplementedError
@abstractmethod
def _baseActivate(self, fullContext, mainItem, selection, i):
raise NotImplementedError
class ContextMenuUnconditional(ContextMenu, metaclass=ABCMeta):
"""
Should be used for context menus which do not depend on which item
has been clicked and what current selection is.
"""
@abstractmethod
def display(self, context):
raise NotImplementedError
def getBitmap(self, context):
return
@abstractmethod
def getText(self, context):
raise NotImplementedError
def getSubMenu(self, context, rootMenu, i, pitem):
return
def activate(self, fullContext, i):
return
def _baseDisplay(self, context, mainItem, selection):
return self.display(context)
def _baseGetBitmap(self, context, mainItem, selection):
return self.getBitmap(context)
def _baseGetText(self, context, mainItem, selection):
return self.getText(context)
def _baseGetSubMenu(self, context, mainItem, selection, rootMenu, i, pitem):
return self.getSubMenu(context, rootMenu, i, pitem)
def _baseActivate(self, fullContext, mainItem, selection, i):
return self.activate(fullContext, i)
class ContextMenuSingle(ContextMenu, metaclass=ABCMeta):
"""
Should be used for context menus which depend on
which item was clicked, but not on selection.
"""
@abstractmethod
def display(self, context, mainItem):
raise NotImplementedError
def getBitmap(self, context, mainItem):
return
@abstractmethod
def getText(self, context, mainItem):
raise NotImplementedError
def getSubMenu(self, context, mainItem, rootMenu, i, pitem):
return
def activate(self, fullContext, mainItem, i):
return
def _baseDisplay(self, context, mainItem, selection):
return self.display(context, mainItem)
def _baseGetBitmap(self, context, mainItem, selection):
return self.getBitmap(context, mainItem)
def _baseGetText(self, context, mainItem, selection):
return self.getText(context, mainItem)
def _baseGetSubMenu(self, context, mainItem, selection, rootMenu, i, pitem):
return self.getSubMenu(context, mainItem, rootMenu, i, pitem)
def _baseActivate(self, fullContext, mainItem, selection, i):
return self.activate(fullContext, mainItem, i)
class ContextMenuSelection(ContextMenu, metaclass=ABCMeta):
"""
Should be used for context menus which depend on which items are
selected. Item clicked is used as fallback if no selection provided.
"""
@abstractmethod
def display(self, context, selection):
raise NotImplementedError
def getBitmap(self, context, selection):
return
@abstractmethod
def getText(self, context, selection):
raise NotImplementedError
def getSubMenu(self, context, selection, rootMenu, i, pitem):
return
def activate(self, fullContext, selection, i):
return
def _baseDisplay(self, context, mainItem, selection):
selection = self.__getSelection(selection, mainItem)
return self.display(context, selection)
def _baseGetBitmap(self, context, mainItem, selection):
selection = self.__getSelection(selection, mainItem)
return self.getBitmap(context, selection)
def _baseGetText(self, context, mainItem, selection):
selection = self.__getSelection(selection, mainItem)
return self.getText(context, selection)
def _baseGetSubMenu(self, context, mainItem, selection, rootMenu, i, pitem):
selection = self.__getSelection(selection, mainItem)
return self.getSubMenu(context, selection, rootMenu, i, pitem)
def _baseActivate(self, fullContext, mainItem, selection, i):
selection = self.__getSelection(selection, mainItem)
return self.activate(fullContext, selection, i)
def __getSelection(self, selection, mainItem):
if mainItem is not None and mainItem not in selection:
selection.append(mainItem)
return selection
class ContextMenuCombined(ContextMenu, metaclass=ABCMeta):
"""
Should be used for context menus which depend on both which
item has been clicked and which items are selected.
"""
@abstractmethod
def display(self, context, mainItem, selection):
raise NotImplementedError
def getBitmap(self, context, mainItem, selection):
return
@abstractmethod
def getText(self, context, mainItem, selection):
raise NotImplementedError
def getSubMenu(self, context, mainItem, selection, rootMenu, i, pitem):
return
def activate(self, fullContext, mainItem, selection, i):
return
def _baseDisplay(self, context, mainItem, selection):
selection = self.__getSelection(selection, mainItem)
return self.display(context, mainItem, selection)
def _baseGetBitmap(self, context, mainItem, selection):
selection = self.__getSelection(selection, mainItem)
return self.getBitmap(context, mainItem, selection)
def _baseGetText(self, context, mainItem, selection):
selection = self.__getSelection(selection, mainItem)
return self.getText(context, mainItem, selection)
def _baseGetSubMenu(self, context, mainItem, selection, rootMenu, i, pitem):
selection = self.__getSelection(selection, mainItem)
return self.getSubMenu(context, mainItem, selection, rootMenu, i, pitem)
def _baseActivate(self, fullContext, mainItem, selection, i):
selection = self.__getSelection(selection, mainItem)
return self.activate(fullContext, mainItem, selection, i)
def __getSelection(self, selection, mainItem):
if mainItem is not None and mainItem not in selection:
selection.append(mainItem)
return selection
import gui.builtinContextMenus

View File

@@ -25,6 +25,7 @@ from gui.cachingImageList import CachingImageList
class Display(wx.ListCtrl):
DEFAULT_COLS = None
def __init__(self, parent, size=wx.DefaultSize, style=0):
@@ -167,12 +168,16 @@ class Display(wx.ListCtrl):
return lastFound
def deselectItems(self):
def unselectAll(self):
sel = self.GetFirstSelected()
while sel != -1:
self.SetItemState(sel, 0, wx.LIST_STATE_SELECTED | wx.LIST_STATE_FOCUSED)
self.Select(sel, False)
sel = self.GetNextSelected(sel)
def selectAll(self):
for row in range(self.GetItemCount()):
self.Select(row, True)
def populate(self, stuff):
if stuff is not None:
@@ -206,12 +211,12 @@ class Display(wx.ListCtrl):
colItem = self.GetItem(item, i)
oldText = colItem.GetText()
oldImageId = colItem.GetImage()
oldColour = colItem.GetBackgroundColour();
oldColour = colItem.GetBackgroundColour()
newText = col.getText(st)
if newText is False:
col.delayedText(st, self, colItem)
newText = "\u21bb"
newColour = self.columnBackground(colItem, st);
newColour = self.columnBackground(colItem, st)
newImageId = col.getImageId(st)
@@ -266,3 +271,29 @@ class Display(wx.ListCtrl):
def columnBackground(self, colItem, item):
return colItem.GetBackgroundColour()
def getRowByAbs(self, pointAbs):
if pointAbs == wx.Point(-1, -1):
return -1
pointRel = self.screenToClientFixed(pointAbs)
row, flags = self.HitTest(pointRel)
return row
def screenToClientFixed(self, ptScreen):
"""
Wx' ScreenToClient implementation seems to not consider header row height when
converting to screen position: https://github.com/wxWidgets/Phoenix/issues/1213
Do it ourselves here.
"""
if ptScreen == wx.Point(-1, -1):
return wx.Point(-1, -1)
ptClient = self.GetMainWindow().ScreenToClient(ptScreen)
return ptClient
def getSelectedRows(self):
rows = []
row = self.GetFirstSelected()
while row != -1:
rows.append(row)
row = self.GetNextSelected(row)
return rows

View File

@@ -21,6 +21,7 @@ pyfalog = Logger(__name__)
class EveFittings(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, id=wx.ID_ANY, title="Browse EVE Fittings", pos=wx.DefaultPosition,
size=wx.Size(550, 450), style=wx.DEFAULT_FRAME_STYLE | wx.TAB_TRAVERSAL)
@@ -69,6 +70,7 @@ class EveFittings(wx.Frame):
self.deleteBtn.Bind(wx.EVT_BUTTON, self.deleteFitting)
self.Bind(wx.EVT_CLOSE, self.OnClose)
self.Bind(wx.EVT_CHAR_HOOK, self.kbEvent)
self.statusbar = wx.StatusBar(self)
self.statusbar.SetFieldsCount()
@@ -92,12 +94,23 @@ class EveFittings(wx.Frame):
self.charChoice.SetSelection(0)
def kbEvent(self, event):
keycode = event.GetKeyCode()
if keycode == wx.WXK_ESCAPE:
self.closeWindow()
return
event.Skip()
def OnClose(self, event):
self.mainFrame.Unbind(GE.EVT_SSO_LOGOUT)
self.mainFrame.Unbind(GE.EVT_SSO_LOGIN)
self.closeWindow()
# self.cacheTimer.Stop() # must be manually stopped, otherwise crash. See https://github.com/wxWidgets/Phoenix/issues/632
event.Skip()
def closeWindow(self):
self.mainFrame.Unbind(GE.EVT_SSO_LOGOUT)
self.mainFrame.Unbind(GE.EVT_SSO_LOGIN)
self.Destroy()
def getActiveCharacter(self):
selection = self.charChoice.GetCurrentSelection()
return self.charChoice.GetClientData(selection) if selection is not None else None
@@ -186,6 +199,7 @@ class ESIExceptionHandler(object):
class ExportToEve(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, id=wx.ID_ANY, title="Export fit to EVE", pos=wx.DefaultPosition,
size=(wx.Size(350, 100)), style=wx.DEFAULT_FRAME_STYLE | wx.TAB_TRAVERSAL)
@@ -213,6 +227,7 @@ class ExportToEve(wx.Frame):
self.statusbar.SetStatusWidths([100, -1])
self.Bind(wx.EVT_CLOSE, self.OnClose)
self.Bind(wx.EVT_CHAR_HOOK, self.kbEvent)
self.SetSizer(mainSizer)
self.SetStatusBar(self.statusbar)
@@ -233,11 +248,21 @@ class ExportToEve(wx.Frame):
self.charChoice.SetSelection(0)
def kbEvent(self, event):
keycode = event.GetKeyCode()
if keycode == wx.WXK_ESCAPE:
self.closeWindow()
return
event.Skip()
def OnClose(self, event):
self.closeWindow()
event.Skip()
def closeWindow(self):
self.mainFrame.Unbind(GE.EVT_SSO_LOGOUT)
self.mainFrame.Unbind(GE.EVT_SSO_LOGIN)
event.Skip()
self.Destroy()
def getActiveCharacter(self):
selection = self.charChoice.GetCurrentSelection()
@@ -283,6 +308,7 @@ class ExportToEve(wx.Frame):
class SsoCharacterMgmt(wx.Dialog):
def __init__(self, parent):
wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title="SSO Character Management", pos=wx.DefaultPosition,
size=wx.Size(550, 250), style=wx.DEFAULT_DIALOG_STYLE)
@@ -312,6 +338,7 @@ class SsoCharacterMgmt(wx.Dialog):
self.deleteBtn.Bind(wx.EVT_BUTTON, self.delChar)
self.mainFrame.Bind(GE.EVT_SSO_LOGIN, self.ssoLogin)
self.Bind(wx.EVT_CHAR_HOOK, self.kbEvent)
self.SetSizer(mainSizer)
self.Layout()
@@ -324,6 +351,16 @@ class SsoCharacterMgmt(wx.Dialog):
self.popCharList()
event.Skip()
def kbEvent(self, event):
keycode = event.GetKeyCode()
if keycode == wx.WXK_ESCAPE:
self.closeWindow()
return
event.Skip()
def closeWindow(self):
self.Destroy()
def popCharList(self):
sEsi = Esi.getInstance()
chars = sEsi.getSsoCharacters()

View File

@@ -1,69 +1,69 @@
from .gui.booster.add import GuiAddBoosterCommand
from .gui.booster.changeMeta import GuiChangeBoosterMetaCommand
from .gui.booster.remove import GuiRemoveBoosterCommand
from .gui.booster.remove import GuiRemoveBoostersCommand
from .gui.booster.sideEffectToggleState import GuiToggleBoosterSideEffectStateCommand
from .gui.booster.toggleState import GuiToggleBoosterStateCommand
from .gui.booster.toggleStates import GuiToggleBoosterStatesCommand
from .gui.cargo.add import GuiAddCargoCommand
from .gui.cargo.changeAmount import GuiChangeCargoAmountCommand
from .gui.cargo.changeMeta import GuiChangeCargoMetaCommand
from .gui.cargo.remove import GuiRemoveCargoCommand
from .gui.cargo.changeMetas import GuiChangeCargoMetasCommand
from .gui.cargo.remove import GuiRemoveCargosCommand
from .gui.commandFit.add import GuiAddCommandFitCommand
from .gui.commandFit.remove import GuiRemoveCommandFitCommand
from .gui.commandFit.toggleState import GuiToggleCommandFitStateCommand
from .gui.commandFit.remove import GuiRemoveCommandFitsCommand
from .gui.commandFit.toggleStates import GuiToggleCommandFitStatesCommand
from .gui.fitRename import GuiRenameFitCommand
from .gui.fitRestrictionToggle import GuiToggleFittingRestrictionsCommand
from .gui.fitSystemSecurity import GuiChangeFitSystemSecurityCommand
from .gui.implant.add import GuiAddImplantCommand
from .gui.implant.changeLocation import GuiChangeImplantLocationCommand
from .gui.implant.changeMeta import GuiChangeImplantMetaCommand
from .gui.implant.remove import GuiRemoveImplantCommand
from .gui.implant.remove import GuiRemoveImplantsCommand
from .gui.implant.setAdd import GuiAddImplantSetCommand
from .gui.implant.toggleState import GuiToggleImplantStateCommand
from .gui.implant.toggleStates import GuiToggleImplantStatesCommand
from .gui.itemsRebase import GuiRebaseItemsCommand
from .gui.localDrone.add import GuiAddLocalDroneCommand
from .gui.localDrone.changeAmount import GuiChangeLocalDroneAmountCommand
from .gui.localDrone.changeMeta import GuiChangeLocalDroneMetaCommand
from .gui.localDrone.remove import GuiRemoveLocalDroneCommand
from .gui.localDrone.changeMetas import GuiChangeLocalDroneMetasCommand
from .gui.localDrone.clone import GuiCloneLocalDroneCommand
from .gui.localDrone.remove import GuiRemoveLocalDronesCommand
from .gui.localDrone.stackSplit import GuiSplitLocalDroneStackCommand
from .gui.localDrone.stacksMerge import GuiMergeLocalDroneStacksCommand
from .gui.localDrone.toggleState import GuiToggleLocalDroneStateCommand
from .gui.localDrone.toggleStates import GuiToggleLocalDroneStatesCommand
from .gui.localFighter.abilityToggleState import GuiToggleLocalFighterAbilityStateCommand
from .gui.localFighter.add import GuiAddLocalFighterCommand
from .gui.localFighter.changeAmount import GuiChangeLocalFighterAmountCommand
from .gui.localFighter.changeMeta import GuiChangeLocalFighterMetaCommand
from .gui.localFighter.remove import GuiRemoveLocalFighterCommand
from .gui.localFighter.toggleState import GuiToggleLocalFighterStateCommand
from .gui.localFighter.changeMetas import GuiChangeLocalFighterMetasCommand
from .gui.localFighter.remove import GuiRemoveLocalFightersCommand
from .gui.localFighter.toggleStates import GuiToggleLocalFighterStatesCommand
from .gui.localModule.add import GuiAddLocalModuleCommand
from .gui.localModule.changeCharges import GuiChangeLocalModuleChargesCommand
from .gui.localModule.changeMetas import GuiChangeLocalModuleMetasCommand
from .gui.localModule.changeMutation import GuiChangeLocalModuleMutationCommand
from .gui.localModule.changeSpool import GuiChangeLocalModuleSpoolCommand
from .gui.localModule.changeStates import GuiChangeLocalModuleStatesCommand
from .gui.localModule.clone import GuiCloneLocalModuleCommand
from .gui.localModule.fill import GuiFillWithLocalModulesCommand
from .gui.localModule.fillAdd import GuiFillWithNewLocalModulesCommand
from .gui.localModule.fillClone import GuiFillWithClonedLocalModulesCommand
from .gui.localModule.mutatedConvert import GuiConvertMutatedLocalModuleCommand
from .gui.localModule.mutatedImport import GuiImportLocalMutatedModuleCommand
from .gui.localModule.mutatedRevert import GuiRevertMutatedLocalModuleCommand
from .gui.localModule.remove import GuiRemoveLocalModuleCommand
from .gui.localModule.replace import GuiReplaceLocalModuleCommand
from .gui.localModule.swap import GuiSwapLocalModulesCommand
from .gui.localModuleCargo.cargoToLocalModule import GuiCargoToLocalModuleCommand
from .gui.localModuleCargo.localModuleToCargo import GuiLocalModuleToCargoCommand
from .gui.projectedChangeStates import GuiChangeProjectedItemStatesCommand
from .gui.projectedDrone.add import GuiAddProjectedDroneCommand
from .gui.projectedDrone.changeAmount import GuiChangeProjectedDroneAmountCommand
from .gui.projectedDrone.changeMeta import GuiChangeProjectedDroneMetaCommand
from .gui.projectedDrone.remove import GuiRemoveProjectedDroneCommand
from .gui.projectedDrone.toggleState import GuiToggleProjectedDroneStateCommand
from .gui.projectedDrone.changeMetas import GuiChangeProjectedDroneMetasCommand
from .gui.projectedFighter.abilityToggleState import GuiToggleProjectedFighterAbilityStateCommand
from .gui.projectedFighter.add import GuiAddProjectedFighterCommand
from .gui.projectedFighter.changeAmount import GuiChangeProjectedFighterAmountCommand
from .gui.projectedFighter.changeMeta import GuiChangeProjectedFighterMetaCommand
from .gui.projectedFighter.remove import GuiRemoveProjectedFighterCommand
from .gui.projectedFighter.toggleState import GuiToggleProjectedFighterStateCommand
from .gui.projectedFighter.changeMetas import GuiChangeProjectedFighterMetasCommand
from .gui.projectedFit.add import GuiAddProjectedFitCommand
from .gui.projectedFit.changeAmount import GuiChangeProjectedFitAmountCommand
from .gui.projectedFit.remove import GuiRemoveProjectedFitCommand
from .gui.projectedFit.toggleState import GuiToggleProjectedFitStateCommand
from .gui.projectedModule.add import GuiAddProjectedModuleCommand
from .gui.projectedModule.changeCharges import GuiChangeProjectedModuleChargesCommand
from .gui.projectedModule.changeMeta import GuiChangeProjectedModuleMetaCommand
from .gui.projectedModule.changeMetas import GuiChangeProjectedModuleMetasCommand
from .gui.projectedModule.changeSpool import GuiChangeProjectedModuleSpoolCommand
from .gui.projectedModule.changeState import GuiChangeProjectedModuleStateCommand
from .gui.projectedModule.remove import GuiRemoveProjectedModuleCommand
from .gui.projectedRemove import GuiRemoveProjectedItemsCommand
from .gui.shipModeChange import GuiChangeShipModeCommand

View File

@@ -11,11 +11,12 @@ pyfalog = Logger(__name__)
class CalcAddBoosterCommand(wx.Command):
def __init__(self, fitID, boosterInfo, position=None):
def __init__(self, fitID, boosterInfo, position=None, commit=True):
wx.Command.__init__(self, True, 'Add Booster')
self.fitID = fitID
self.newBoosterInfo = boosterInfo
self.newPosition = position
self.commit = commit
self.oldBoosterInfo = None
self.oldPosition = None
@@ -38,7 +39,11 @@ class CalcAddBoosterCommand(wx.Command):
fit.boosters.insert(self.newPosition, newBooster)
except HandledListActionError:
pyfalog.warning('Failed to insert to list')
cmd = CalcAddBoosterCommand(fitID=self.fitID, boosterInfo=self.oldBoosterInfo, position=self.oldPosition)
cmd = CalcAddBoosterCommand(
fitID=self.fitID,
boosterInfo=self.oldBoosterInfo,
position=self.oldPosition,
commit=self.commit)
cmd.Do()
return False
else:
@@ -46,19 +51,24 @@ class CalcAddBoosterCommand(wx.Command):
fit.boosters.append(newBooster)
except HandledListActionError:
pyfalog.warning('Failed to append to list')
cmd = CalcAddBoosterCommand(fitID=self.fitID, boosterInfo=self.oldBoosterInfo, position=self.oldPosition)
cmd = CalcAddBoosterCommand(
fitID=self.fitID,
boosterInfo=self.oldBoosterInfo,
position=self.oldPosition,
commit=self.commit)
cmd.Do()
return False
self.newPosition = fit.boosters.index(newBooster)
eos.db.commit()
if self.commit:
eos.db.commit()
return True
def Undo(self):
pyfalog.debug('Undo addition of booster {} to fit {}'.format(self.newBoosterInfo, self.fitID))
if self.oldBoosterInfo is not None and self.oldPosition is not None:
cmd = CalcAddBoosterCommand(fitID=self.fitID, boosterInfo=self.oldBoosterInfo, position=self.oldPosition)
cmd = CalcAddBoosterCommand(fitID=self.fitID, boosterInfo=self.oldBoosterInfo, position=self.oldPosition, commit=self.commit)
return cmd.Do()
from .remove import CalcRemoveBoosterCommand
cmd = CalcRemoveBoosterCommand(fitID=self.fitID, position=self.newPosition)
cmd = CalcRemoveBoosterCommand(fitID=self.fitID, position=self.newPosition, commit=self.commit)
return cmd.Do()

View File

@@ -11,10 +11,11 @@ pyfalog = Logger(__name__)
class CalcRemoveBoosterCommand(wx.Command):
def __init__(self, fitID, position):
def __init__(self, fitID, position, commit=True):
wx.Command.__init__(self, True, 'Remove Booster')
self.fitID = fitID
self.position = position
self.commit = commit
self.savedBoosterInfo = None
def Do(self):
@@ -23,11 +24,16 @@ class CalcRemoveBoosterCommand(wx.Command):
booster = fit.boosters[self.position]
self.savedBoosterInfo = BoosterInfo.fromBooster(booster)
fit.boosters.remove(booster)
eos.db.commit()
if self.commit:
eos.db.commit()
return True
def Undo(self):
pyfalog.debug('Undoing removal of booster {} on fit {}'.format(self.savedBoosterInfo, self.fitID))
from .add import CalcAddBoosterCommand
cmd = CalcAddBoosterCommand(fitID=self.fitID, boosterInfo=self.savedBoosterInfo, position=self.position)
cmd = CalcAddBoosterCommand(
fitID=self.fitID,
boosterInfo=self.savedBoosterInfo,
position=self.position,
commit=self.commit)
return cmd.Do()

View File

@@ -1,32 +0,0 @@
import wx
from logbook import Logger
import eos.db
from service.fit import Fit
pyfalog = Logger(__name__)
class CalcToggleBoosterStateCommand(wx.Command):
def __init__(self, fitID, position, forceState=None):
wx.Command.__init__(self, True, 'Toggle Booster State')
self.fitID = fitID
self.position = position
self.forceState = forceState
self.savedState = None
def Do(self):
pyfalog.debug('Doing toggling of booster state at position {} for fit {}'.format(self.position, self.fitID))
fit = Fit.getInstance().getFit(self.fitID)
booster = fit.boosters[self.position]
self.savedState = booster.active
booster.active = not booster.active if self.forceState is None else self.forceState
eos.db.commit()
return True
def Undo(self):
pyfalog.debug('Undoing toggling of booster state at position {} for fit {}'.format(self.position, self.fitID))
cmd = CalcToggleBoosterStateCommand(fitID=self.fitID, position=self.position, forceState=self.savedState)
return cmd.Do()

View File

@@ -0,0 +1,54 @@
import wx
from logbook import Logger
import eos.db
from service.fit import Fit
pyfalog = Logger(__name__)
class CalcToggleBoosterStatesCommand(wx.Command):
def __init__(self, fitID, mainPosition, positions, forceStates=None):
wx.Command.__init__(self, True, 'Toggle Booster States')
self.fitID = fitID
self.mainPosition = mainPosition
self.positions = positions
self.forceStates = forceStates
self.savedStates = None
def Do(self):
pyfalog.debug('Doing toggling of booster state at position {}/{} for fit {}'.format(self.mainPosition, self.positions, self.fitID))
fit = Fit.getInstance().getFit(self.fitID)
positions = self.positions[:]
if self.mainPosition not in positions:
positions.append(self.mainPosition)
self.savedStates = {p: fit.boosters[p].active for p in positions}
if self.forceStates is not None:
for position, state in self.forceStates.items():
booster = fit.boosters[position]
booster.active = state
elif fit.boosters[self.mainPosition].active:
for position in positions:
booster = fit.boosters[position]
if booster.active:
booster.active = False
else:
for position in positions:
booster = fit.boosters[position]
if not booster.active:
booster.active = True
eos.db.commit()
return True
def Undo(self):
pyfalog.debug('Undoing toggling of booster state at position {}/{} for fit {}'.format(self.mainPosition, self.positions, self.fitID))
cmd = CalcToggleBoosterStatesCommand(
fitID=self.fitID,
mainPosition=self.mainPosition,
positions=self.positions,
forceStates=self.savedStates)
return cmd.Do()

View File

@@ -10,11 +10,12 @@ pyfalog = Logger(__name__)
class CalcAddCommandCommand(wx.Command):
def __init__(self, fitID, commandFitID, state=None):
def __init__(self, fitID, commandFitID, state=None, commit=True):
wx.Command.__init__(self, True, 'Add Command Fit')
self.fitID = fitID
self.commandFitID = commandFitID
self.state = state
self.commit = commit
def Do(self):
pyfalog.debug('Doing addition of command fit {} for fit {}'.format(self.commandFitID, self.fitID))
@@ -30,7 +31,9 @@ class CalcAddCommandCommand(wx.Command):
if commandFit in fit.commandFits:
pyfalog.debug('Command fit had been applied already')
return False
if commandFit.ID in fit.commandFitDict:
pyfalog.debug('Commanding fit is in command dict already')
return False
fit.commandFitDict[commandFit.ID] = commandFit
# This bit is required, see issue #83
eos.db.saveddata_session.flush()
@@ -44,7 +47,8 @@ class CalcAddCommandCommand(wx.Command):
return False
fitCommandInfo.active = self.state
eos.db.commit()
if self.commit:
eos.db.commit()
return True
def Undo(self):
@@ -54,6 +58,6 @@ class CalcAddCommandCommand(wx.Command):
commandFit = Fit.getInstance().getFit(self.commandFitID)
if commandFit is None:
return True
from .remove import CalcRemoveCommandCommand
cmd = CalcRemoveCommandCommand(fitID=self.fitID, commandFitID=self.commandFitID)
from .remove import CalcRemoveCommandFitCommand
cmd = CalcRemoveCommandFitCommand(fitID=self.fitID, commandFitID=self.commandFitID, commit=self.commit)
return cmd.Do()

View File

@@ -8,12 +8,13 @@ from service.fit import Fit
pyfalog = Logger(__name__)
class CalcRemoveCommandCommand(wx.Command):
class CalcRemoveCommandFitCommand(wx.Command):
def __init__(self, fitID, commandFitID):
def __init__(self, fitID, commandFitID, commit=True):
wx.Command.__init__(self, True, 'Remove Command Fit')
self.fitID = fitID
self.commandFitID = commandFitID
self.commit = commit
self.savedState = None
def Do(self):
@@ -30,14 +31,21 @@ class CalcRemoveCommandCommand(wx.Command):
if commandInfo is None:
pyfalog.warning('Fit command info is not available')
return False
self.savedState = commandInfo.active
if commandFit.ID not in fit.commandFitDict:
pyfalog.warning('Unable to find commanding fit in command dict')
return False
del fit.commandFitDict[commandFit.ID]
eos.db.commit()
if self.commit:
eos.db.commit()
return True
def Undo(self):
pyfalog.debug('Undoing removal of command fit {} for fit {}'.format(self.commandFitID, self.fitID))
from .add import CalcAddCommandCommand
cmd = CalcAddCommandCommand(fitID=self.fitID, commandFitID=self.commandFitID, state=self.savedState)
cmd = CalcAddCommandCommand(
fitID=self.fitID,
commandFitID=self.commandFitID,
state=self.savedState,
commit=self.commit)
return cmd.Do()

View File

@@ -1,39 +0,0 @@
import wx
from logbook import Logger
import eos.db
from service.fit import Fit
pyfalog = Logger(__name__)
class CalcToggleCommandFitStateCommand(wx.Command):
def __init__(self, fitID, commandFitID, forceState=None):
wx.Command.__init__(self, True, 'Toggle Command Fit State')
self.fitID = fitID
self.commandFitID = commandFitID
self.forceState = forceState
self.savedState = None
def Do(self):
pyfalog.debug('Doing toggling of command fit {} state for fit {}'.format(self.commandFitID, self.fitID))
commandFit = Fit.getInstance().getFit(self.commandFitID)
# Command fit could have been deleted if we are redoing
if commandFit is None:
pyfalog.debug('Command fit is not available')
return False
commandInfo = commandFit.getCommandInfo(self.fitID)
if commandInfo is None:
pyfalog.warning('Fit command info is not available')
return False
self.savedState = commandInfo.active
commandInfo.active = not commandInfo.active if self.forceState is None else self.forceState
eos.db.commit()
return True
def Undo(self):
pyfalog.debug('Undoing toggling of command fit {} state for fit {}'.format(self.commandFitID, self.fitID))
cmd = CalcToggleCommandFitStateCommand(fitID=self.fitID, commandFitID=self.commandFitID, forceState=self.savedState)
return cmd.Do()

View File

@@ -0,0 +1,73 @@
import wx
from logbook import Logger
import eos.db
from service.fit import Fit
pyfalog = Logger(__name__)
class CalcToggleCommandFitStatesCommand(wx.Command):
def __init__(self, fitID, mainCommandFitID, commandFitIDs, forceStates=None):
wx.Command.__init__(self, True, 'Toggle Command Fit States')
self.fitID = fitID
self.mainCommandFitID = mainCommandFitID
self.commandFitIDs = commandFitIDs
self.forceStates = forceStates
self.savedStates = None
def Do(self):
pyfalog.debug('Doing toggling of command fit {}/{} state for fit {}'.format(self.mainCommandFitID, self.commandFitIDs, self.fitID))
sFit = Fit.getInstance()
commandFitIDs = self.commandFitIDs[:]
if self.mainCommandFitID not in commandFitIDs:
commandFitIDs.append(self.mainCommandFitID)
commandInfos = {}
for commandFitID in commandFitIDs:
commandFit = sFit.getFit(commandFitID)
# Command fit could have been deleted if we are redoing
if commandFit is None:
pyfalog.debug('Command fit is not available')
continue
commandInfo = commandFit.getCommandInfo(self.fitID)
if commandInfo is None:
pyfalog.warning('Fit command info is not available')
continue
commandInfos[commandFitID] = commandInfo
if len(commandInfos) == 0:
return False
self.savedStates = {cfid: ci.active for cfid, ci in commandInfos.items()}
mainCommandInfo = commandInfos.get(self.mainCommandFitID)
if self.forceStates is not None:
for commandFitID, state in self.forceStates.items():
commandInfo = commandInfos.get(commandFitID)
if commandInfo is None:
continue
commandInfo.active = state
elif mainCommandInfo is not None and mainCommandInfo.active:
for commandInfo in commandInfos.values():
commandInfo.active = False
elif mainCommandInfo is not None and not mainCommandInfo.active:
for commandInfo in commandInfos.values():
commandInfo.active = True
# Bail if we cannot calculate which state to take
else:
return False
eos.db.commit()
return True
def Undo(self):
pyfalog.debug('Undoing toggling of command fit {}/{} state for fit {}'.format(self.mainCommandFitID, self.commandFitIDs, self.fitID))
cmd = CalcToggleCommandFitStatesCommand(
fitID=self.fitID,
mainCommandFitID=self.mainCommandFitID,
commandFitIDs=self.commandFitIDs,
forceStates=self.savedStates)
return cmd.Do()

View File

@@ -4,7 +4,7 @@ from logbook import Logger
import eos.db
from eos.exception import HandledListActionError
from gui.fitCommands.helpers import DroneInfo
from gui.fitCommands.helpers import DroneInfo, droneStackLimit
from service.fit import Fit
from service.market import Market
@@ -14,11 +14,12 @@ pyfalog = Logger(__name__)
class CalcAddLocalDroneCommand(wx.Command):
def __init__(self, fitID, droneInfo, forceNewStack=False, commit=True):
wx.Command.__init__(self, True, 'Add Drone')
def __init__(self, fitID, droneInfo, forceNewStack=False, ignoreRestrictions=False, commit=True):
wx.Command.__init__(self, True, 'Add Local Drone')
self.fitID = fitID
self.droneInfo = droneInfo
self.forceNewStack = forceNewStack
self.ignoreRestrictions = ignoreRestrictions
self.commit = commit
self.savedDroneInfo = None
self.savedPosition = None
@@ -30,10 +31,11 @@ class CalcAddLocalDroneCommand(wx.Command):
# If we're not adding any active drones, check if there's an inactive stack
# with enough space for new drones and use it
if not self.forceNewStack and self.droneInfo.amountActive == 0:
maxStack = droneStackLimit(fit, item)
for drone in fit.drones.find(item):
if (
drone is not None and drone.amountActive == 0 and
drone.amount + self.droneInfo.amount) <= max(5, fit.extraAttributes["maxActiveDrones"]
drone.amount + self.droneInfo.amount <= maxStack
):
self.savedDroneInfo = DroneInfo.fromDrone(drone)
self.savedPosition = fit.drones.index(drone)
@@ -45,7 +47,7 @@ class CalcAddLocalDroneCommand(wx.Command):
drone = self.droneInfo.toDrone()
if drone is None:
return False
if not drone.fits(fit):
if not self.ignoreRestrictions and not drone.fits(fit):
pyfalog.warning('Drone does not fit')
return False
try:
@@ -67,6 +69,8 @@ class CalcAddLocalDroneCommand(wx.Command):
drone = fit.drones[self.savedPosition]
drone.amount = self.savedDroneInfo.amount
drone.amountActive = self.savedDroneInfo.amountActive
if self.commit:
eos.db.commit()
return True
from .localRemove import CalcRemoveLocalDroneCommand
cmd = CalcRemoveLocalDroneCommand(

View File

@@ -1,5 +1,3 @@
import math
import wx
from logbook import Logger
@@ -14,7 +12,7 @@ pyfalog = Logger(__name__)
class CalcChangeLocalDroneAmountCommand(wx.Command):
def __init__(self, fitID, position, amount, commit=True):
wx.Command.__init__(self, True, 'Change Drone Amount')
wx.Command.__init__(self, True, 'Change Local Drone Amount')
self.fitID = fitID
self.position = position
self.amount = amount

View File

@@ -13,7 +13,7 @@ pyfalog = Logger(__name__)
class CalcRemoveLocalDroneCommand(wx.Command):
def __init__(self, fitID, position, amount, commit=True):
wx.Command.__init__(self, True, 'Remove Drone')
wx.Command.__init__(self, True, 'Remove Local Drone')
self.fitID = fitID
self.position = position
self.amountToRemove = amount
@@ -48,8 +48,6 @@ class CalcRemoveLocalDroneCommand(wx.Command):
drone = self.savedDroneInfo.toDrone()
if drone is None:
return False
if not drone.fits(fit):
return False
try:
fit.drones.insert(self.position, drone)
except HandledListActionError:

View File

@@ -1,37 +0,0 @@
import wx
from logbook import Logger
import eos.db
from service.fit import Fit
pyfalog = Logger(__name__)
class CalcToggleLocalDroneStateCommand(wx.Command):
def __init__(self, fitID, position, forceAmountActive=None):
wx.Command.__init__(self, True, 'Toggle Drone State')
self.fitID = fitID
self.position = position
self.forceAmountActive = forceAmountActive
self.savedAmountActive = None
def Do(self):
pyfalog.debug('Doing toggling of local drone state at position {} for fit {}'.format(self.position, self.fitID))
fit = Fit.getInstance().getFit(self.fitID)
drone = fit.drones[self.position]
self.savedAmountActive = drone.amountActive
if self.forceAmountActive is not None:
drone.amountActive = self.forceAmountActive
elif drone.amountActive > 0:
drone.amountActive = 0
else:
drone.amountActive = drone.amount
eos.db.commit()
return True
def Undo(self):
pyfalog.debug('Undoing toggling of local drone state at position {} for fit {}'.format(self.position, self.fitID))
cmd = CalcToggleLocalDroneStateCommand(fitID=self.fitID, position=self.position, forceAmountActive=self.savedAmountActive)
return cmd.Do()

View File

@@ -0,0 +1,55 @@
import wx
from logbook import Logger
import eos.db
from service.fit import Fit
pyfalog = Logger(__name__)
class CalcToggleLocalDroneStatesCommand(wx.Command):
def __init__(self, fitID, mainPosition, positions, forceActiveAmounts=None):
wx.Command.__init__(self, True, 'Toggle Local Drone States')
self.fitID = fitID
self.mainPosition = mainPosition
self.positions = positions
self.forceActiveAmounts = forceActiveAmounts
self.savedActiveAmounts = None
def Do(self):
pyfalog.debug('Doing toggling of local drone state at position {}/{} for fit {}'.format(self.mainPosition, self.positions, self.fitID))
fit = Fit.getInstance().getFit(self.fitID)
positions = self.positions[:]
if self.mainPosition not in positions:
positions.append(self.mainPosition)
self.savedActiveAmounts = {p: fit.drones[p].amountActive for p in positions}
if self.forceActiveAmounts is not None:
for position, amountActive in self.forceActiveAmounts.items():
drone = fit.drones[position]
drone.amountActive = amountActive
elif fit.drones[self.mainPosition].amountActive > 0:
for position in positions:
drone = fit.drones[position]
if drone.amountActive > 0:
drone.amountActive = 0
else:
for position in positions:
drone = fit.drones[position]
if drone.amountActive == 0:
drone.amountActive = drone.amount
eos.db.commit()
return True
def Undo(self):
pyfalog.debug('Undoing toggling of local drone state at position {}/{} for fit {}'.format(self.mainPosition, self.positions, self.fitID))
cmd = CalcToggleLocalDroneStatesCommand(
fitID=self.fitID,
mainPosition=self.mainPosition,
positions=self.positions,
forceActiveAmounts=self.savedActiveAmounts)
return cmd.Do()

View File

@@ -14,10 +14,11 @@ pyfalog = Logger(__name__)
class CalcAddProjectedDroneCommand(wx.Command):
def __init__(self, fitID, droneInfo):
def __init__(self, fitID, droneInfo, commit=True):
wx.Command.__init__(self, True, 'Add Projected Drone')
self.fitID = fitID
self.droneInfo = droneInfo
self.commit = commit
self.savedDroneInfo = None
def Do(self):
@@ -32,7 +33,8 @@ class CalcAddProjectedDroneCommand(wx.Command):
drone.amount += self.droneInfo.amount
if drone.amountActive > 0:
drone.amountActive += self.droneInfo.amount
eos.db.commit()
if self.commit:
eos.db.commit()
return True
# Making new stack
drone = self.droneInfo.toDrone()
@@ -45,9 +47,11 @@ class CalcAddProjectedDroneCommand(wx.Command):
fit.projectedDrones.append(drone)
except HandledListActionError:
pyfalog.warning('Failed to append to list')
eos.db.commit()
if self.commit:
eos.db.commit()
return False
eos.db.commit()
if self.commit:
eos.db.commit()
return True
def Undo(self):
@@ -61,8 +65,14 @@ class CalcAddProjectedDroneCommand(wx.Command):
return False
drone.amount = self.savedDroneInfo.amount
drone.amountActive = self.savedDroneInfo.amountActive
if self.commit:
eos.db.commit()
return True
# Removing previously added stack
from .projectedRemove import CalcRemoveProjectedDroneCommand
cmd = CalcRemoveProjectedDroneCommand(fitID=self.fitID, itemID=self.droneInfo.itemID, amount=math.inf)
cmd = CalcRemoveProjectedDroneCommand(
fitID=self.fitID,
itemID=self.droneInfo.itemID,
amount=math.inf,
commit=self.commit)
return cmd.Do()

View File

@@ -0,0 +1,53 @@
import wx
from logbook import Logger
import eos.db
from service.fit import Fit
pyfalog = Logger(__name__)
class CalcChangeProjectedDroneStateCommand(wx.Command):
def __init__(self, fitID, itemID, state, commit=True):
wx.Command.__init__(self, True, 'Change Projected Drone State')
self.fitID = fitID
self.itemID = itemID
self.state = state
self.commit = commit
self.savedState = None
def Do(self):
pyfalog.debug('Doing changing of projected drone {} state to {} for fit {}'.format(self.itemID, self.state, self.fitID))
fit = Fit.getInstance().getFit(self.fitID)
drone = next((pd for pd in fit.projectedDrones if pd.itemID == self.itemID), None)
if drone is None:
pyfalog.warning('Unable to find projected drone')
return False
self.savedState = drone.amountActive > 0
if self.state == self.savedState:
return False
if self.state:
if not drone.canBeApplied(fit):
pyfalog.warning('Projected drone cannot be applied')
return False
drone.amountActive = drone.amount
else:
drone.amountActive = 0
if self.commit:
eos.db.commit()
return True
def Undo(self):
pyfalog.debug('Undoing changing of projected drone {} state to {} for fit {}'.format(self.itemID, self.state, self.fitID))
cmd = CalcChangeProjectedDroneStateCommand(
fitID=self.fitID,
itemID=self.itemID,
state=self.savedState,
commit=self.commit)
return cmd.Do()

View File

@@ -11,11 +11,12 @@ pyfalog = Logger(__name__)
class CalcRemoveProjectedDroneCommand(wx.Command):
def __init__(self, fitID, itemID, amount):
def __init__(self, fitID, itemID, amount, commit=True):
wx.Command.__init__(self, True, 'Remove Projected Drone')
self.fitID = fitID
self.itemID = itemID
self.amountToRemove = amount
self.commit = commit
self.savedDroneInfo = None
def Do(self):
@@ -33,7 +34,8 @@ class CalcRemoveProjectedDroneCommand(wx.Command):
else:
if drone.amountActive > 0:
drone.amountActive = min(drone.amountActive, drone.amount)
eos.db.commit()
if self.commit:
eos.db.commit()
return True
def Undo(self):
@@ -44,8 +46,10 @@ class CalcRemoveProjectedDroneCommand(wx.Command):
if drone is not None:
drone.amount = self.savedDroneInfo.amount
drone.amountActive = self.savedDroneInfo.amountActive
if self.commit:
eos.db.commit()
return True
# Make new stack
from .projectedAdd import CalcAddProjectedDroneCommand
cmd = CalcAddProjectedDroneCommand(fitID=self.fitID, droneInfo=self.savedDroneInfo)
cmd = CalcAddProjectedDroneCommand(fitID=self.fitID, droneInfo=self.savedDroneInfo, commit=self.commit)
return cmd.Do()

View File

@@ -1,46 +0,0 @@
import wx
from logbook import Logger
import eos.db
from service.fit import Fit
pyfalog = Logger(__name__)
class CalcToggleProjectedDroneStateCommand(wx.Command):
def __init__(self, fitID, itemID, forceAmountActive=None):
wx.Command.__init__(self, True, 'Toggle Projected Drone State')
self.fitID = fitID
self.itemID = itemID
self.forceAmountActive = forceAmountActive
self.savedAmountActive = None
def Do(self):
pyfalog.debug('Doing toggling of projected drone {} state for fit {}'.format(self.itemID, self.fitID))
fit = Fit.getInstance().getFit(self.fitID)
drone = next((pd for pd in fit.projectedDrones if pd.itemID == self.itemID), None)
if drone is None:
pyfalog.warning('Unable to find projected drone')
return False
self.savedAmountActive = drone.amountActive
if self.forceAmountActive is not None:
if self.forceAmountActive > 0 and not drone.canBeApplied(fit):
pyfalog.warning('Projected drone cannot be applied')
return False
drone.amountActive = self.forceAmountActive
elif drone.amountActive > 0:
drone.amountActive = 0
else:
if not drone.canBeApplied(fit):
pyfalog.warning('Projected drone cannot be applied')
return False
drone.amountActive = drone.amount
eos.db.commit()
return True
def Undo(self):
pyfalog.debug('Undoing toggling of projected drone {} state for fit {}'.format(self.itemID, self.fitID))
cmd = CalcToggleProjectedDroneStateCommand(fitID=self.fitID, itemID=self.itemID, forceAmountActive=self.savedAmountActive)
return cmd.Do()

View File

@@ -1,44 +0,0 @@
import wx
from logbook import Logger
import eos.db
from service.fit import Fit
pyfalog = Logger(__name__)
class CalcToggleFighterAbilityStateCommand(wx.Command):
def __init__(self, fitID, projected, position, effectID, forceState=None):
wx.Command.__init__(self, True, 'Toggle Fighter Ability State')
self.fitID = fitID
self.projected = projected
self.position = position
self.effectID = effectID
self.forceState = forceState
self.savedState = None
def Do(self):
pyfalog.debug('Doing toggling of fighter ability {} state at position {} for fit {}'.format(self.effectID, self.position, self.fitID))
fit = Fit.getInstance().getFit(self.fitID)
container = fit.projectedFighters if self.projected else fit.fighters
fighter = container[self.position]
ability = next((fa for fa in fighter.abilities if fa.effectID == self.effectID), None)
if ability is None:
pyfalog.warning('Unable to find fighter ability')
return False
self.savedState = ability.active
ability.active = not ability.active if self.forceState is None else self.forceState
eos.db.commit()
return True
def Undo(self):
pyfalog.debug('Unoing toggling of fighter ability {} state at position {} for fit {}'.format(self.effectID, self.position, self.fitID))
cmd = CalcToggleFighterAbilityStateCommand(
fitID=self.fitID,
projected=self.projected,
position=self.position,
effectID=self.effectID,
forceState=self.savedState)
return cmd.Do()

View File

@@ -0,0 +1,87 @@
import wx
from logbook import Logger
import eos.db
from service.fit import Fit
pyfalog = Logger(__name__)
class CalcToggleFighterAbilityStatesCommand(wx.Command):
def __init__(self, fitID, projected, mainPosition, positions, effectID, forceStates=None):
wx.Command.__init__(self, True, 'Toggle Fighter Ability States')
self.fitID = fitID
self.projected = projected
self.mainPosition = mainPosition
self.positions = positions
self.effectID = effectID
self.forceStates = forceStates
self.savedStates = None
def Do(self):
pyfalog.debug('Doing toggling of fighter ability {} state at position {}/{} for fit {}'.format(self.effectID, self.mainPosition, self.positions, self.fitID))
fit = Fit.getInstance().getFit(self.fitID)
container = fit.projectedFighters if self.projected else fit.fighters
positions = self.positions[:]
if self.mainPosition not in positions:
positions.append(self.mainPosition)
savedStates = {}
for position in positions:
fighter = container[position]
ability = next((fa for fa in fighter.abilities if fa.effectID == self.effectID), None)
if ability is None:
continue
savedStates[position] = ability.active
if len(savedStates) > 0:
self.savedStates = savedStates
mainFighter = container[self.mainPosition]
mainAbility = next((fa for fa in mainFighter.abilities if fa.effectID == self.effectID), None)
changes = False
if self.forceStates is not None:
for position, state in self.forceStates.items():
fighter = container[position]
ability = next((fa for fa in fighter.abilities if fa.effectID == self.effectID), None)
if ability is None:
continue
changes = True
if ability.active is not state:
ability.active = state
elif mainAbility is None:
pyfalog.warning('Unable to find main fighter ability')
return False
elif mainAbility.active:
for position in positions:
fighter = container[position]
ability = next((fa for fa in fighter.abilities if fa.effectID == self.effectID), None)
if ability is None:
continue
if ability.active:
changes = True
ability.active = False
else:
for position in positions:
fighter = container[position]
ability = next((fa for fa in fighter.abilities if fa.effectID == self.effectID), None)
if ability is None:
continue
if not ability.active:
changes = True
ability.active = True
eos.db.commit()
return changes
def Undo(self):
pyfalog.debug('Undoing toggling of fighter ability {} state at position {}/{} for fit {}'.format(self.effectID, self.mainPosition, self.positions, self.fitID))
cmd = CalcToggleFighterAbilityStatesCommand(
fitID=self.fitID,
projected=self.projected,
mainPosition=self.mainPosition,
positions=self.positions,
effectID=self.effectID,
forceStates=self.savedStates)
return cmd.Do()

View File

@@ -11,11 +11,13 @@ pyfalog = Logger(__name__)
class CalcAddLocalFighterCommand(wx.Command):
def __init__(self, fitID, fighterInfo, position=None):
def __init__(self, fitID, fighterInfo, position=None, ignoreRestrictions=False, commit=True):
wx.Command.__init__(self, True, 'Add Fighter')
self.fitID = fitID
self.fighterInfo = fighterInfo
self.position = position
self.ignoreRestrictions = ignoreRestrictions
self.commit = commit
def Do(self):
pyfalog.debug('Doing addition of fighter {} to fit {}'.format(self.fighterInfo, self.fitID))
@@ -24,7 +26,7 @@ class CalcAddLocalFighterCommand(wx.Command):
return False
fit = Fit.getInstance().getFit(self.fitID)
if not fighter.fits(fit):
if not self.ignoreRestrictions and not fighter.fits(fit):
pyfalog.warning('Fighter does not fit')
return False
@@ -43,7 +45,8 @@ class CalcAddLocalFighterCommand(wx.Command):
fit.fighters.append(fighter)
except HandledListActionError:
pyfalog.warning('Failed to append to list')
eos.db.commit()
if self.commit:
eos.db.commit()
return False
self.position = fit.fighters.index(fighter)
else:
@@ -51,14 +54,16 @@ class CalcAddLocalFighterCommand(wx.Command):
fit.fighters.insert(self.position, fighter)
except HandledListActionError:
pyfalog.warning('Failed to insert to list')
eos.db.commit()
if self.commit:
eos.db.commit()
return False
eos.db.commit()
if self.commit:
eos.db.commit()
return True
def Undo(self):
pyfalog.debug('Undoing addition of fighter {} to fit {}'.format(self.fighterInfo, self.fitID))
from .localRemove import CalcRemoveLocalFighterCommand
cmd = CalcRemoveLocalFighterCommand(fitID=self.fitID, position=self.position)
cmd = CalcRemoveLocalFighterCommand(fitID=self.fitID, position=self.position, commit=self.commit)
cmd.Do()
return True

View File

@@ -11,10 +11,11 @@ pyfalog = Logger(__name__)
class CalcRemoveLocalFighterCommand(wx.Command):
def __init__(self, fitID, position):
def __init__(self, fitID, position, commit=True):
wx.Command.__init__(self, True, 'Remove Fighter')
self.fitID = fitID
self.position = position
self.commit = commit
self.savedFighterInfo = None
def Do(self):
@@ -23,11 +24,17 @@ class CalcRemoveLocalFighterCommand(wx.Command):
fighter = fit.fighters[self.position]
self.savedFighterInfo = FighterInfo.fromFighter(fighter)
fit.fighters.remove(fighter)
eos.db.commit()
if self.commit:
eos.db.commit()
return True
def Undo(self):
pyfalog.debug('Undoing removal of fighter at position {} from fit {}'.format(self.position, self.fitID))
from .localAdd import CalcAddLocalFighterCommand
cmd = CalcAddLocalFighterCommand(fitID=self.fitID, fighterInfo=self.savedFighterInfo, position=self.position)
cmd = CalcAddLocalFighterCommand(
fitID=self.fitID,
fighterInfo=self.savedFighterInfo,
position=self.position,
ignoreRestrictions=True,
commit=self.commit)
return cmd.Do()

View File

@@ -0,0 +1,56 @@
import wx
from logbook import Logger
import eos.db
from service.fit import Fit
pyfalog = Logger(__name__)
class CalcToggleLocalFighterStatesCommand(wx.Command):
def __init__(self, fitID, mainPosition, positions, forceStates=None):
wx.Command.__init__(self, True, 'Toggle Local Fighter States')
self.fitID = fitID
self.mainPosition = mainPosition
self.positions = positions
self.forceStates = forceStates
self.savedStates = None
def Do(self):
pyfalog.debug('Doing toggling of local fighter state at position {}/{} for fit {}'.format(
self.mainPosition, self.positions, self.fitID))
fit = Fit.getInstance().getFit(self.fitID)
positions = self.positions[:]
if self.mainPosition not in positions:
positions.append(self.mainPosition)
self.savedStates = {p: fit.fighters[p].active for p in positions}
if self.forceStates is not None:
for position, state in self.forceStates.items():
fighter = fit.fighters[position]
fighter.active = state
elif fit.fighters[self.mainPosition].active:
for position in positions:
fighter = fit.fighters[position]
if fighter.active:
fighter.active = False
else:
for position in positions:
fighter = fit.fighters[position]
if not fighter.active:
fighter.active = True
eos.db.commit()
return True
def Undo(self):
pyfalog.debug('Undoing toggling of local fighter state at position {}/{} for fit {}'.format(
self.mainPosition, self.positions, self.fitID))
cmd = CalcToggleLocalFighterStatesCommand(
fitID=self.fitID,
mainPosition=self.mainPosition,
positions=self.positions,
forceStates=self.savedStates)
return cmd.Do()

View File

@@ -11,11 +11,12 @@ pyfalog = Logger(__name__)
class CalcAddProjectedFighterCommand(wx.Command):
def __init__(self, fitID, fighterInfo, position=None):
def __init__(self, fitID, fighterInfo, position=None, commit=True):
wx.Command.__init__(self, True, 'Add Projected Fighter')
self.fitID = fitID
self.fighterInfo = fighterInfo
self.position = position
self.commit = commit
def Do(self):
pyfalog.debug('Doing addition of projected fighter {} onto: {}'.format(self.fighterInfo, self.fitID))
@@ -27,20 +28,23 @@ class CalcAddProjectedFighterCommand(wx.Command):
try:
fit.projectedFighters.insert(self.position, fighter)
except HandledListActionError:
eos.db.commit()
if self.commit:
eos.db.commit()
return False
else:
try:
fit.projectedFighters.append(fighter)
except HandledListActionError:
eos.db.commit()
if self.commit:
eos.db.commit()
return False
self.position = fit.projectedFighters.index(fighter)
eos.db.commit()
if self.commit:
eos.db.commit()
return True
def Undo(self):
pyfalog.debug('Undoing addition of projected fighter {} onto: {}'.format(self.fighterInfo, self.fitID))
from .projectedRemove import CalcRemoveProjectedFighterCommand
cmd = CalcRemoveProjectedFighterCommand(fitID=self.fitID, position=self.position)
cmd = CalcRemoveProjectedFighterCommand(fitID=self.fitID, position=self.position, commit=self.commit)
return cmd.Do()

View File

@@ -0,0 +1,46 @@
import wx
from logbook import Logger
import eos.db
from service.fit import Fit
pyfalog = Logger(__name__)
class CalcChangeProjectedFighterStateCommand(wx.Command):
def __init__(self, fitID, position, state, commit=True):
wx.Command.__init__(self, True, 'Change Projected Fighter State')
self.fitID = fitID
self.position = position
self.state = state
self.commit = commit
self.savedState = None
def Do(self):
pyfalog.debug('Doing changing of projected fighter state to {} at position {} for fit {}'.format(
self.state, self.position, self.fitID))
fit = Fit.getInstance().getFit(self.fitID)
fighter = fit.projectedFighters[self.position]
self.savedState = fighter.active
if self.state == self.savedState:
return False
fighter.active = self.state
if self.commit:
eos.db.commit()
return True
def Undo(self):
pyfalog.debug('Undoing changing of projected fighter state to {} at position {} for fit {}'.format(
self.state, self.position, self.fitID))
cmd = CalcChangeProjectedFighterStateCommand(
fitID=self.fitID,
position=self.position,
state=self.savedState,
commit=self.commit)
return cmd.Do()

View File

@@ -11,10 +11,11 @@ pyfalog = Logger(__name__)
class CalcRemoveProjectedFighterCommand(wx.Command):
def __init__(self, fitID, position):
def __init__(self, fitID, position, commit=True):
wx.Command.__init__(self, True, 'Add Projected Fighter')
self.fitID = fitID
self.position = position
self.commit = commit
self.savedFighterInfo = None
def Do(self):
@@ -23,11 +24,16 @@ class CalcRemoveProjectedFighterCommand(wx.Command):
fighter = fit.projectedFighters[self.position]
self.savedFighterInfo = FighterInfo.fromFighter(fighter)
fit.projectedFighters.remove(fighter)
eos.db.commit()
if self.commit:
eos.db.commit()
return True
def Undo(self):
pyfalog.debug('Undoing removal of projected fighter at position {} from fit {}'.format(self.position, self.fitID))
from .projectedAdd import CalcAddProjectedFighterCommand
cmd = CalcAddProjectedFighterCommand(fitID=self.fitID, fighterInfo=self.savedFighterInfo)
cmd = CalcAddProjectedFighterCommand(
fitID=self.fitID,
fighterInfo=self.savedFighterInfo,
position=self.position,
commit=self.commit)
return cmd.Do()

Some files were not shown because too many files have changed in this diff Show More