1
0
mirror of https://github.com/chylex/TweetDuck.git synced 2025-09-14 10:32:10 +02:00

Compare commits

...

398 Commits
1.7.7 ... 1.9.2

Author SHA1 Message Date
820ce9e845 Fix uploading files with uppercase extensions 2017-09-11 18:27:39 +02:00
f17806f4e8 Allow dragging tweet links over columns to open them in detail view
Closes #167
2017-09-10 23:03:03 +02:00
3f5ffc9e10 Fix t.co bypass when middle-clicking links in tweets 2017-09-09 19:16:08 +02:00
aeb0842ab4 Move system tray options to a separate tab 2017-09-08 11:36:34 +02:00
38837ae84c Add option to open search columns before the first column 2017-09-07 15:22:14 +02:00
a4eb6935af Fix 'Theme color tweaks' changing color of some scrollbars it shouldn't change 2017-09-06 12:57:43 +02:00
52f1f4c4eb Release 1.9.1 2017-09-05 22:55:32 +02:00
6c1782a038 Fix some twitter links (/signup, /tos, /privacy) having context menu for accounts 2017-09-05 22:43:07 +02:00
8b8f5f5473 Fix login page links opening in the app instead of external browser 2017-09-05 22:32:11 +02:00
61d3ed891a Update t.co bypass to work for media and bio urls 2017-09-05 21:47:22 +02:00
b1abf87320 Revert TweetDeck scrollbar color & fix notification scrollbar with 'Theme color tweaks' on 2017-09-05 14:55:29 +02:00
9aedfc2799 Fix scrollbar in Options not disappearing when switching tabs while animating 2017-09-04 17:52:01 +02:00
ad6240a067 Refactor delegating multiple events at once in code.js 2017-09-04 03:02:30 +02:00
9539eb076a Fix heart icon size and animation 2017-09-03 01:45:59 +02:00
c808e7bd83 Fix calling OpenExternalBrowser from non-UI threads, causing crashes or errors 2017-09-02 21:49:45 +02:00
13ea388f5e Fix upload dialog to include 'All Supported Formats' instead of separating them 2017-09-02 20:51:58 +02:00
c46dc0f1a3 Fix 'Open link in browser' not bypassing t.co 2017-09-02 20:30:54 +02:00
2ae311007d Make https scheme check first because https rocks 2017-09-02 13:54:54 +02:00
9344e02bff Add a privacy warning when opening a t.co link in case the bypass fails 2017-09-02 13:47:43 +02:00
40ad836fc3 Bypass t.co on click & fix right-clicking t.co links in notifications 2017-09-02 13:07:40 +02:00
e8604a261d Replace 'new EventArgs()' with 'EventArgs.Empty' 2017-09-01 14:34:23 +02:00
2a41d21a29 Add unit tests for UserConfig 2017-08-31 21:38:26 +02:00
4c62aa067b Update unit test generated file names 2017-08-31 19:45:56 +02:00
49db3074c6 Rewrite IO handling in unit tests 2017-08-31 19:33:24 +02:00
f5e3b34f30 Tweak border radius on inputs in column settings and custom timelines 2017-08-31 16:03:38 +02:00
f0affa4aec Implement 'Save all images as...' for images in quoted tweets 2017-08-31 15:34:05 +02:00
4f5075ac54 Fix 'Save image as...' usernames in quoted tweets & more refactoring 2017-08-31 15:26:03 +02:00
20f0445b10 Replace FormNotificationBase.ChirpId with CanViewDetail that checks ColumnId too 2017-08-31 14:52:38 +02:00
c77c974455 Rename more parameters and fields in TweetDeckBridge 2017-08-31 14:40:10 +02:00
44397b2d45 Fix parameter name in TweetDeckBridge 2017-08-31 14:07:46 +02:00
943d4d4d72 Release 1.9 2017-08-30 21:40:11 +02:00
6468c03465 Fix 'Restore Defaults' not resetting plugin status and import/reset not closing Plugins form 2017-08-30 21:34:12 +02:00
8141a5a5c5 Fix TrackBar labels being above focus cues 2017-08-30 21:14:58 +02:00
26a1779310 Fix 'Restart with Arguments' including disabled data folder message in shortcut 2017-08-30 21:00:59 +02:00
45d18ffafe Set volume slider SmallChange to 1 and increase width of video player volume slider 2017-08-30 21:00:11 +02:00
5f1c30609c Fix typo in error message in FileSerializer 2017-08-30 20:34:02 +02:00
7266d705d3 Fix video player UI for small videos & increase FormBrowser min size 2017-08-30 19:15:45 +02:00
ee6bb782d6 Tweak the download icon in video player 2017-08-30 16:59:01 +02:00
8ae6e2c886 Bump project and plugin versions 2017-08-30 16:51:53 +02:00
dd3a0d3890 Tweak emoji warning message 2017-08-30 14:57:36 +02:00
8d8e2da57e Make Enter key in emoji search insert the first available emoji 2017-08-30 14:53:43 +02:00
e60d204302 Refocus tweet input after closing emoji keyboard via icon & remove unused code 2017-08-30 14:50:39 +02:00
3d642d8ad2 Tweak emoji search to only select query on click and refocus it after clicking emoji 2017-08-30 14:48:43 +02:00
8db6e8a090 Remove unused custom emoji keyboard code 2017-08-30 14:09:32 +02:00
8153fcde85 Minor refactoring 2017-08-30 13:35:47 +02:00
96469cfca5 Rewrite config reload & fix some options breaking after import or reset 2017-08-30 12:53:10 +02:00
7601645c12 Fix some config options not being committed before opening Manage Options 2017-08-30 12:41:54 +02:00
c28615d548 Add options to reset session and plugin data when restoring defaults 2017-08-29 14:28:33 +02:00
b515add94e Rewrite browser/plugin reload handling when importing a profile 2017-08-29 14:26:42 +02:00
9fd5e9443d Make 'Manage Options' dialog close options after a successful operation 2017-08-29 14:22:20 +02:00
b2ddb1fab2 Disable 'Tray Highlight' option when the icon is disabled 2017-08-29 00:02:41 +02:00
fdac42947c Only activate parent form in video player if the player window itself is active 2017-08-28 23:41:46 +02:00
eeaf6949c5 Delay 'View detail' if the website is reloading 2017-08-28 22:46:06 +02:00
d7ad62d476 Make TweetNotification use persistent column ID 2017-08-28 22:38:11 +02:00
cd87a329fc Add a network error notification if the device goes offline
Closes #145
2017-08-28 22:14:07 +02:00
8c0d306823 Rewrite sound notification hook to be hopefully more reliable 2017-08-28 20:05:49 +02:00
d5c3ea0862 'View detail' errors now ask user if they want to open the tweet in a browser 2017-08-28 19:55:10 +02:00
83c962a7a4 Add support for icons in alert/confirm/prompt JS functions 2017-08-28 19:40:32 +02:00
40ef9a42dd Fix unsealed classes 2017-08-28 18:46:14 +02:00
868af5ac6a Goodbye, sweet rant 2017-08-28 18:19:32 +02:00
625227d0ce Rewrite audio library & add notification volume option for WMP impl 2017-08-28 18:16:13 +02:00
064627961e Fix zoom option label overlapping the slider 2017-08-28 17:16:04 +02:00
de0321cb2d Tweak video player label rendering & add label to volume slider 2017-08-28 15:31:27 +02:00
0d71a33b28 Add close/download/fullscreen buttons to video player 2017-08-28 13:31:19 +02:00
6d779f17b3 Fix video player tooltip going outside Form bounds 2017-08-28 10:37:18 +02:00
05510d7bc1 Add tooltip to seek bar in video player 2017-08-28 10:36:53 +02:00
8e162fe031 Add a custom tooltip to be used for video player controls 2017-08-27 23:13:39 +02:00
7ea7366a43 Change default CultureInfo in video player 2017-08-27 23:12:52 +02:00
445e6fcec0 Make Escape key in video player exit fullscreen or close the player 2017-08-27 21:03:57 +02:00
42f4d97d5d Rewrite key handling in video player 2017-08-27 20:46:10 +02:00
6357708533 Finish implementing 'View detail' context menu option in notifications
Closes #152
2017-08-27 20:11:56 +02:00
59c9801437 Address code analysis and remove unused code 2017-08-27 18:48:54 +02:00
d691bef1fb Add video context menu items and update video service check 2017-08-27 18:23:50 +02:00
442d74d0cb Refactor context menu handling and make adding new types of context easier 2017-08-27 18:18:30 +02:00
588bb9a093 Refactor FormNotificationBase to store TweetNotification instead of copying data 2017-08-27 13:40:49 +02:00
380e580d65 Fix cut off badge icon in notifications in notifications 2017-08-27 13:35:02 +02:00
4e306661f8 Fix cut off badge icon in notifications 2017-08-24 14:45:20 +02:00
9f3f33da93 More power! 2017-08-23 07:28:08 +02:00
69cd96a37c Add 'View detail' context menu item in notifications (currently loaded tweets only) 2017-08-22 11:59:34 +02:00
1293a2a533 Harness the incredible power of return-if statements 2017-08-22 10:10:46 +02:00
d24b7bbcb9 Implement return-if transpiler for JS files 2017-08-22 09:51:27 +02:00
b55b47b689 Refactor postbuild js/html processing script 2017-08-22 09:48:03 +02:00
c4c032b4d5 Bump TweetDuck.Video project version 2017-08-22 08:16:28 +02:00
970cd21964 Move TweetDuck.Video project folder 2017-08-22 08:13:49 +02:00
8ca9d242b2 Fix tab order in restart dialog 2017-08-22 07:30:17 +02:00
6f0518edcc Disable text input in locale drop-down in restart dialog 2017-08-22 07:22:09 +02:00
e2d15dd7e3 Add a shortcut target field to restart dialog 2017-08-22 07:20:40 +02:00
5c310e8647 Disable data folder in restart dialog for portable installs, and fix up tooltips 2017-08-22 06:24:02 +02:00
01dca0bc66 Fix sensitive media preference being ignored in notification previews 2017-08-22 04:59:55 +02:00
8b54fbdb2f Remove GC reload & threshold option migration code 2017-08-22 03:53:59 +02:00
663d0a633e Remove redundant Config.Save() call in TabSettingsGeneral 2017-08-22 03:44:13 +02:00
ccd5edb0e4 Remove legacy config file upgrade code 2017-08-22 03:23:53 +02:00
c6190db918 Rewrite update event args and update dismissal handling 2017-08-22 03:22:44 +02:00
3d4cec3b22 Remove update code that handles unsupported system check 2017-08-22 02:45:51 +02:00
5ed970b5a0 Remove resx file on FormUpdateDownload 2017-08-21 19:18:12 +02:00
c22934336b Remove Program.VersionFull and refactor plugin version checks 2017-08-21 18:47:26 +02:00
a3a52e0a1c Release 1.8.7 2017-08-21 14:32:15 +02:00
68dca6e3d9 Fix spacebar not toggling video pause when the main app was focused 2017-08-21 14:14:38 +02:00
017f883e0b Disable custom emoji input, fix selection handling and support twemoji font if installed 2017-08-21 13:37:21 +02:00
77b5c95f75 Add basic js minification (trim whitespace and remove single line comments) 2017-08-21 09:41:15 +02:00
9d052c8339 Update close button fix to only affect New Tweet drawer 2017-08-21 02:17:48 +02:00
d67623a657 Tweak follow notification padding in the browser 2017-08-21 01:52:19 +02:00
c740b3dd46 What the fuck are you doing Twitter 2017-08-21 01:35:53 +02:00
2ef5f7f96f Fix border radius on media previews in tweet detail 2017-08-16 18:27:44 +02:00
404568d795 Fix pre-build powershell command causing build error 2017-08-16 18:23:38 +02:00
b5a6337a0c Update custom CSS to work better with recent TweetDeck changes 2017-08-14 17:15:18 +02:00
82170c3fbd Fix sensitive media in notification previews and tweak follow notification padding 2017-08-14 16:12:34 +02:00
e6d6275fcc Work on emoji keyboard contenteditable fixes (selection, focus, editor migration) 2017-08-14 15:37:55 +02:00
97c865a127 Make emoji editor only show after adding emoji, fix minor UI issues 2017-08-14 04:22:13 +02:00
1ff21f0ee0 Make emoji keyboard replace tweet input with one that displays emoji
Closes #146
2017-08-14 00:47:08 +02:00
2a3dca4467 Rewrite video player to use duplex pipe for process communication 2017-08-13 17:52:46 +02:00
d4ecfcceec Tweak DuplexPipe to set key instead of data when separator is missing 2017-08-13 17:31:58 +02:00
ec5d503e4d Make DuplexPipe data serialized as key/value pairs 2017-08-13 17:23:23 +02:00
346391ca2d Remove unused 'using' statements for the billionth time 2017-08-13 16:55:08 +02:00
9074cdf340 Add a hover effect to video player seek bar 2017-08-13 16:46:33 +02:00
2fcf3604a8 Move video player form controls to a different namespace 2017-08-13 16:14:46 +02:00
34e5185fa1 Fuck localized .NET exceptions 2017-08-13 15:53:39 +02:00
e09e0e69ca Fuck browser process when building the project 2017-08-13 15:50:43 +02:00
963c98e588 Move interprocess comms to a separate project & implement duplex pipe 2017-08-13 15:20:04 +02:00
92acb823a4 Implement a duplex anonymous pipe in TweetLib.Communication 2017-08-13 15:14:17 +02:00
b967b1288f Move process communication to a separate project 2017-08-13 13:54:34 +02:00
1db271ce90 Fix spacebar triggering fullscreen in video player 2017-08-13 00:23:08 +02:00
58c64025e3 Fix level 2 lists and links in update changelog modal 2017-08-12 23:52:38 +02:00
643a7a87aa Release 1.8.6 2017-08-12 23:39:41 +02:00
5e9ed5d713 Improve video player startup and ensure it's always closed with the main app 2017-08-12 23:36:14 +02:00
78e492c764 Tweak 'stay open' pin position and tooltip 2017-08-12 20:33:52 +02:00
59c2a3642b Bump version of subprocess exe (should have been done a long time ago) 2017-08-12 19:10:38 +02:00
40ca923745 Cleanup FormPlayer code and set sync timer interval to 15 instead of 10 2017-08-12 19:08:17 +02:00
03af6cecaa Replace 'Stay open' checkbox with a pin icon
Closes #154
2017-08-12 17:49:27 +02:00
3992e447f4 Change tooltip border radius to be almost square 2017-08-12 17:47:55 +02:00
14a9edeb73 Fix various focus issues with video player and fix double-clicking control panel 2017-08-12 15:12:54 +02:00
92f1e9f7ec Make video player progress bar seek on mouse down instead of up 2017-08-12 14:31:46 +02:00
19c294c53e Terminate video player when pressing back mouse button over it 2017-08-12 13:43:53 +02:00
fe88ea5c05 Fix ctrl key not opening gifs externally 2017-08-12 03:37:24 +02:00
c9d551213a Remove license screen from installers 2017-08-12 03:15:51 +02:00
1e86a33ceb Hide video player overlay when video process exits gracelessly 2017-08-12 03:12:50 +02:00
551dd229f5 Make back mouse button hide video player and overlay 2017-08-12 03:04:24 +02:00
5ecf3c4147 Fix video player going past the end of a video when paused near the end 2017-08-12 02:26:52 +02:00
91bb2f4df0 Fix video player control panel not disappearing & improve error handling 2017-08-12 01:02:09 +02:00
ae3a0ae83d Fix crash when trying to update with 'Edit CSS' or 'Edit CEF Arguments' open 2017-08-12 00:05:56 +02:00
63ce7523de Fix oversight from previous commit 2017-08-12 00:01:13 +02:00
9e3b92bfc1 Move PluginManager initialization and move Form manipulation to FormManager 2017-08-11 23:57:44 +02:00
bc1767fb84 Change namespace of BrowserProcesses, MemoryUsageTracker, VideoPlayer 2017-08-11 23:50:16 +02:00
f917096cc7 Refactor plugin execution code 2017-08-11 23:32:47 +02:00
308926a2ae Add video player volume sync with user config 2017-08-11 20:58:37 +02:00
76f2b1a454 Make video player volume slider constant width 2017-08-11 20:20:07 +02:00
d899e4b38b Refactor video player control outside designer for dev convenience 2017-08-11 20:14:45 +02:00
e1422e35cc Add seeking + current time and duration to video player 2017-08-11 16:49:23 +02:00
2c00c6bb81 Expand the video player control panel and add progress bar 2017-08-11 16:21:31 +02:00
7e56ba6408 Make custom video player triggerable in tweet detail 2017-08-11 15:52:20 +02:00
8ceb70e67d Fix back button and context menu handling with a video playing 2017-08-11 15:22:45 +02:00
37d5efef1d Add an icon to TweetDuck.Video.exe 2017-08-11 15:06:38 +02:00
924065c26e Change video play icon color and handle playback errors 2017-08-11 13:59:05 +02:00
58cc7ea10d Add WIP video player for MP4s 2017-08-11 13:27:15 +02:00
f93e275ddf Add a volume slider to video player 2017-08-11 13:22:12 +02:00
06d2a5f715 Make video player pause/unpause when pressing space 2017-08-11 13:20:50 +02:00
3a7455eafe Fix video player cursor & pause/unpause on click 2017-08-11 12:33:34 +02:00
8b676fe6ce Implement video player in TweetDeck 2017-08-11 11:56:19 +02:00
54d12686af Tweak video player UI handling 2017-08-11 11:32:20 +02:00
f231256402 Improve player UI handling (cursor, position, setting owner handle) 2017-08-11 10:31:23 +02:00
410ead66f8 Add video player args and adjust location and size to owner window 2017-08-11 09:36:29 +02:00
c833a810af Add TweetDuck.Video project for video playback 2017-08-11 08:22:12 +02:00
50f1336b1d Tweak headings in update changelog renderer 2017-08-10 16:33:45 +02:00
60ed0b8cde Release 1.8.5.1 2017-08-10 16:25:55 +02:00
cc55a81c1b Remove emoji-instructions.txt during an update 2017-08-10 16:25:49 +02:00
f832e04e9e Remove unnecessary resx files and cleanup csproj 2017-08-10 15:09:15 +02:00
fc760b9a0c Fix another case in duplicate DM notifications 2017-08-10 13:46:13 +02:00
9addff0521 Exclude emoji-instructions.txt from build 2017-08-10 13:45:32 +02:00
dcaa3aab19 Work around duplicate DM notifications and rewrite recent tweet check 2017-08-10 00:51:38 +02:00
628785c68c Move _postbuild.bat to an MSBuild target directly in the project file 2017-08-10 00:20:58 +02:00
a5aa396fda Fix image quality setting not working in columns with large previews 2017-08-09 18:48:36 +02:00
f53a9f05e3 Fix image download filename for avatars and add more unit tests 2017-08-07 14:48:20 +02:00
7749b14156 Increment emoji keyboard plugin version 2017-08-06 20:32:13 +02:00
c15f339718 Fix emoji keyboard not disappearing after pressing ctrl+enter to tweet 2017-08-06 20:31:44 +02:00
775f590bfa Release 1.8.5 2017-08-06 15:58:28 +02:00
76408ea56f Increment verison of edit-design and emoji-keyboard plugins 2017-08-06 15:58:23 +02:00
a391d8ee83 Fix image pasting allowing more than 1 image in DMs 2017-08-05 21:52:38 +02:00
48c38f6e1d Include tweet author and quality in image download filename 2017-08-05 21:32:07 +02:00
37c5fba162 Change text color of sound notification file option for invalid paths 2017-08-05 19:50:30 +02:00
23e99b1d44 Update GC memory threshold defaults, also GC reload is enabled by default 2017-08-05 19:42:10 +02:00
8432240a47 Update HW acceleration & GC reload tooltips to note they won't be exported 2017-08-05 19:37:14 +02:00
a4bab743d6 Remove notification warning in GC reload option tooltip 2017-08-05 19:34:20 +02:00
60766789ab Move GC reload options to SystemConfig 2017-08-05 19:27:20 +02:00
ca014f881c Rewrite unknown property handling in FileSerializer 2017-08-05 19:23:42 +02:00
886eabe26c Show notifications that were missed during a browser reload 2017-08-05 18:43:57 +02:00
65b7167b5f Rewrite browser reload to save column notification state in session data 2017-08-05 18:36:17 +02:00
abbdde851e Make quoted tweets and RT account selectors square, fix RT account selector heading 2017-08-05 18:30:42 +02:00
54ac54aba6 Add session data that persists across browser reloads 2017-08-05 18:08:22 +02:00
184340f400 Increase delay for clearing recent notifications to prevent duplicates 2017-08-05 17:06:02 +02:00
93dd6813e8 Fix old icon alignment in 'Add column' dialog 2017-08-05 14:44:49 +02:00
b689b08711 Make follow notification button less visible when not hovered 2017-08-05 12:56:18 +02:00
1479a097d6 Fix alignment of old icons on buttons after TweetDeck update 2017-08-05 02:39:23 +02:00
b2be530f6b Remove legacy config file upgrade code 2017-08-01 19:29:01 +02:00
e4967ea46d Add paragraphs and level 1-2 headings to update notification markdown renderer 2017-08-01 17:33:47 +02:00
3f28f18fb4 Release 1.8.4.1 2017-08-01 17:11:34 +02:00
1b90e0f65e Slightly increase default notification height 2017-08-01 17:05:31 +02:00
756ed649e6 Change default avatar shape to square, rename 'Default' to 'Legacy' 2017-08-01 17:03:29 +02:00
fbc423e2a7 Fix like/retweet notifications having invisible space with notification media previews disabled 2017-08-01 16:59:25 +02:00
f04cdb6a13 Fix PropertyBridge not updating properly 2017-08-01 16:58:46 +02:00
63b58b1cfe Release 1.8.4 2017-08-01 15:07:03 +02:00
77e656d8e4 Tweak JS prompt dialog layout on high DPI 2017-08-01 15:06:09 +02:00
a673957bd0 Tweak JS prompt dialog layout 2017-08-01 14:54:21 +02:00
c99a0c9974 Add Layout & Design plugin button to the TweetDeck settings modal 2017-08-01 13:45:44 +02:00
0fb06d0ff2 Remove reply revert option from edit-design plugin 2017-08-01 12:28:52 +02:00
c51eebfe22 Add new unit tests for TwitterUtils and CombinedFileStream 2017-07-31 22:27:02 +02:00
a51b34b48f Move CommandLineArgsParser code to CommandLineArgs 2017-07-31 22:26:48 +02:00
1b239bada1 Delay screenshots again due to iframes 2017-07-31 21:17:31 +02:00
50ab1a6ac3 Improve login/logout page design 2017-07-31 20:29:07 +02:00
f181f1fadc Refactor PropertyBridge 2017-07-31 19:58:23 +02:00
c686349922 Refactor Program (tweak properties, move locking code) 2017-07-31 18:04:04 +02:00
5f44a1f4ad Fix semicolons in code.js 2017-07-31 14:58:42 +02:00
a968938832 Move square scrollbars from edit-design plugin to code.js 2017-07-31 14:55:31 +02:00
8d67f3dfdc Move code.js notification setup and fix dropdown border radius 2017-07-31 14:42:26 +02:00
973ae8cb5d Move twitter account regex to TwitterUtils 2017-07-31 14:31:32 +02:00
a4747b0d7b Add JS dialog handler to notifications 2017-07-31 14:25:00 +02:00
f07640cc84 Reorganize CEF handlers 2017-07-31 14:24:42 +02:00
c235c55b19 Add option to show media previews in notification 2017-07-31 14:12:24 +02:00
485ef684be Prevent notification keyboard controls from triggering in dev tools 2017-07-31 13:36:44 +02:00
7caca22e57 Remove 'TweetDuck' from JS dialog captions 2017-07-31 01:42:22 +02:00
f1d9e32bf5 Add keyboard controls to notifications
Closes #153
2017-07-31 01:23:57 +02:00
23d5fa3107 Tweak emoji keyboard border radius and character count width 2017-07-30 23:58:35 +02:00
4e7d8aba1c Improve FormMessage to match MessageBox closer and look better on high DPI 2017-07-30 23:50:24 +02:00
98ba871a71 Fix back mouse button ignoring columns inside User modals
Closes #155
2017-07-30 21:38:38 +02:00
3ff23c0264 Remove unnecessary TweetDeck logo CSS rule 2017-07-30 21:29:02 +02:00
e21f89477b Fix ISerializedObject not being removed from unit tests and csproj file 2017-07-30 21:28:26 +02:00
f177f514f5 Fix column type icons jumping when opening column settings 2017-07-30 21:19:03 +02:00
af30f3b348 Square-ify many elements of TweetDeck (buttons, inputs, dialogs, menus, previews) 2017-07-30 21:15:39 +02:00
82df618429 Fix code.js after refactoring CSS insertion 2017-07-30 21:13:45 +02:00
bb3538e270 Refocus tweet textarea after selecting a different account
Closes #156
2017-07-30 20:36:17 +02:00
71925e1126 Refactor parts of code.js (make code shorter, use 'let') 2017-07-30 20:19:59 +02:00
93c1cbd231 Update SystemConfig to use FileSerializer and migrate old files 2017-07-30 19:54:28 +02:00
894b890fe5 Tweak serialization code and remove ISerializedObject 2017-07-30 19:28:03 +02:00
8e9e8f7fad Fix magic number and add a comment 2017-07-30 19:02:30 +02:00
2a0461a76f Add safeguards for accessing TweetDeckBridge.LastHighlightedTweetImages 2017-07-21 12:43:10 +02:00
85f923a6fc Add StringUtils.EmptyArray and use it instead of new string[0] 2017-07-21 12:37:30 +02:00
b35e4d4d01 Add "Save all images as..." context menu option for tweets with multiple images 2017-07-21 12:14:15 +02:00
cb24a859f4 Fix file type description in Save image dialog 2017-07-21 11:16:47 +02:00
b1ef00746f Hide open/copy link context menu items for media previews 2017-07-21 11:07:40 +02:00
aebe82e3a7 Add context menu for image previews that use background-image 2017-07-21 10:46:28 +02:00
7c87856b4d Show waiting cursor while taking a tweet screenshot 2017-07-20 16:29:39 +02:00
d1b1dd539f Add an option to use :orig image links in context menu 2017-07-17 05:39:59 +02:00
55eea88ace Add twitter image link & download methods to TwitterUtils 2017-07-17 05:10:06 +02:00
a70f64e1f6 Move some stuff from BrowserUtils to a new TwitterUtils class 2017-07-17 02:09:20 +02:00
fa0cb120a7 Add a 'Close' button to the modal dialog in the template plugin
Closes #143
2017-07-13 05:57:12 +02:00
e3080d07dc Ensure plugin config exists after first run, fixes profile export crash
Closes #147
2017-07-13 05:21:22 +02:00
34726c533e Release 1.8.3 2017-07-09 20:17:33 +02:00
4a0d72d2cc Fix FormMessage icon position on high DPI 2017-07-09 17:24:01 +02:00
fe3fc5c9f7 Add WindowsUtils.CreateDirectoryForFile and use it 2017-07-09 14:12:27 +02:00
441228e2b0 Stop using BrandName in msg dialogs, update msg titles, fix mistakes from prev commits 2017-07-09 04:21:33 +02:00
7538aee4f2 Replace all MessageBox.Show calls with FormMessage 2017-07-09 03:50:04 +02:00
acf809268e Add many helper methods to FormMessage 2017-07-09 03:45:35 +02:00
4ebc0c10b6 Forgot something! 2017-07-09 02:55:48 +02:00
a453888ca2 Tweak new lines in FormMessage, add ControlType enum for FormMessage buttons 2017-07-09 02:40:37 +02:00
530b44762b Make \n the only new line character in FormMessage 2017-07-09 01:52:44 +02:00
f85587fb0b Bump emoji keyboard plugin version 2017-07-09 00:36:22 +02:00
edb8799b1a Update emoji keyboard w/ emoji 9.0, instructions, and code tweaks 2017-07-09 00:30:03 +02:00
e47aeb37f0 Designer, why 2017-07-08 20:19:22 +02:00
776e9968dc Fix tab order in Advanced tab in Options 2017-07-08 19:25:20 +02:00
1898bf4731 Add a tooltip to browser GC reload checkbox 2017-07-08 19:21:36 +02:00
78df020737 Add a modal with release info to update notifications
Closes #139
2017-07-08 18:00:00 +02:00
b93f9a4b9a Fix compose textarea not being focused after pasting an image in a reply 2017-07-08 03:17:20 +02:00
748b230ef5 Fix missing BrowserProcesses in project file after merge 2017-07-08 02:55:45 +02:00
deb8dde9e1 Merge pull request #141 from chylex/memory
Merge browser process identification & GC reload with memory threshold
2017-07-08 02:50:03 +02:00
dbb2f10754 Update from master 2017-07-08 02:49:21 +02:00
0ded03ab92 Fix more analysis violations (exceptions, native method pointers, form disposal) 2017-07-08 00:21:41 +02:00
2198e84f3b Fix subprocess NativeMethods to use pointers instead of value types 2017-07-07 23:58:45 +02:00
14d44528b0 Fuck CultureInfo some more and fix analysis violations (dispose pattern, lang features) 2017-07-07 23:53:04 +02:00
eb8159ca0f Add a tooltip to text box in the Sounds tab in Options 2017-07-07 23:49:57 +02:00
9811f40a53 Go fuck yourself CurrentCulture and stop messing with string interpolation 2017-07-07 22:56:36 +02:00
8de7e13aa3 Reorganize and refactor UserConfig and PluginConfig 2017-07-07 19:22:33 +02:00
c63e6a1e49 More refactoring (seal classes, fix names and comments) 2017-07-07 16:15:10 +02:00
5a21d2cb10 Add StringUtils with unit tests and use it 2017-07-07 15:52:13 +02:00
424c0e596c Add legacy config detection and replace UserConfig serialization with FileSerializer 2017-07-07 02:56:02 +02:00
d431b63c27 Add SingleTypeConverter and update names in FileSerializer 2017-07-07 01:47:14 +02:00
38c2781cd3 Add an enum test to FileSerializer unit test 2017-07-07 00:53:19 +02:00
796fb348a3 Add classes for serializing objects to/from text files 2017-07-07 00:48:00 +02:00
71b306d5fd Fix unit test project file after refactoring 2017-07-06 21:26:43 +02:00
4c610ea32d Move TweetDeck URL into a constant 2017-07-06 20:58:40 +02:00
4bff006743 Refactor (move files into different namespaces) 2017-07-06 20:58:06 +02:00
1645079bc0 Allow plugins to modify screenshot css and include a 'td-screenshot' body class 2017-07-06 03:47:59 +02:00
9afb58e4a7 Remove unused 'using' statement 2017-07-06 03:30:15 +02:00
2820fc8acf Fix some modals not closing when pressing the back button 2017-07-04 22:01:33 +02:00
4d77a498f6 Add a WIP memory tracker that runs GC reload, and fix config 2017-07-04 22:00:03 +02:00
d77de3bb12 Remove debug code 2017-06-30 23:53:36 +02:00
29e7ad6ce6 Add a way to track browser process IDs 2017-06-30 23:46:52 +02:00
1712b5120e Merge remote-tracking branch 'refs/remotes/origin/master' into memory 2017-06-30 20:47:22 +02:00
06c0153cf5 Fix tray restoration from another process if the original process is hung 2017-06-30 20:44:39 +02:00
44f7ecda6d Merge remote-tracking branch 'refs/remotes/origin/master' into memory 2017-06-30 20:17:21 +02:00
fb94bf1b80 Add WindowsUtils.IsChildProcess to check process parent 2017-06-30 20:14:49 +02:00
4818652582 Add current PID into WindowsUtils.CurrentProcessID and use it 2017-06-30 17:07:37 +02:00
c69b9784fc Add option to enable GC reload with a custom memory threshold (currently unused) 2017-06-30 16:47:31 +02:00
0ac244a3ea Merge remote-tracking branch 'refs/remotes/origin/master' into memory 2017-06-30 00:00:33 +02:00
19a445fdab Add a NumericUpDown control with a text suffix 2017-06-30 00:00:20 +02:00
c90a18a2c0 Merge remote-tracking branch 'refs/remotes/origin/master' into memory 2017-06-29 23:47:00 +02:00
502310c413 Prevent TrackBar from stealing focus when scrolling 2017-06-29 23:34:00 +02:00
6f9424d4ec Force GC cleanup when clicking 'Reload browser' 2017-06-29 18:21:09 +02:00
bb379fe667 Expose gc() in JS 2017-06-29 04:01:50 +02:00
0fd86bf214 Move CEF argument setup to BrowserUtils 2017-06-29 03:52:55 +02:00
29b75d4391 Release 1.8.2 2017-06-29 02:25:07 +02:00
a7124e5449 Fix FormMessage not scaling well with high DPI 2017-06-29 02:21:39 +02:00
a714f3480a Refactor Program.Restart and UpdaterSettings initialization 2017-06-28 23:09:49 +02:00
c10e0df898 Fix user profile modals not closing with back mouse button 2017-06-27 22:05:03 +02:00
fba734fd5a Fix new lines not being recognized in CEF argument parser 2017-06-27 20:28:55 +02:00
27e2372097 Add an option for custom notification window size 2017-06-27 17:54:52 +02:00
7f5b99495c Move some code (config, notifications, settings) 2017-06-27 16:49:37 +02:00
1efe2a56af Fix Win+Arrow and Win+Shift+Arrow not saving new window location
Closes #135
2017-06-26 20:23:32 +02:00
850873aec8 Add a delay to notification duration option to fix example notification flicker
Closes #136
2017-06-26 19:40:20 +02:00
d9e6afbf36 Replace Math.Round calls with a more convenient custom method 2017-06-26 17:33:07 +02:00
7f3bd2715c Enable high DPI support and fix all known DPI-related UI issues
Closes #99
2017-06-26 17:08:51 +02:00
c81cb393e9 Allow scroll speed option to use increments of 5 instead of 25 2017-06-26 16:22:52 +02:00
4800faa783 Fix a race condition crash in update checker events 2017-06-24 12:10:25 +02:00
1087b5e1d1 Release 1.8.1 2017-06-24 11:49:25 +02:00
5dc2e71976 Update timeline-polls plugin to work better with recent TweetDeck update 2017-06-23 22:01:54 +02:00
28eb7d0810 Fix scrollbar in Options not working after focusing certain controls 2017-06-23 19:01:09 +02:00
c641a92d89 Add a 'Remind me later' button to update notifications 2017-06-22 12:32:06 +02:00
3e57cc045f Redesign update notification buttons 2017-06-22 12:31:48 +02:00
c60a8ddf66 Rewrite and begin redesigning update dialog 2017-06-22 09:48:50 +02:00
8becef3e45 Reorganize Notifications tab in Options 2017-06-22 08:31:24 +02:00
3237634e3b Tweak button positions in Options 2017-06-22 08:11:59 +02:00
2a4a659e39 Make retweets lowercase again 2017-06-21 20:35:32 +02:00
fcaa47c0a8 Move 'Custom' notification location option 2017-06-21 17:52:02 +02:00
68ea17ccbd Move some notification options into a separate panel 2017-06-21 13:59:49 +02:00
80308c7102 Redesign and cleanup parts of Options 2017-06-21 11:27:56 +02:00
6f4a99a7cb Remove drop down button from follow notifications 2017-06-21 10:13:40 +02:00
ac245f5128 Fix retarded scroll bar behavior in Options 2017-06-21 09:07:50 +02:00
7f9e9e27a0 Move import/export/restore to a single dialog in FormSettings 2017-06-21 08:33:36 +02:00
abf58a4aec Add an option to change notification scroll speed 2017-06-21 05:56:10 +02:00
5d9a700a6e Move TrackBar value alignment code to ControlExtensions 2017-06-21 05:52:40 +02:00
3662b1eb1d Fix activated & notification icons in edit-design plugin
Closes #134
2017-06-21 04:59:08 +02:00
34449da2b8 Redo Options tabs to fit the new layout 2017-06-20 20:42:17 +02:00
42f367f822 Fix mouse wheel not working in Options form 2017-06-20 20:40:37 +02:00
781ca0bb77 Tweak Options form design 2017-06-20 18:37:46 +02:00
ebe5d50dae Remove TabButton and TabPanel 2017-06-20 16:36:29 +02:00
00d6dc5626 Rewrite Options form to have tabs on the left 2017-06-20 16:29:50 +02:00
a1648c307f Remove (now) redundant call to plugin panel resize event 2017-06-20 14:35:54 +02:00
b8f170ae39 Fix visual issues in Plugins form 2017-06-20 13:02:34 +02:00
073f1da5b4 Redesign Plugins form 2017-06-20 11:58:33 +02:00
6310711136 Add a vertical label control 2017-06-20 11:48:44 +02:00
fcac7a4ce1 Remove unused 'using' statement 2017-06-20 09:50:31 +02:00
c2b1aef810 Show official & custom plugins on one page 2017-06-20 09:50:11 +02:00
c8ab26275c Fix column list overflow after hiding app title 2017-06-19 23:32:15 +02:00
a1fd6a2b6b Bump edit-design plugin version 2017-06-19 23:15:51 +02:00
561c08e0cc Fix debug plugin after hiding app title 2017-06-19 23:06:21 +02:00
4658e30e89 Fix Twitter messing with retweet icon in edit-design plugin 2017-06-19 23:05:14 +02:00
225e6b369a Make selected theme an attribute in <html> for plugins 2017-06-19 16:18:48 +02:00
0e9094a19f Rewrite timeline-polls plugin (fix issues, support quoted tweets) 2017-06-19 16:18:42 +02:00
2da0e03c6c Fix emoji keyboard search not resetting when hiding the keyboard 2017-06-19 11:05:33 +02:00
9a6fac5fc8 Add extra height to tweet footer with old icons in edit-design plugin 2017-06-17 18:44:25 +02:00
b541f0a896 Redesign header (hide logo, tweak positions & border radius, fix compose btn icon) 2017-06-17 18:24:52 +02:00
cec7cce077 Fix dynamic column width in edit-design plugin for recent TweetDeck update 2017-06-17 17:40:18 +02:00
fb13695ca5 Release 1.8 2017-06-16 19:56:57 +02:00
20c76d06f7 Fix templates not triggering textarea change 2017-06-16 19:43:48 +02:00
339a11f649 Remove empty line in unit test code 2017-06-16 19:43:25 +02:00
0989400d87 Fix column header icon position with old icons in edit-design plugin 2017-06-16 18:32:19 +02:00
52aacf602d Tweak formatting of edit-design plugin 2017-06-16 18:30:36 +02:00
54d70a6a17 Fix edit-design plugin to revert icons in notifications too 2017-06-16 08:03:22 +02:00
d980e09e0f Fix {ajax} token resolve order in template plugin 2017-06-16 04:22:44 +02:00
2e4cb12817 Add template name check and safeguard empty ajax token in template plugin 2017-06-16 03:50:16 +02:00
7b91cb2e96 Fix escaping and update modal text in template plugin 2017-06-16 03:34:30 +02:00
95c04a8abc Work on template plugin (keep button active, tweak code) 2017-06-16 02:04:44 +02:00
25822fefdb Add option to revert icons to edit-design plugin 2017-06-16 00:40:23 +02:00
d800ee2d28 Fix template plugin to handle combining {ajax} and {cursor} together 2017-06-11 01:05:15 +02:00
2a51371aca Add {ajax} token to template plugin 2017-06-11 00:25:42 +02:00
ee5d1a47dc Add {cursor} token to template plugin 2017-06-10 23:26:04 +02:00
b330b74347 Implement token reading in template plugin 2017-06-10 23:25:55 +02:00
11fa13f0bb Tweak google analytics detection to work on twitter.com 2017-06-08 18:43:46 +02:00
21400d72b3 Block TweetDeck's google analytics script 2017-06-08 18:39:40 +02:00
a710cb9d4f Make middle click on tweet reply icon open the compose drawer 2017-06-08 14:17:46 +02:00
3326ad52ce Work on template plugin (basic template impl, modal tweaks) 2017-06-07 19:25:38 +02:00
c9560df851 Implement template management, buttons, and persistency in template plugin 2017-06-07 17:22:31 +02:00
74cb45118e Force tweet actions to stay visible when replying in edit-design plugin 2017-06-07 14:41:39 +02:00
c79bf19e51 Add a section with advanced tips to template plugin and update layout 2017-06-07 00:34:27 +02:00
961bec0a2f Add basic layout and styles for the template plugin 2017-06-06 23:23:06 +02:00
89e4977cd1 Bump version of emoji keyboard plugin 2017-06-06 11:11:57 +02:00
bfe16475db Tweak update installer to explicitly list needed files instead of excluding unneeded ones 2017-06-06 04:41:17 +02:00
915d36867c Fix emoji keyboard position and leaking outside the window 2017-06-06 03:27:30 +02:00
48435af407 Add emoji search 2017-06-05 23:07:31 +02:00
86b6ec5212 Tweak emoji names to only use one array when loading file 2017-06-05 21:56:43 +02:00
775e70bc45 Rewrite HTML generation in emoji-keyboard & add emoji names to elements 2017-06-05 21:54:39 +02:00
9f565447d0 Remove emoji characters from emoji-ordering.txt 2017-06-05 19:35:41 +02:00
88d27bc29d Add instructions for updating the emoji order file in emoji-keyboard plugin 2017-06-05 19:32:04 +02:00
172ae87ac6 Merge pull request #129 from chylex/remove_legacy
Remove legacy code (plugins and installers)
2017-06-05 17:49:19 +02:00
91d572235e Make $id and $token properties in plugin objects unmodifiable 2017-06-05 17:28:45 +02:00
64d32dcb75 Delete plugin properties when disabling them 2017-06-05 17:28:01 +02:00
564b4283b6 Rewrite plugin reloading when enabled/disabled and refactor core plugin scripts 2017-06-05 14:49:34 +02:00
ca4d374a81 Fix errors in edit-design plugin when disabling/enabling 2017-06-05 14:43:14 +02:00
a753806d7b Fix 'Edit CEF arguments' restart prompt and tweak dialog text 2017-06-03 18:03:34 +02:00
bd1692cea3 Rename Settings to Options 2017-06-03 17:24:43 +02:00
b7ce089f08 Fix audio playback error leading to the wrong tab in Settings 2017-06-03 17:03:33 +02:00
8a6b47c5db Fix naming and tooltips in profile export/import UI 2017-06-03 16:37:23 +02:00
9f1fc4df18 Decrease compression level in update installer 2017-06-03 16:03:31 +02:00
c018a2a7bc Move sound notification handling to a separate library 2017-06-03 15:32:18 +02:00
a1aebab114 Update installers (remove outdated code, exclude .txt files in updater) 2017-06-03 15:30:10 +02:00
e30702e1d8 Move CefSharp license to bld/Resources/ 2017-06-03 13:01:39 +02:00
008ff4b055 Fix edit-design plugin resetting TweetDeck settings on first run 2017-05-31 18:33:21 +02:00
d7bba22e19 Remove legacy configuration for list of disabled plugins 2017-05-31 17:57:55 +02:00
2b9a910533 Remove legacy installation data from installers 2017-05-31 17:17:47 +02:00
118ebcc627 Fix update installer removing devtools_resources.pak 2017-05-28 18:19:37 +02:00
186 changed files with 9966 additions and 5458 deletions

View File

@@ -1,5 +1,5 @@
using System; using System;
using TweetDuck.Core.Utils; using TweetDuck.Data;
namespace TweetDuck.Configuration{ namespace TweetDuck.Configuration{
static class Arguments{ static class Arguments{
@@ -12,6 +12,7 @@ namespace TweetDuck.Configuration{
// internal args // internal args
public const string ArgRestart = "-restart"; public const string ArgRestart = "-restart";
public const string ArgImportCookies = "-importcookies"; public const string ArgImportCookies = "-importcookies";
public const string ArgDeleteCookies = "-deletecookies";
public const string ArgUpdated = "-updated"; public const string ArgUpdated = "-updated";
// class data and methods // class data and methods
@@ -29,6 +30,7 @@ namespace TweetDuck.Configuration{
CommandLineArgs args = Current.Clone(); CommandLineArgs args = Current.Clone();
args.RemoveFlag(ArgRestart); args.RemoveFlag(ArgRestart);
args.RemoveFlag(ArgImportCookies); args.RemoveFlag(ArgImportCookies);
args.RemoveFlag(ArgDeleteCookies);
args.RemoveFlag(ArgUpdated); args.RemoveFlag(ArgUpdated);
return args; return args;
} }

View File

@@ -1,29 +1,28 @@
using System; using System;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Threading;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetLib.Communication;
namespace TweetDuck.Configuration{ namespace TweetDuck.Configuration{
sealed class LockManager{ sealed class LockManager{
private const int RetryDelay = 250;
public enum Result{ public enum Result{
Success, HasProcess, Fail Success, HasProcess, Fail
} }
public Process LockingProcess { get; private set; }
private readonly string file; private readonly string file;
private FileStream lockStream; private FileStream lockStream;
private Process lockingProcess;
public LockManager(string file){ public LockManager(string file){
this.file = file; this.file = file;
} }
private void CreateLockFileStream(){ // Lock file
lockStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read);
WriteIntToStream(lockStream, GetCurrentProcessId());
lockStream.Flush(true);
}
private bool ReleaseLockFileStream(){ private bool ReleaseLockFileStream(){
if (lockStream != null){ if (lockStream != null){
@@ -37,8 +36,10 @@ namespace TweetDuck.Configuration{
} }
private Result TryCreateLockFile(){ private Result TryCreateLockFile(){
if (lockStream != null){ void CreateLockFileStream(){
throw new InvalidOperationException("Lock file already exists."); lockStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read);
lockStream.Write(BitConverter.GetBytes(WindowsUtils.CurrentProcessID), 0, sizeof(int));
lockStream.Flush(true);
} }
try{ try{
@@ -60,6 +61,8 @@ namespace TweetDuck.Configuration{
} }
} }
// Lock management
public Result Lock(){ public Result Lock(){
if (lockStream != null){ if (lockStream != null){
return Result.Success; return Result.Success;
@@ -72,7 +75,9 @@ namespace TweetDuck.Configuration{
int pid; int pid;
using(FileStream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)){ using(FileStream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)){
pid = ReadIntFromStream(fileStream); byte[] bytes = new byte[sizeof(int)];
fileStream.Read(bytes, 0, bytes.Length);
pid = BitConverter.ToInt32(bytes, 0);
} }
try{ try{
@@ -80,7 +85,7 @@ namespace TweetDuck.Configuration{
using(Process currentProcess = Process.GetCurrentProcess()){ using(Process currentProcess = Process.GetCurrentProcess()){
if (foundProcess.MainModule.FileVersionInfo.InternalName == currentProcess.MainModule.FileVersionInfo.InternalName){ if (foundProcess.MainModule.FileVersionInfo.InternalName == currentProcess.MainModule.FileVersionInfo.InternalName){
LockingProcess = foundProcess; lockingProcess = foundProcess;
} }
else{ else{
foundProcess.Close(); foundProcess.Close();
@@ -91,7 +96,7 @@ namespace TweetDuck.Configuration{
// Process.MainModule can throw exceptions in some cases // Process.MainModule can throw exceptions in some cases
} }
return LockingProcess == null ? Result.Fail : Result.HasProcess; return lockingProcess == null ? Result.Fail : Result.HasProcess;
}catch{ }catch{
return Result.Fail; return Result.Fail;
} }
@@ -100,45 +105,72 @@ namespace TweetDuck.Configuration{
return initialResult; return initialResult;
} }
public bool Unlock(){ public Result LockWait(int timeout){
bool result = true; for(int elapsed = 0; elapsed < timeout; elapsed += RetryDelay){
Result result = Lock();
if (result == Result.HasProcess){
Thread.Sleep(RetryDelay);
}
else{
return result;
}
}
return Lock();
}
public bool Unlock(){
if (ReleaseLockFileStream()){ if (ReleaseLockFileStream()){
try{ try{
File.Delete(file); File.Delete(file);
}catch(Exception e){ }catch(Exception e){
Program.Reporter.Log(e.ToString()); Program.Reporter.Log(e.ToString());
result = false; return false;
} }
} }
return result; return true;
}
// Locking process
public bool RestoreLockingProcess(int failTimeout){
if (lockingProcess != null){
if (lockingProcess.MainWindowHandle == IntPtr.Zero){ // restore if the original process is in tray
Comms.BroadcastMessage(Program.WindowRestoreMessage, (uint)lockingProcess.Id, 0);
if (WindowsUtils.TrySleepUntil(() => CheckLockingProcessExited() || (lockingProcess.MainWindowHandle != IntPtr.Zero && lockingProcess.Responding), failTimeout, RetryDelay)){
return true;
}
}
}
return false;
} }
public bool CloseLockingProcess(int closeTimeout, int killTimeout){ public bool CloseLockingProcess(int closeTimeout, int killTimeout){
if (LockingProcess != null){ if (lockingProcess != null){
try{ try{
if (LockingProcess.CloseMainWindow()){ if (lockingProcess.CloseMainWindow()){
WindowsUtils.TrySleepUntil(CheckLockingProcessExited, closeTimeout, 250); WindowsUtils.TrySleepUntil(CheckLockingProcessExited, closeTimeout, RetryDelay);
} }
if (!LockingProcess.HasExited){ if (!lockingProcess.HasExited){
LockingProcess.Kill(); lockingProcess.Kill();
WindowsUtils.TrySleepUntil(CheckLockingProcessExited, killTimeout, 250); WindowsUtils.TrySleepUntil(CheckLockingProcessExited, killTimeout, RetryDelay);
} }
if (LockingProcess.HasExited){ if (lockingProcess.HasExited){
LockingProcess.Dispose(); lockingProcess.Dispose();
LockingProcess = null; lockingProcess = null;
return true; return true;
} }
}catch(Exception ex){ }catch(Exception ex){
if (ex is InvalidOperationException || ex is Win32Exception){ if (ex is InvalidOperationException || ex is Win32Exception){
if (LockingProcess != null){ if (lockingProcess != null){
LockingProcess.Refresh(); bool hasExited = CheckLockingProcessExited();
lockingProcess.Dispose();
bool hasExited = LockingProcess.HasExited;
LockingProcess.Dispose();
return hasExited; return hasExited;
} }
} }
@@ -150,27 +182,8 @@ namespace TweetDuck.Configuration{
} }
private bool CheckLockingProcessExited(){ private bool CheckLockingProcessExited(){
LockingProcess.Refresh(); lockingProcess.Refresh();
return LockingProcess.HasExited; return lockingProcess.HasExited;
}
// Utility functions
private static void WriteIntToStream(Stream stream, int value){
byte[] id = BitConverter.GetBytes(value);
stream.Write(id, 0, id.Length);
}
private static int ReadIntFromStream(Stream stream){
byte[] bytes = new byte[4];
stream.Read(bytes, 0, 4);
return BitConverter.ToInt32(bytes, 0);
}
private static int GetCurrentProcessId(){
using(Process process = Process.GetCurrentProcess()){
return process.Id;
}
} }
} }
} }

View File

@@ -1,45 +1,41 @@
using System; using System;
using System.IO; using System.IO;
using TweetDuck.Core.Utils;
using TweetDuck.Data.Serialization;
namespace TweetDuck.Configuration{ namespace TweetDuck.Configuration{
sealed class SystemConfig{ sealed class SystemConfig{
private static readonly FileSerializer<SystemConfig> Serializer = new FileSerializer<SystemConfig>();
public static readonly bool IsHardwareAccelerationSupported = File.Exists(Path.Combine(Program.ProgramPath, "libEGL.dll")) && public static readonly bool IsHardwareAccelerationSupported = File.Exists(Path.Combine(Program.ProgramPath, "libEGL.dll")) &&
File.Exists(Path.Combine(Program.ProgramPath, "libGLESv2.dll")); File.Exists(Path.Combine(Program.ProgramPath, "libGLESv2.dll"));
// CONFIGURATION DATA
private bool _hardwareAcceleration = true;
public bool EnableBrowserGCReload { get; set; } = true;
public int BrowserMemoryThreshold { get; set; } = 400;
// SPECIAL PROPERTIES
public bool HardwareAcceleration{ public bool HardwareAcceleration{
get => hardwareAcceleration && IsHardwareAccelerationSupported; get => _hardwareAcceleration && IsHardwareAccelerationSupported;
set => hardwareAcceleration = value; set => _hardwareAcceleration = value;
} }
// END OF CONFIG
private readonly string file; private readonly string file;
private bool hardwareAcceleration;
private SystemConfig(string file){ private SystemConfig(string file){
this.file = file; this.file = file;
HardwareAcceleration = true;
}
private void WriteToStream(Stream stream){
stream.WriteByte((byte)(HardwareAcceleration ? 1 : 0));
}
private void ReadFromStream(Stream stream){
HardwareAcceleration = stream.ReadByte() > 0;
} }
public bool Save(){ public bool Save(){
try{ try{
string directory = Path.GetDirectoryName(file); WindowsUtils.CreateDirectoryForFile(file);
if (directory == null)return false; Serializer.Write(file, this);
Directory.CreateDirectory(directory);
using(Stream stream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None)){
WriteToStream(stream);
}
return true; return true;
}catch(Exception e){ }catch(Exception e){
Program.Reporter.HandleException("Configuration Error", "Could not save the system configuration file.", true, e); Program.Reporter.HandleException("Configuration Error", "Could not save the system configuration file.", true, e);
@@ -51,9 +47,7 @@ namespace TweetDuck.Configuration{
SystemConfig config = new SystemConfig(file); SystemConfig config = new SystemConfig(file);
try{ try{
using(Stream stream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read)){ Serializer.Read(file, config);
config.ReadFromStream(stream);
}
}catch(FileNotFoundException){ }catch(FileNotFoundException){
}catch(DirectoryNotFoundException){ }catch(DirectoryNotFoundException){
}catch(Exception e){ }catch(Exception e){

View File

@@ -1,201 +1,157 @@
using System; using System;
using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using TweetDuck.Core; using TweetDuck.Core;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Notification; using TweetDuck.Core.Notification;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetDuck.Plugins; using TweetDuck.Data;
using TweetDuck.Data.Serialization;
namespace TweetDuck.Configuration{ namespace TweetDuck.Configuration{
[Serializable]
sealed class UserConfig{ sealed class UserConfig{
private static readonly IFormatter Formatter = new BinaryFormatter{ Binder = new LegacyBinder() }; private static readonly FileSerializer<UserConfig> Serializer = new FileSerializer<UserConfig>{ HandleUnknownProperties = HandleUnknownProperties };
private class LegacyBinder : SerializationBinder{ private static void HandleUnknownProperties(UserConfig obj, Dictionary<string, string> data){
public override Type BindToType(string assemblyName, string typeName){ data.Remove("EnableBrowserGCReload");
return Type.GetType(string.Format("{0}, {1}", typeName.Replace("TweetDck", "TweetDuck"), assemblyName.Replace("TweetDck", "TweetDuck"))); data.Remove("BrowserMemoryThreshold");
if (data.Count == 0){
obj.Save();
} }
} }
private const int CurrentFileVersion = 9; static UserConfig(){
Serializer.RegisterTypeConverter(typeof(WindowState), WindowState.Converter);
// START OF CONFIGURATION Serializer.RegisterTypeConverter(typeof(Point), new SingleTypeConverter<Point>{
ConvertToString = value => $"{value.X} {value.Y}",
ConvertToObject = value => {
int[] elements = StringUtils.ParseInts(value, ' ');
return new Point(elements[0], elements[1]);
}
});
public WindowState BrowserWindow { get; set; } Serializer.RegisterTypeConverter(typeof(Size), new SingleTypeConverter<Size>{
public bool DisplayNotificationColumn { get; set; } ConvertToString = value => $"{value.Width} {value.Height}",
public bool DisplayNotificationTimer { get; set; } ConvertToObject = value => {
public bool NotificationTimerCountDown { get; set; } int[] elements = StringUtils.ParseInts(value, ' ');
public bool NotificationSkipOnLinkClick { get; set; } return new Size(elements[0], elements[1]);
public bool NotificationNonIntrusiveMode { get; set; } }
});
}
public TweetNotification.Position NotificationPosition { get; set; } // CONFIGURATION DATA
public Point CustomNotificationPosition { get; set; }
public int NotificationEdgeDistance { get; set; }
public int NotificationDisplay { get; set; }
public int NotificationIdlePauseSeconds { get; set; }
public int NotificationDurationValue { get; set; }
public bool EnableSpellCheck { get; set; } public WindowState BrowserWindow { get; set; } = new WindowState();
public bool ExpandLinksOnHover { get; set; } public WindowState PluginsWindow { get; set; } = new WindowState();
public bool SwitchAccountSelectors { get; set; }
public bool EnableTrayHighlight { get; set; }
public bool EnableUpdateCheck { get; set; } public bool ExpandLinksOnHover { get; set; } = true;
public string DismissedUpdate { get; set; } public bool SwitchAccountSelectors { get; set; } = true;
public bool OpenSearchInFirstColumn { get; set; } = true;
public bool BestImageQuality { get; set; } = true;
public bool EnableSpellCheck { get; set; } = false;
public int VideoPlayerVolume { get; set; } = 50;
private int _zoomLevel = 100;
private bool _muteNotifications;
[Obsolete] public PluginConfig Plugins { get; set; } // TODO remove eventually private TrayIcon.Behavior _trayBehavior = TrayIcon.Behavior.Disabled;
public WindowState PluginsWindow { get; set; } public bool EnableTrayHighlight { get; set; } = true;
public string CustomCefArgs { get; set; } public bool EnableUpdateCheck { get; set; } = true;
public string CustomBrowserCSS { get; set; } public string DismissedUpdate { get; set; } = null;
public string CustomNotificationCSS { get; set; }
public bool DisplayNotificationColumn { get; set; } = false;
public bool NotificationMediaPreviews { get; set; } = true;
public bool NotificationSkipOnLinkClick { get; set; } = false;
public bool NotificationNonIntrusiveMode { get; set; } = true;
public int NotificationIdlePauseSeconds { get; set; } = 0;
public bool DisplayNotificationTimer { get; set; } = true;
public bool NotificationTimerCountDown { get; set; } = false;
public int NotificationDurationValue { get; set; } = 25;
public TweetNotification.Position NotificationPosition { get; set; } = TweetNotification.Position.TopRight;
public Point CustomNotificationPosition { get; set; } = ControlExtensions.InvisibleLocation;
public int NotificationDisplay { get; set; } = 0;
public int NotificationEdgeDistance { get; set; } = 8;
public TweetNotification.Size NotificationSize { get; set; } = TweetNotification.Size.Auto;
public Size CustomNotificationSize { get; set; } = Size.Empty;
public int NotificationScrollSpeed { get; set; } = 10;
public int NotificationSoundVolume { get; set; } = 100;
private string _notificationSoundPath;
public string CustomCefArgs { get; set; } = null;
public string CustomBrowserCSS { get; set; } = null;
public string CustomNotificationCSS { get; set; } = null;
// SPECIAL PROPERTIES
public bool IsCustomNotificationPositionSet => CustomNotificationPosition != ControlExtensions.InvisibleLocation; public bool IsCustomNotificationPositionSet => CustomNotificationPosition != ControlExtensions.InvisibleLocation;
public bool IsCustomNotificationSizeSet => CustomNotificationSize != Size.Empty;
public TwitterUtils.ImageQuality TwitterImageQuality => BestImageQuality ? TwitterUtils.ImageQuality.Orig : TwitterUtils.ImageQuality.Default;
public string NotificationSoundPath{ public string NotificationSoundPath{
get => string.IsNullOrEmpty(notificationSoundPath) ? string.Empty : notificationSoundPath; get => string.IsNullOrEmpty(_notificationSoundPath) ? string.Empty : _notificationSoundPath;
set => notificationSoundPath = value; set => _notificationSoundPath = value;
} }
public bool MuteNotifications{ public bool MuteNotifications{
get => muteNotifications; get => _muteNotifications;
set{ set{
if (muteNotifications != value){ if (_muteNotifications != value){
muteNotifications = value; _muteNotifications = value;
MuteToggled?.Invoke(this, new EventArgs()); MuteToggled?.Invoke(this, EventArgs.Empty);
} }
} }
} }
public int ZoomLevel{ public int ZoomLevel{
get => zoomLevel; get => _zoomLevel;
set{ set{
if (zoomLevel != value){ if (_zoomLevel != value){
zoomLevel = value; _zoomLevel = value;
ZoomLevelChanged?.Invoke(this, new EventArgs()); ZoomLevelChanged?.Invoke(this, EventArgs.Empty);
} }
} }
} }
public double ZoomMultiplier => zoomLevel/100.0; public double ZoomMultiplier => _zoomLevel/100.0;
public TrayIcon.Behavior TrayBehavior{ public TrayIcon.Behavior TrayBehavior{
get => trayBehavior; get => _trayBehavior;
set{ set{
if (trayBehavior != value){ if (_trayBehavior != value){
trayBehavior = value; _trayBehavior = value;
TrayBehaviorChanged?.Invoke(this, new EventArgs()); TrayBehaviorChanged?.Invoke(this, EventArgs.Empty);
} }
} }
} }
// END OF CONFIGURATION // EVENTS
[field:NonSerialized]
public event EventHandler MuteToggled; public event EventHandler MuteToggled;
[field:NonSerialized]
public event EventHandler ZoomLevelChanged; public event EventHandler ZoomLevelChanged;
[field:NonSerialized]
public event EventHandler TrayBehaviorChanged; public event EventHandler TrayBehaviorChanged;
[NonSerialized] // END OF CONFIG
private string file;
private int fileVersion; private readonly string file;
private bool muteNotifications;
private int zoomLevel;
private string notificationSoundPath;
private TrayIcon.Behavior trayBehavior;
private UserConfig(string file){ private UserConfig(string file){
this.file = file; this.file = file;
BrowserWindow = new WindowState();
ZoomLevel = 100;
DisplayNotificationTimer = true;
NotificationNonIntrusiveMode = true;
NotificationPosition = TweetNotification.Position.TopRight;
CustomNotificationPosition = ControlExtensions.InvisibleLocation;
NotificationEdgeDistance = 8;
NotificationDurationValue = 25;
EnableUpdateCheck = true;
ExpandLinksOnHover = true;
SwitchAccountSelectors = true;
EnableTrayHighlight = true;
PluginsWindow = new WindowState();
}
private void UpgradeFile(){
if (fileVersion == CurrentFileVersion){
return;
}
// if outdated, cycle through all versions
if (fileVersion == 0){
DisplayNotificationTimer = true;
EnableUpdateCheck = true;
++fileVersion;
}
if (fileVersion == 1){
ExpandLinksOnHover = true;
++fileVersion;
}
if (fileVersion == 2){
BrowserWindow = new WindowState();
PluginsWindow = new WindowState();
++fileVersion;
}
if (fileVersion == 3){
EnableTrayHighlight = true;
NotificationDurationValue = 25;
++fileVersion;
}
if (fileVersion == 4){
++fileVersion;
}
if (fileVersion == 5){
++fileVersion;
}
if (fileVersion == 6){
NotificationNonIntrusiveMode = true;
++fileVersion;
}
if (fileVersion == 7){
ZoomLevel = 100;
++fileVersion;
}
if (fileVersion == 8){
SwitchAccountSelectors = true;
++fileVersion;
}
// update the version
fileVersion = CurrentFileVersion;
Save();
} }
public bool Save(){ public bool Save(){
try{ try{
string directory = Path.GetDirectoryName(file); WindowsUtils.CreateDirectoryForFile(file);
if (directory == null)return false;
Directory.CreateDirectory(directory);
if (File.Exists(file)){ if (File.Exists(file)){
string backupFile = GetBackupFile(file); string backupFile = GetBackupFile(file);
@@ -203,10 +159,7 @@ namespace TweetDuck.Configuration{
File.Move(file, backupFile); File.Move(file, backupFile);
} }
using(Stream stream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None)){ Serializer.Write(file, this);
Formatter.Serialize(stream, this);
}
return true; return true;
}catch(Exception e){ }catch(Exception e){
Program.Reporter.HandleException("Configuration Error", "Could not save the configuration file.", true, e); Program.Reporter.HandleException("Configuration Error", "Could not save the configuration file.", true, e);
@@ -214,20 +167,37 @@ namespace TweetDuck.Configuration{
} }
} }
public bool Reload(){
try{
LoadInternal(false);
return true;
}catch(FileNotFoundException){
try{
Serializer.Write(file, new UserConfig(file));
LoadInternal(false);
return true;
}catch(Exception e){
Program.Reporter.HandleException("Configuration Error", "Could not regenerate configuration file.", true, e);
return false;
}
}catch(Exception e){
Program.Reporter.HandleException("Configuration Error", "Could not reload configuration file.", true, e);
return false;
}
}
private void LoadInternal(bool backup){
Serializer.Read(backup ? GetBackupFile(file) : file, this);
}
public static UserConfig Load(string file){ public static UserConfig Load(string file){
UserConfig config = null;
Exception firstException = null; Exception firstException = null;
for(int attempt = 0; attempt < 2; attempt++){ for(int attempt = 0; attempt < 2; attempt++){
try{ try{
using(Stream stream = new FileStream(attempt == 0 ? file : GetBackupFile(file), FileMode.Open, FileAccess.Read, FileShare.Read)){ UserConfig config = new UserConfig(file);
if ((config = Formatter.Deserialize(stream) as UserConfig) != null){ config.LoadInternal(attempt > 0);
config.file = file; return config;
}
}
config?.UpgradeFile();
break;
}catch(FileNotFoundException){ }catch(FileNotFoundException){
}catch(DirectoryNotFoundException){ }catch(DirectoryNotFoundException){
break; break;
@@ -236,17 +206,22 @@ namespace TweetDuck.Configuration{
firstException = e; firstException = e;
Program.Reporter.Log(e.ToString()); Program.Reporter.Log(e.ToString());
} }
else if (firstException is FormatException){
Program.Reporter.HandleException("Configuration Error", "The configuration file is outdated or corrupted. If you continue, your program options will be reset.", true, e);
return new UserConfig(file);
}
else if (firstException != null){ else if (firstException != null){
Program.Reporter.HandleException("Configuration Error", "Could not open the backup configuration file. If you continue, you may lose your settings and list of enabled plugins.", true, e); Program.Reporter.HandleException("Configuration Error", "Could not open the backup configuration file. If you continue, your program options will be reset.", true, e);
return new UserConfig(file);
} }
} }
} }
if (firstException != null && config == null){ if (firstException != null){
Program.Reporter.HandleException("Configuration Error", "Could not open the configuration file.", true, firstException); Program.Reporter.HandleException("Configuration Error", "Could not open the configuration file.", true, firstException);
} }
return config ?? new UserConfig(file); return new UserConfig(file);
} }
public static string GetBackupFile(string file){ public static string GetBackupFile(string file){

View File

@@ -1,45 +1,33 @@
using System; using System.Text;
using System.Text;
namespace TweetDuck.Core.Bridge{ namespace TweetDuck.Core.Bridge{
static class PropertyBridge{ static class PropertyBridge{
[Flags] public enum Environment{
public enum Properties{ Browser, Notification
ExpandLinksOnHover = 1,
MuteNotifications = 2,
HasCustomNotificationSound = 4,
SkipOnLinkClick = 8,
SwitchAccountSelectors = 16,
AllBrowser = ExpandLinksOnHover | SwitchAccountSelectors | MuteNotifications | HasCustomNotificationSound,
AllNotification = ExpandLinksOnHover | SkipOnLinkClick
} }
public static string GenerateScript(Properties properties){ public static string GenerateScript(Environment environment){
StringBuilder build = new StringBuilder(); string Bool(bool value){
build.Append("(function(c){"); return value ? "true;" : "false;";
if (properties.HasFlag(Properties.ExpandLinksOnHover)){
build.Append("c.expandLinksOnHover=").Append(Program.UserConfig.ExpandLinksOnHover ? "true;" : "false;");
} }
if (properties.HasFlag(Properties.SwitchAccountSelectors)){ StringBuilder build = new StringBuilder().Append("(function(x){");
build.Append("c.switchAccountSelectors=").Append(Program.UserConfig.SwitchAccountSelectors ? "true;" : "false;");
build.Append("x.expandLinksOnHover=").Append(Bool(Program.UserConfig.ExpandLinksOnHover));
if (environment == Environment.Browser){
build.Append("x.switchAccountSelectors=").Append(Bool(Program.UserConfig.SwitchAccountSelectors));
build.Append("x.openSearchInFirstColumn=").Append(Bool(Program.UserConfig.OpenSearchInFirstColumn));
build.Append("x.muteNotifications=").Append(Bool(Program.UserConfig.MuteNotifications));
build.Append("x.hasCustomNotificationSound=").Append(Bool(Program.UserConfig.NotificationSoundPath.Length > 0));
build.Append("x.notificationMediaPreviews=").Append(Bool(Program.UserConfig.NotificationMediaPreviews));
} }
if (properties.HasFlag(Properties.MuteNotifications)){ if (environment == Environment.Notification){
build.Append("c.muteNotifications=").Append(Program.UserConfig.MuteNotifications ? "true;" : "false;"); build.Append("x.skipOnLinkClick=").Append(Bool(Program.UserConfig.NotificationSkipOnLinkClick));
} }
if (properties.HasFlag(Properties.HasCustomNotificationSound)){ return build.Append("})(window.$TDX=window.$TDX||{})").ToString();
build.Append("c.hasCustomNotificationSound=").Append(Program.UserConfig.NotificationSoundPath.Length > 0 ? "true;" : "false;");
}
if (properties.HasFlag(Properties.SkipOnLinkClick)){
build.Append("c.skipOnLinkClick=").Append(Program.UserConfig.NotificationSkipOnLinkClick ? "true;" : "false;");
}
build.Append("})(window.$TDX=window.$TDX||{})");
return build.ToString();
} }
} }
} }

View File

@@ -1,16 +1,41 @@
using System.Windows.Forms; using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using CefSharp;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Handling;
using TweetDuck.Core.Notification; using TweetDuck.Core.Notification;
using TweetDuck.Core.Other;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetDuck.Resources;
namespace TweetDuck.Core.Bridge{ namespace TweetDuck.Core.Bridge{
sealed class TweetDeckBridge{ sealed class TweetDeckBridge{
public static string LastRightClickedLink = string.Empty; public static string LastHighlightedTweetUrl = string.Empty;
public static string LastHighlightedTweet = string.Empty; public static string LastHighlightedQuoteUrl = string.Empty;
public static string LastHighlightedQuotedTweet = string.Empty; private static string LastHighlightedTweetAuthors = string.Empty;
private static string LastHighlightedTweetImages = string.Empty;
public static string[] LastHighlightedTweetAuthorsArray => LastHighlightedTweetAuthors.Split(';');
public static string[] LastHighlightedTweetImagesArray => LastHighlightedTweetImages.Split(';');
private static readonly Dictionary<string, string> SessionData = new Dictionary<string, string>(2);
public static void ResetStaticProperties(){ public static void ResetStaticProperties(){
LastRightClickedLink = LastHighlightedTweet = LastHighlightedQuotedTweet = string.Empty; LastHighlightedTweetUrl = LastHighlightedQuoteUrl = LastHighlightedTweetAuthors = LastHighlightedTweetImages = string.Empty;
}
public static void RestoreSessionData(IFrame frame){
if (SessionData.Count > 0){
StringBuilder build = new StringBuilder().Append("window.TD_SESSION={");
foreach(KeyValuePair<string, string> kvp in SessionData){
build.Append(kvp.Key).Append(":'").Append(kvp.Value.Replace("'", "\\'")).Append("',");
}
ScriptLoader.ExecuteScript(frame, build.Append("}").ToString(), "gen:session");
SessionData.Clear();
}
} }
private readonly FormBrowser form; private readonly FormBrowser form;
@@ -33,14 +58,16 @@ namespace TweetDuck.Core.Bridge{
}); });
} }
public void SetLastRightClickedLink(string link){ public void SetLastRightClickInfo(string type, string link){
form.InvokeAsyncSafe(() => LastRightClickedLink = link); form.InvokeAsyncSafe(() => ContextMenuBase.SetContextInfo(type, link));
} }
public void SetLastHighlightedTweet(string link, string quotedLink){ public void SetLastHighlightedTweet(string tweetUrl, string quoteUrl, string authors, string imageList){
form.InvokeAsyncSafe(() => { form.InvokeAsyncSafe(() => {
LastHighlightedTweet = link; LastHighlightedTweetUrl = tweetUrl;
LastHighlightedQuotedTweet = quotedLink; LastHighlightedQuoteUrl = quoteUrl;
LastHighlightedTweetAuthors = authors;
LastHighlightedTweetImages = imageList;
}); });
} }
@@ -48,10 +75,10 @@ namespace TweetDuck.Core.Bridge{
form.InvokeAsyncSafe(form.OpenContextMenu); form.InvokeAsyncSafe(form.OpenContextMenu);
} }
public void OnTweetPopup(string columnName, string tweetHtml, int tweetCharacters, string tweetUrl, string quoteUrl){ public void OnTweetPopup(string columnId, string chirpId, string columnName, string tweetHtml, int tweetCharacters, string tweetUrl, string quoteUrl){
notification.InvokeAsyncSafe(() => { notification.InvokeAsyncSafe(() => {
form.OnTweetNotification(); form.OnTweetNotification();
notification.ShowNotification(new TweetNotification(columnName, tweetHtml, tweetCharacters, tweetUrl, quoteUrl)); notification.ShowNotification(new TweetNotification(columnId, chirpId, columnName, tweetHtml, tweetCharacters, tweetUrl, quoteUrl));
}); });
} }
@@ -71,6 +98,12 @@ namespace TweetDuck.Core.Bridge{
} }
} }
public void SetSessionData(string key, string value){
form.InvokeSafe(() => { // do not use InvokeAsyncSafe, return only after invocation
SessionData.Add(key, value);
});
}
public void LoadNextNotification(){ public void LoadNextNotification(){
notification.InvokeAsyncSafe(notification.FinishCurrentNotification); notification.InvokeAsyncSafe(notification.FinishCurrentNotification);
} }
@@ -79,16 +112,20 @@ namespace TweetDuck.Core.Bridge{
form.InvokeAsyncSafe(() => form.OnTweetScreenshotReady(html, width, height)); form.InvokeAsyncSafe(() => form.OnTweetScreenshotReady(html, width, height));
} }
public void PlayVideo(string url){
form.InvokeAsyncSafe(() => form.PlayVideo(url));
}
public void FixClipboard(){ public void FixClipboard(){
form.InvokeAsyncSafe(WindowsUtils.ClipboardStripHtmlStyles); form.InvokeAsyncSafe(WindowsUtils.ClipboardStripHtmlStyles);
} }
public int GetIdleSeconds(){ public void OpenBrowser(string url){
return NativeMethods.GetIdleSeconds(); form.InvokeAsyncSafe(() => BrowserUtils.OpenExternalBrowser(url));
} }
public void OpenBrowser(string url){ public int GetIdleSeconds(){
BrowserUtils.OpenExternalBrowser(url); return NativeMethods.GetIdleSeconds();
} }
public void Alert(string type, string contents){ public void Alert(string type, string contents){
@@ -101,7 +138,7 @@ namespace TweetDuck.Core.Bridge{
default: icon = MessageBoxIcon.None; break; default: icon = MessageBoxIcon.None; break;
} }
MessageBox.Show(contents, Program.BrandName+" Browser Message", MessageBoxButtons.OK, icon); FormMessage.Show("TweetDuck Browser Message", contents, icon, FormMessage.OK);
} }
public void CrashDebug(string message){ public void CrashDebug(string message){

View File

@@ -1,8 +1,7 @@
using System; using System;
using System.Drawing; using System.Drawing;
using System.Linq; using System.Linq;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Utils;
namespace TweetDuck.Core.Controls{ namespace TweetDuck.Core.Controls{
static class ControlExtensions{ static class ControlExtensions{
@@ -21,6 +20,12 @@ namespace TweetDuck.Core.Controls{
control.BeginInvoke(func); control.BeginInvoke(func);
} }
public static float GetDPIScale(this Control control){
using(Graphics graphics = control.CreateGraphics()){
return graphics.DpiY/96F;
}
}
public static bool IsFullyOutsideView(this Form form){ public static bool IsFullyOutsideView(this Form form){
return !Screen.AllScreens.Any(screen => screen.WorkingArea.IntersectsWith(form.Bounds)); return !Screen.AllScreens.Any(screen => screen.WorkingArea.IntersectsWith(form.Bounds));
} }
@@ -41,16 +46,24 @@ namespace TweetDuck.Core.Controls{
} }
} }
public static void SetValueSafe(this NumericUpDown numUpDown, int value){
if (value >= numUpDown.Minimum && value <= numUpDown.Maximum){
numUpDown.Value = value;
}
}
public static void SetValueSafe(this TrackBar trackBar, int value){ public static void SetValueSafe(this TrackBar trackBar, int value){
if (value >= trackBar.Minimum && value <= trackBar.Maximum){ if (value >= trackBar.Minimum && value <= trackBar.Maximum){
trackBar.Value = value; trackBar.Value = value;
} }
} }
public static void SetElevated(this Button button){ public static bool AlignValueToTick(this TrackBar trackBar){
button.Text = " "+button.Text; if (trackBar.Value % trackBar.SmallChange != 0){
button.FlatStyle = FlatStyle.System; trackBar.Value = trackBar.SmallChange*(int)Math.Floor(((double)trackBar.Value/trackBar.SmallChange)+0.5);
NativeMethods.SendMessage(button.Handle, NativeMethods.BCM_SETSHIELD, 0, new IntPtr(1)); return false;
}
else return true;
} }
public static void EnableMultilineShortcuts(this TextBox textBox){ public static void EnableMultilineShortcuts(this TextBox textBox){

View File

@@ -2,7 +2,7 @@
using System.Windows.Forms; using System.Windows.Forms;
namespace TweetDuck.Core.Controls{ namespace TweetDuck.Core.Controls{
class FlatButton : Button{ sealed class FlatButton : Button{
protected override bool ShowFocusCues => false; protected override bool ShowFocusCues => false;
public FlatButton(){ public FlatButton(){

View File

@@ -23,7 +23,7 @@ namespace TweetDuck.Core.Controls{
Rectangle rect = e.ClipRectangle; Rectangle rect = e.ClipRectangle;
rect.Width = (int)(rect.Width*((double)Value/Maximum)); rect.Width = (int)(rect.Width*((double)Value/Maximum));
e.Graphics.FillRectangle(brush,rect); e.Graphics.FillRectangle(brush, rect);
} }
protected override void Dispose(bool disposing){ protected override void Dispose(bool disposing){

View File

@@ -0,0 +1,23 @@
using System;
using System.Drawing;
using System.Windows.Forms;
namespace TweetDuck.Core.Controls{
sealed class LabelVertical : Label{
public int LineHeight { get; set; }
protected override void OnPaint(PaintEventArgs e){
int y = (int)Math.Floor((ClientRectangle.Height-Text.Length*LineHeight)/2F)-2; // 2 = random
using(Brush brush = new SolidBrush(ForeColor)){
foreach(char chr in Text){
string str = chr.ToString();
float x = (ClientRectangle.Width-e.Graphics.MeasureString(str, Font).Width)/2F;
e.Graphics.DrawString(str, Font, brush, x, y);
y += LineHeight;
}
}
}
}
}

View File

@@ -0,0 +1,18 @@
using System.ComponentModel;
using System.Windows.Forms;
namespace TweetDuck.Core.Controls{
sealed class NumericUpDownEx : NumericUpDown{
public string TextSuffix { get; set ; }
protected override void UpdateEditText(){
base.UpdateEditText();
if (LicenseManager.UsageMode != LicenseUsageMode.Designtime){
ChangingText = true;
Text += TextSuffix;
ChangingText = false;
}
}
}
}

View File

@@ -1,25 +0,0 @@
using System;
using System.Drawing;
using System.Windows.Forms;
namespace TweetDuck.Core.Controls{
sealed class TabButton : FlatButton{
public Action Callback { get; private set; }
public void SetupButton(int locationX, int sizeWidth, string title, Action callback){
Callback = callback;
SuspendLayout();
FlatAppearance.BorderColor = Color.DimGray;
FlatAppearance.MouseDownBackColor = Color.White;
FlatAppearance.MouseOverBackColor = Color.White;
FlatStyle = FlatStyle.Flat;
Location = new Point(locationX, 0);
Margin = new Padding(0);
Size = new Size(sizeWidth, 30);
Text = title;
UseVisualStyleBackColor = true;
ResumeLayout(true);
}
}
}

View File

@@ -1,68 +0,0 @@
namespace TweetDuck.Core.Controls {
partial class TabPanel {
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.panelButtons = new System.Windows.Forms.Panel();
this.panelContent = new System.Windows.Forms.Panel();
this.SuspendLayout();
//
// panelButtons
//
this.panelButtons.Dock = System.Windows.Forms.DockStyle.Top;
this.panelButtons.Location = new System.Drawing.Point(0, 0);
this.panelButtons.Margin = new System.Windows.Forms.Padding(0);
this.panelButtons.Name = "panelButtons";
this.panelButtons.Size = new System.Drawing.Size(640, 30);
this.panelButtons.TabIndex = 0;
//
// panelContent
//
this.panelContent.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.panelContent.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.panelContent.Location = new System.Drawing.Point(0, 29);
this.panelContent.Margin = new System.Windows.Forms.Padding(0);
this.panelContent.Name = "panelContent";
this.panelContent.Size = new System.Drawing.Size(640, 451);
this.panelContent.TabIndex = 1;
//
// TabPanel
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.panelContent);
this.Controls.Add(this.panelButtons);
this.Name = "TabPanel";
this.Size = new System.Drawing.Size(640, 480);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Panel panelButtons;
private System.Windows.Forms.Panel panelContent;
}
}

View File

@@ -1,52 +0,0 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
namespace TweetDuck.Core.Controls{
sealed partial class TabPanel : UserControl{
public Panel Content => panelContent;
public IEnumerable<TabButton> Buttons => panelButtons.Controls.Cast<TabButton>();
public TabButton ActiveButton { get; private set; }
private int btnWidth;
public TabPanel(){
InitializeComponent();
}
public void SetupTabPanel(int buttonWidth){
this.btnWidth = buttonWidth;
}
public TabButton AddButton(string title, Action callback){
TabButton button = new TabButton();
button.SetupButton((btnWidth-1)*panelButtons.Controls.Count, btnWidth, title, callback);
button.Click += (sender, args) => SelectTab((TabButton)sender);
panelButtons.Controls.Add(button);
return button;
}
public void SelectTab(TabButton button){
if (ActiveButton != null){
ActiveButton.BackColor = SystemColors.Control;
}
button.BackColor = Color.White;
button.Callback();
ActiveButton = button;
}
public void ReplaceContent(Control newControl){
newControl.Dock = DockStyle.Fill;
Content.SuspendLayout();
Content.Controls.Clear();
Content.Controls.Add(newControl);
Content.ResumeLayout(true);
}
}
}

View File

@@ -26,22 +26,29 @@
this.components = new System.ComponentModel.Container(); this.components = new System.ComponentModel.Container();
this.trayIcon = new TweetDuck.Core.TrayIcon(this.components); this.trayIcon = new TweetDuck.Core.TrayIcon(this.components);
this.toolTip = new System.Windows.Forms.ToolTip(this.components); this.toolTip = new System.Windows.Forms.ToolTip(this.components);
this.timerResize = new System.Windows.Forms.Timer(this.components);
this.SuspendLayout(); this.SuspendLayout();
// //
// timerResize
//
this.timerResize.Interval = 500;
this.timerResize.Tick += new System.EventHandler(this.timerResize_Tick);
//
// FormBrowser // FormBrowser
// //
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = TweetDuck.Core.Utils.BrowserUtils.BackgroundColor; this.BackColor = TweetDuck.Core.Utils.TwitterUtils.BackgroundColor;
this.ClientSize = new System.Drawing.Size(324, 386); this.ClientSize = new System.Drawing.Size(400, 386);
this.Icon = Properties.Resources.icon; this.Icon = Properties.Resources.icon;
this.Location = TweetDuck.Core.Controls.ControlExtensions.InvisibleLocation; this.Location = TweetDuck.Core.Controls.ControlExtensions.InvisibleLocation;
this.MinimumSize = new System.Drawing.Size(340, 424); this.MinimumSize = new System.Drawing.Size(416, 424);
this.Name = "FormBrowser"; this.Name = "FormBrowser";
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual; this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
this.Activated += new System.EventHandler(this.FormBrowser_Activated); this.Activated += new System.EventHandler(this.FormBrowser_Activated);
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FormBrowser_FormClosing); this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FormBrowser_FormClosing);
this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.FormBrowser_FormClosed); this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.FormBrowser_FormClosed);
this.LocationChanged += new System.EventHandler(this.FormBrowser_LocationChanged);
this.ResizeEnd += new System.EventHandler(this.FormBrowser_ResizeEnd); this.ResizeEnd += new System.EventHandler(this.FormBrowser_ResizeEnd);
this.Resize += new System.EventHandler(this.FormBrowser_Resize); this.Resize += new System.EventHandler(this.FormBrowser_Resize);
this.ResumeLayout(false); this.ResumeLayout(false);
@@ -52,6 +59,7 @@
private TrayIcon trayIcon; private TrayIcon trayIcon;
private System.Windows.Forms.ToolTip toolTip; private System.Windows.Forms.ToolTip toolTip;
private System.Windows.Forms.Timer timerResize;
} }
} }

View File

@@ -1,30 +1,47 @@
using CefSharp; using CefSharp;
using CefSharp.WinForms; using CefSharp.WinForms;
using System; using System;
using System.Diagnostics;
using System.Drawing; using System.Drawing;
using System.Linq;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Configuration; using TweetDuck.Configuration;
using TweetDuck.Core.Bridge; using TweetDuck.Core.Bridge;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Handling; using TweetDuck.Core.Handling;
using TweetDuck.Core.Handling.General;
using TweetDuck.Core.Notification; using TweetDuck.Core.Notification;
using TweetDuck.Core.Notification.Screenshot; using TweetDuck.Core.Notification.Screenshot;
using TweetDuck.Core.Notification.Sound;
using TweetDuck.Core.Other; using TweetDuck.Core.Other;
using TweetDuck.Core.Other.Management;
using TweetDuck.Core.Other.Settings;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetDuck.Plugins; using TweetDuck.Plugins;
using TweetDuck.Plugins.Enums; using TweetDuck.Plugins.Enums;
using TweetDuck.Plugins.Events; using TweetDuck.Plugins.Events;
using TweetDuck.Resources; using TweetDuck.Resources;
using TweetDuck.Updates; using TweetDuck.Updates;
using TweetDuck.Updates.Events; using TweetLib.Audio;
namespace TweetDuck.Core{ namespace TweetDuck.Core{
sealed partial class FormBrowser : Form{ sealed partial class FormBrowser : Form{
private static UserConfig Config => Program.UserConfig; private static UserConfig Config => Program.UserConfig;
public bool IsWaiting{
set{
if (value){
browser.Enabled = false;
Cursor = Cursors.WaitCursor;
}
else{
browser.Enabled = true;
Cursor = Cursors.Default;
if (Focused){ // re-focus browser only if the window or a child is activated
browser.Focus();
}
}
}
}
public string UpdateInstallerPath { get; private set; } public string UpdateInstallerPath { get; private set; }
private readonly ChromiumWebBrowser browser; private readonly ChromiumWebBrowser browser;
@@ -32,24 +49,29 @@ namespace TweetDuck.Core{
private readonly UpdateHandler updates; private readonly UpdateHandler updates;
private readonly FormNotificationTweet notification; private readonly FormNotificationTweet notification;
private readonly ContextMenu contextMenu; private readonly ContextMenu contextMenu;
private readonly MemoryUsageTracker memoryUsageTracker;
private bool isLoaded; private bool isLoaded;
private bool isBrowserReady; private bool isBrowserReady;
private FormWindowState prevState; private FormWindowState prevState;
private TweetScreenshotManager notificationScreenshotManager; private TweetScreenshotManager notificationScreenshotManager;
private ISoundNotificationPlayer soundNotification; private SoundNotification soundNotification;
private VideoPlayer videoPlayer;
public FormBrowser(PluginManager pluginManager, UpdaterSettings updaterSettings){ public FormBrowser(UpdaterSettings updaterSettings){
InitializeComponent(); InitializeComponent();
Text = Program.BrandName; Text = Program.BrandName;
this.plugins = pluginManager; this.plugins = new PluginManager(Program.PluginPath, Program.PluginConfigFilePath);
this.plugins.Reloaded += plugins_Reloaded; this.plugins.Reloaded += plugins_Reloaded;
this.plugins.Executed += plugins_Executed;
this.plugins.PluginChangedState += plugins_PluginChangedState; this.plugins.PluginChangedState += plugins_PluginChangedState;
this.plugins.Reload();
this.contextMenu = ContextMenuBrowser.CreateMenu(this); this.contextMenu = ContextMenuBrowser.CreateMenu(this);
this.memoryUsageTracker = new MemoryUsageTracker("TDGF_tryRunCleanup");
this.notification = new FormNotificationTweet(this, plugins){ this.notification = new FormNotificationTweet(this, plugins){
#if DEBUG #if DEBUG
@@ -62,8 +84,11 @@ namespace TweetDuck.Core{
this.notification.Show(); this.notification.Show();
this.browser = new ChromiumWebBrowser("https://tweetdeck.twitter.com/"){ this.browser = new ChromiumWebBrowser("https://tweetdeck.twitter.com/"){
DialogHandler = new FileDialogHandler(),
DragHandler = new DragHandlerBrowser(),
MenuHandler = new ContextMenuBrowser(this), MenuHandler = new ContextMenuBrowser(this),
JsDialogHandler = new JavaScriptDialogHandler(), JsDialogHandler = new JavaScriptDialogHandler(),
KeyboardHandler = new KeyboardHandlerBrowser(this),
LifeSpanHandler = new LifeSpanHandler(), LifeSpanHandler = new LifeSpanHandler(),
RequestHandler = new RequestHandlerBrowser() RequestHandler = new RequestHandlerBrowser()
}; };
@@ -79,7 +104,7 @@ namespace TweetDuck.Core{
this.browser.RegisterAsyncJsObject("$TD", new TweetDeckBridge(this, notification)); this.browser.RegisterAsyncJsObject("$TD", new TweetDeckBridge(this, notification));
this.browser.RegisterAsyncJsObject("$TDP", plugins.Bridge); this.browser.RegisterAsyncJsObject("$TDP", plugins.Bridge);
browser.BrowserSettings.BackgroundColor = (uint)BrowserUtils.BackgroundColor.ToArgb(); browser.BrowserSettings.BackgroundColor = (uint)TwitterUtils.BackgroundColor.ToArgb();
browser.Dock = DockStyle.None; browser.Dock = DockStyle.None;
browser.Location = ControlExtensions.InvisibleLocation; browser.Location = ControlExtensions.InvisibleLocation;
Controls.Add(browser); Controls.Add(browser);
@@ -87,11 +112,14 @@ namespace TweetDuck.Core{
Controls.Add(new MenuStrip{ Visible = false }); // fixes Alt freezing the program in Win 10 Anniversary Update Controls.Add(new MenuStrip{ Visible = false }); // fixes Alt freezing the program in Win 10 Anniversary Update
Disposed += (sender, args) => { Disposed += (sender, args) => {
memoryUsageTracker.Dispose();
browser.Dispose(); browser.Dispose();
contextMenu.Dispose(); contextMenu.Dispose();
notificationScreenshotManager?.Dispose(); notificationScreenshotManager?.Dispose();
soundNotification?.Dispose(); soundNotification?.Dispose();
videoPlayer?.Dispose();
}; };
this.trayIcon.ClickRestore += trayIcon_ClickRestore; this.trayIcon.ClickRestore += trayIcon_ClickRestore;
@@ -103,23 +131,13 @@ namespace TweetDuck.Core{
Config.MuteToggled += Config_MuteToggled; Config.MuteToggled += Config_MuteToggled;
Config.ZoomLevelChanged += Config_ZoomLevelChanged; Config.ZoomLevelChanged += Config_ZoomLevelChanged;
this.updates = new UpdateHandler(browser, this, updaterSettings); this.updates = new UpdateHandler(browser, updaterSettings);
this.updates.UpdateAccepted += updates_UpdateAccepted; this.updates.UpdateAccepted += updates_UpdateAccepted;
this.updates.UpdateDismissed += updates_UpdateDismissed; this.updates.UpdateDismissed += updates_UpdateDismissed;
RestoreWindow(); RestoreWindow();
} }
private bool TryBringToFront<T>() where T : Form{
T form = Application.OpenForms.OfType<T>().FirstOrDefault();
if (form != null){
form.BringToFront();
return true;
}
else return false;
}
private void ShowChildForm(Form form){ private void ShowChildForm(Form form){
form.VisibleChanged += (sender, args) => form.MoveToCenter(this); form.VisibleChanged += (sender, args) => form.MoveToCenter(this);
form.Show(this); form.Show(this);
@@ -154,7 +172,7 @@ namespace TweetDuck.Core{
private void browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){ private void browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){
if (!e.IsLoading){ if (!e.IsLoading){
foreach(string word in BrowserUtils.DictionaryWords){ foreach(string word in TwitterUtils.DictionaryWords){
browser.AddWordToDictionary(word); browser.AddWordToDictionary(word);
} }
@@ -165,31 +183,33 @@ namespace TweetDuck.Core{
private void browser_FrameLoadStart(object sender, FrameLoadStartEventArgs e){ private void browser_FrameLoadStart(object sender, FrameLoadStartEventArgs e){
if (e.Frame.IsMain){ if (e.Frame.IsMain){
memoryUsageTracker.Stop();
if (Config.ZoomLevel != 100){ if (Config.ZoomLevel != 100){
BrowserUtils.SetZoomLevel(browser.GetBrowser(), Config.ZoomLevel); BrowserUtils.SetZoomLevel(browser.GetBrowser(), Config.ZoomLevel);
} }
if (BrowserUtils.IsTwitterWebsite(e.Frame)){ if (TwitterUtils.IsTwitterWebsite(e.Frame)){
ScriptLoader.ExecuteFile(e.Frame, "twitter.js"); ScriptLoader.ExecuteFile(e.Frame, "twitter.js");
} }
} }
} }
private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){ private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
if (e.Frame.IsMain && BrowserUtils.IsTweetDeckWebsite(e.Frame)){ if (e.Frame.IsMain && TwitterUtils.IsTweetDeckWebsite(e.Frame)){
e.Frame.ExecuteJavaScriptAsync(BrowserUtils.BackgroundColorFix); e.Frame.ExecuteJavaScriptAsync(TwitterUtils.BackgroundColorFix);
UpdateProperties(PropertyBridge.Properties.AllBrowser); UpdateProperties(PropertyBridge.Environment.Browser);
TweetDeckBridge.RestoreSessionData(e.Frame);
ScriptLoader.ExecuteFile(e.Frame, "code.js"); ScriptLoader.ExecuteFile(e.Frame, "code.js");
ReinjectCustomCSS(Config.CustomBrowserCSS); ReinjectCustomCSS(Config.CustomBrowserCSS);
plugins.ExecutePlugins(e.Frame, PluginEnvironment.Browser);
if (plugins.HasAnyPlugin(PluginEnvironment.Browser)){
ScriptLoader.ExecuteFile(e.Frame, PluginManager.PluginBrowserScriptFile);
ScriptLoader.ExecuteFile(e.Frame, PluginManager.PluginGlobalScriptFile);
plugins.ExecutePlugins(e.Frame, PluginEnvironment.Browser, true);
}
TweetDeckBridge.ResetStaticProperties(); TweetDeckBridge.ResetStaticProperties();
if (Program.SystemConfig.EnableBrowserGCReload){
memoryUsageTracker.Start(this, e.Browser, Program.SystemConfig.BrowserMemoryThreshold);
}
} }
} }
@@ -198,7 +218,7 @@ namespace TweetDuck.Core{
return; return;
} }
if (!e.FailedUrl.StartsWith("http://td/")){ if (!e.FailedUrl.StartsWith("http://td/", StringComparison.Ordinal)){
string errorPage = ScriptLoader.LoadResource("pages/error.html", true); string errorPage = ScriptLoader.LoadResource("pages/error.html", true);
if (errorPage != null){ if (errorPage != null){
@@ -207,10 +227,25 @@ namespace TweetDuck.Core{
} }
} }
private void timerResize_Tick(object sender, EventArgs e){
FormBrowser_ResizeEnd(this, e); // also stops timer
}
private void FormBrowser_Activated(object sender, EventArgs e){ private void FormBrowser_Activated(object sender, EventArgs e){
if (!isLoaded)return; if (!isLoaded)return;
trayIcon.HasNotifications = false; trayIcon.HasNotifications = false;
if (!browser.Enabled){ // when taking a screenshot, the window is unfocused and
browser.Enabled = true; // the browser is disabled; if the user clicks back into
} // the window, enable the browser again
}
private void FormBrowser_LocationChanged(object sender, EventArgs e){
if (!isLoaded)return;
timerResize.Stop();
timerResize.Start();
} }
private void FormBrowser_Resize(object sender, EventArgs e){ private void FormBrowser_Resize(object sender, EventArgs e){
@@ -228,11 +263,17 @@ namespace TweetDuck.Core{
FormBrowser_ResizeEnd(sender, e); FormBrowser_ResizeEnd(sender, e);
} }
} }
else{
timerResize.Stop();
timerResize.Start();
}
} }
private void FormBrowser_ResizeEnd(object sender, EventArgs e){ // also triggers when the window moves private void FormBrowser_ResizeEnd(object sender, EventArgs e){ // also triggers when the window moves
if (!isLoaded)return; if (!isLoaded)return;
timerResize.Stop();
if (Location != ControlExtensions.InvisibleLocation){ if (Location != ControlExtensions.InvisibleLocation){
Config.BrowserWindow.Save(this); Config.BrowserWindow.Save(this);
Config.Save(); Config.Save();
@@ -255,7 +296,7 @@ namespace TweetDuck.Core{
} }
private void Config_MuteToggled(object sender, EventArgs e){ private void Config_MuteToggled(object sender, EventArgs e){
UpdateProperties(PropertyBridge.Properties.MuteNotifications); UpdateProperties(PropertyBridge.Environment.Browser);
} }
private void Config_ZoomLevelChanged(object sender, EventArgs e){ private void Config_ZoomLevelChanged(object sender, EventArgs e){
@@ -278,19 +319,28 @@ namespace TweetDuck.Core{
} }
private void plugins_Reloaded(object sender, PluginErrorEventArgs e){ private void plugins_Reloaded(object sender, PluginErrorEventArgs e){
browser.GetBrowser().Reload(); if (e.HasErrors){
FormMessage.Error("Error Loading Plugins", "The following plugins will not be available until the issues are resolved:\n\n"+string.Join("\n\n", e.Errors), FormMessage.OK);
}
if (isLoaded){
ReloadToTweetDeck();
}
}
private static void plugins_Executed(object sender, PluginErrorEventArgs e){
if (e.HasErrors){
FormMessage.Error("Error Executing Plugins", "Failed to execute the following plugins:\n\n"+string.Join("\n\n", e.Errors), FormMessage.OK);
}
} }
private void plugins_PluginChangedState(object sender, PluginChangedStateEventArgs e){ private void plugins_PluginChangedState(object sender, PluginChangedStateEventArgs e){
browser.ExecuteScriptAsync("window.TDPF_setPluginState", e.Plugin, e.IsEnabled); browser.ExecuteScriptAsync("window.TDPF_setPluginState", e.Plugin, e.IsEnabled);
} }
private void updates_UpdateAccepted(object sender, UpdateAcceptedEventArgs e){ private void updates_UpdateAccepted(object sender, UpdateEventArgs e){
foreach(Form form in Application.OpenForms.Cast<Form>().Reverse()){ this.InvokeAsyncSafe(() => {
if (form is FormSettings || form is FormPlugins || form is FormAbout){ FormManager.CloseAllDialogs();
form.Close();
}
}
updates.BeginUpdateDownload(this, e.UpdateInfo, update => { updates.BeginUpdateDownload(this, e.UpdateInfo, update => {
if (update.DownloadStatus == UpdateDownloadStatus.Done){ if (update.DownloadStatus == UpdateDownloadStatus.Done){
@@ -299,42 +349,60 @@ namespace TweetDuck.Core{
ForceClose(); ForceClose();
}); });
});
} }
private void updates_UpdateDismissed(object sender, UpdateDismissedEventArgs e){ private void updates_UpdateDismissed(object sender, UpdateEventArgs e){
Config.DismissedUpdate = e.VersionTag; this.InvokeAsyncSafe(() => {
Config.DismissedUpdate = e.UpdateInfo.VersionTag;
Config.Save(); Config.Save();
});
} }
private void soundNotification_PlaybackError(object sender, PlaybackErrorEventArgs e){ private void soundNotification_PlaybackError(object sender, PlaybackErrorEventArgs e){
e.Ignore = true; e.Ignore = true;
using(FormMessage form = new FormMessage("Notification Sound Error", "Could not play custom notification sound."+Environment.NewLine+e.Message, MessageBoxIcon.Error)){ using(FormMessage form = new FormMessage("Notification Sound Error", "Could not play custom notification sound.\n"+e.Message, MessageBoxIcon.Error)){
form.CancelButton = form.AddButton("Ignore"); form.AddButton(FormMessage.Ignore, ControlType.Cancel | ControlType.Focused);
Button btnOpenSettings = form.AddButton("Open Settings"); Button btnOpenSettings = form.AddButton("View Options");
btnOpenSettings.Width += 16; btnOpenSettings.Width += 16;
btnOpenSettings.Location = new Point(btnOpenSettings.Location.X-16, btnOpenSettings.Location.Y); btnOpenSettings.Location = new Point(btnOpenSettings.Location.X-16, btnOpenSettings.Location.Y);
if (form.ShowDialog() == DialogResult.OK && form.ClickedButton == btnOpenSettings){ if (form.ShowDialog() == DialogResult.OK && form.ClickedButton == btnOpenSettings){
OpenSettings(FormSettings.TabIndexNotification); OpenSettings(typeof(TabSettingsSounds));
} }
} }
} }
protected override void WndProc(ref Message m){ protected override void WndProc(ref Message m){
if (isLoaded && m.Msg == Program.WindowRestoreMessage){ if (isLoaded){
using(Process process = Process.GetCurrentProcess()){ if (m.Msg == Program.WindowRestoreMessage){
if (process.Id == m.WParam.ToInt32()){ if (WindowsUtils.CurrentProcessID == m.WParam.ToInt32()){
trayIcon_ClickRestore(trayIcon, new EventArgs()); trayIcon_ClickRestore(trayIcon, EventArgs.Empty);
}
} }
return; return;
} }
else if (m.Msg == Program.SubProcessMessage){
int processId = m.WParam.ToInt32();
if (WindowsUtils.IsChildProcess(processId)){ // child process is checked in two places for safety
BrowserProcesses.Link(m.LParam.ToInt32(), processId);
}
return;
}
}
if (isBrowserReady && m.Msg == NativeMethods.WM_PARENTNOTIFY && (m.WParam.ToInt32() & 0xFFFF) == NativeMethods.WM_XBUTTONDOWN){ if (isBrowserReady && m.Msg == NativeMethods.WM_PARENTNOTIFY && (m.WParam.ToInt32() & 0xFFFF) == NativeMethods.WM_XBUTTONDOWN){
if (videoPlayer != null && videoPlayer.Running){
videoPlayer.Close();
}
else{
browser.ExecuteScriptAsync("TDGF_onMouseClickExtra", (m.WParam.ToInt32() >> 16) & 0xFFFF); browser.ExecuteScriptAsync("TDGF_onMouseClickExtra", (m.WParam.ToInt32() >> 16) & 0xFFFF);
}
return; return;
} }
@@ -361,12 +429,12 @@ namespace TweetDuck.Core{
browser.ExecuteScriptAsync("TDGF_reinjectCustomCSS", css?.Replace(Environment.NewLine, " ") ?? string.Empty); browser.ExecuteScriptAsync("TDGF_reinjectCustomCSS", css?.Replace(Environment.NewLine, " ") ?? string.Empty);
} }
public void UpdateProperties(PropertyBridge.Properties properties){ public void UpdateProperties(PropertyBridge.Environment environment){
browser.ExecuteScriptAsync(PropertyBridge.GenerateScript(properties)); browser.ExecuteScriptAsync(PropertyBridge.GenerateScript(environment));
} }
public void ReloadToTweetDeck(){ public void ReloadToTweetDeck(){
browser.ExecuteScriptAsync("window.location.href = 'https://tweetdeck.twitter.com'"); browser.ExecuteScriptAsync($"if(window.TDGF_reload)window.TDGF_reload();else window.location.href='{TwitterUtils.TweetDeckURL}'");
} }
// callback handlers // callback handlers
@@ -376,26 +444,43 @@ namespace TweetDuck.Core{
} }
public void OpenSettings(){ public void OpenSettings(){
OpenSettings(0); OpenSettings(null);
} }
public void OpenSettings(int tabIndex){ public void OpenSettings(Type startTab){
if (!TryBringToFront<FormSettings>()){ if (!FormManager.TryBringToFront<FormSettings>()){
bool prevEnableUpdateCheck = Config.EnableUpdateCheck; bool prevEnableUpdateCheck = Config.EnableUpdateCheck;
FormSettings form = new FormSettings(this, plugins, updates, tabIndex); FormSettings form = new FormSettings(this, plugins, updates, startTab);
form.FormClosed += (sender, args) => { form.FormClosed += (sender, args) => {
if (!prevEnableUpdateCheck && Config.EnableUpdateCheck){ if (!prevEnableUpdateCheck && Config.EnableUpdateCheck){
updates.DismissUpdate(string.Empty); Config.DismissedUpdate = null;
updates.Check(false); Config.Save();
updates.Check(true);
} }
if (!Config.EnableTrayHighlight){ if (!Config.EnableTrayHighlight){
trayIcon.HasNotifications = false; trayIcon.HasNotifications = false;
} }
UpdateProperties(PropertyBridge.Properties.ExpandLinksOnHover | PropertyBridge.Properties.SwitchAccountSelectors | PropertyBridge.Properties.HasCustomNotificationSound); if (Program.SystemConfig.EnableBrowserGCReload){
memoryUsageTracker.Start(this, browser.GetBrowser(), Program.SystemConfig.BrowserMemoryThreshold);
}
else{
memoryUsageTracker.Stop();
}
if (form.ShouldReloadBrowser){
FormManager.TryFind<FormPlugins>()?.Close();
plugins.Reload(); // also reloads the browser
}
else{
UpdateProperties(PropertyBridge.Environment.Browser);
}
notification.RequiresResize = true;
form.Dispose(); form.Dispose();
}; };
@@ -404,13 +489,13 @@ namespace TweetDuck.Core{
} }
public void OpenAbout(){ public void OpenAbout(){
if (!TryBringToFront<FormAbout>()){ if (!FormManager.TryBringToFront<FormAbout>()){
ShowChildForm(new FormAbout()); ShowChildForm(new FormAbout());
} }
} }
public void OpenPlugins(){ public void OpenPlugins(){
if (!TryBringToFront<FormPlugins>()){ if (!FormManager.TryBringToFront<FormPlugins>()){
ShowChildForm(new FormPlugins(plugins)); ShowChildForm(new FormPlugins(plugins));
} }
} }
@@ -427,16 +512,62 @@ namespace TweetDuck.Core{
} }
if (soundNotification == null){ if (soundNotification == null){
soundNotification = SoundNotification.New(); soundNotification = new SoundNotification();
soundNotification.PlaybackError += soundNotification_PlaybackError; soundNotification.PlaybackError += soundNotification_PlaybackError;
} }
soundNotification.SetVolume(Config.NotificationSoundVolume);
soundNotification.Play(Config.NotificationSoundPath); soundNotification.Play(Config.NotificationSoundPath);
} }
public void PlayVideo(string url){
if (string.IsNullOrEmpty(url)){
videoPlayer?.Close();
return;
}
if (videoPlayer == null){
videoPlayer = new VideoPlayer(this);
videoPlayer.ProcessExited += (sender, args) => {
browser.GetBrowser().GetHost().SendFocusEvent(true);
HideVideoOverlay();
};
}
videoPlayer.Launch(url);
}
public void HideVideoOverlay(){
browser.ExecuteScriptAsync("$('#td-video-player-overlay').remove()");
}
public bool ProcessBrowserKey(Keys key){
if (videoPlayer != null && videoPlayer.Running){
videoPlayer.SendKeyEvent(key);
return true;
}
return false;
}
public void ShowTweetDetail(string columnId, string chirpId, string fallbackUrl){
Activate();
using(IFrame frame = browser.GetBrowser().MainFrame){
if (!TwitterUtils.IsTweetDeckWebsite(frame)){
FormMessage.Error("View Tweet Detail", "TweetDeck is not currently loaded.", FormMessage.OK);
return;
}
}
notification.FinishCurrentNotification();
browser.ExecuteScriptAsync("window.TDGF_showTweetDetail", columnId, chirpId, fallbackUrl);
}
public void OnTweetScreenshotReady(string html, int width, int height){ public void OnTweetScreenshotReady(string html, int width, int height){
if (notificationScreenshotManager == null){ if (notificationScreenshotManager == null){
notificationScreenshotManager = new TweetScreenshotManager(this); notificationScreenshotManager = new TweetScreenshotManager(this, plugins);
} }
notificationScreenshotManager.Trigger(html, width, height); notificationScreenshotManager.Trigger(html, width, height);

View File

@@ -117,11 +117,13 @@
<resheader name="writer"> <resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> <metadata name="trayIcon.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<data name="trayIcon.TrayLocation" type="System.Drawing.Point, System.Drawing">
<value>17, 17</value> <value>17, 17</value>
</data> </metadata>
<data name="toolTip.TrayLocation" type="System.Drawing.Point, System.Drawing"> <metadata name="toolTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>112, 17</value> <value>112, 17</value>
</data> </metadata>
<metadata name="timerResize.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>202, 17</value>
</metadata>
</root> </root>

29
Core/FormManager.cs Normal file
View File

@@ -0,0 +1,29 @@
using System.Linq;
using System.Windows.Forms;
using TweetDuck.Core.Other;
namespace TweetDuck.Core{
static class FormManager{
public static T TryFind<T>() where T : Form{
return Application.OpenForms.OfType<T>().FirstOrDefault();
}
public static bool TryBringToFront<T>() where T : Form{
T form = TryFind<T>();
if (form != null){
form.BringToFront();
return true;
}
else return false;
}
public static void CloseAllDialogs(){
foreach(Form form in Application.OpenForms.Cast<Form>().Reverse()){
if (form is FormSettings || form is FormPlugins || form is FormAbout){
form.Close();
}
}
}
}
}

View File

@@ -1,98 +1,131 @@
using CefSharp; using System;
using System;
using System.IO; using System.IO;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Windows.Forms; using System.Windows.Forms;
using CefSharp;
using TweetDuck.Core.Bridge; using TweetDuck.Core.Bridge;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using System.Collections.Generic;
using System.Linq;
namespace TweetDuck.Core.Handling{ namespace TweetDuck.Core.Handling{
abstract class ContextMenuBase : IContextMenuHandler{ abstract class ContextMenuBase : IContextMenuHandler{
private static readonly Lazy<Regex> RegexTwitterAccount = new Lazy<Regex>(() => new Regex(@"^https?://twitter\.com/([^/]+)/?$", RegexOptions.Compiled), false);
protected static readonly bool HasDevTools = File.Exists(Path.Combine(Program.ProgramPath, "devtools_resources.pak")); protected static readonly bool HasDevTools = File.Exists(Path.Combine(Program.ProgramPath, "devtools_resources.pak"));
private const int MenuOpenLinkUrl = 26500; private static TwitterUtils.ImageQuality ImageQuality => Program.UserConfig.TwitterImageQuality;
private const int MenuCopyLinkUrl = 26501;
private const int MenuCopyUsername = 26502;
private const int MenuOpenImage = 26503;
private const int MenuSaveImage = 26504;
private const int MenuCopyImageUrl = 26505;
private const int MenuOpenDevTools = 26599;
private readonly Form form; private static KeyValuePair<string, string> ContextInfo;
private static bool IsLink => ContextInfo.Key == "link";
private static bool IsImage => ContextInfo.Key == "image";
private static bool IsVideo => ContextInfo.Key == "video";
protected ContextMenuBase(Form form){ public static void SetContextInfo(string type, string link){
this.form = form; ContextInfo = new KeyValuePair<string, string>(string.IsNullOrEmpty(link) ? null : type, link);
} }
private static string GetMediaLink(IContextMenuParams parameters){
return IsImage || IsVideo ? ContextInfo.Value : parameters.SourceUrl;
}
private const CefMenuCommand MenuOpenLinkUrl = (CefMenuCommand)26500;
private const CefMenuCommand MenuCopyLinkUrl = (CefMenuCommand)26501;
private const CefMenuCommand MenuCopyUsername = (CefMenuCommand)26502;
private const CefMenuCommand MenuOpenMediaUrl = (CefMenuCommand)26503;
private const CefMenuCommand MenuCopyMediaUrl = (CefMenuCommand)26504;
private const CefMenuCommand MenuSaveMedia = (CefMenuCommand)26505;
private const CefMenuCommand MenuSaveTweetImages = (CefMenuCommand)26506;
private const CefMenuCommand MenuOpenDevTools = (CefMenuCommand)26599;
private string[] lastHighlightedTweetAuthors;
private string[] lastHighlightedTweetImageList;
public virtual void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){ public virtual void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){
if (parameters.TypeFlags.HasFlag(ContextMenuType.Link) && !parameters.UnfilteredLinkUrl.EndsWith("tweetdeck.twitter.com/#", StringComparison.Ordinal)){ if (!TwitterUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){
if (RegexTwitterAccount.Value.IsMatch(parameters.UnfilteredLinkUrl)){ lastHighlightedTweetAuthors = StringUtils.EmptyArray;
model.AddItem((CefMenuCommand)MenuOpenLinkUrl, "Open account in browser"); lastHighlightedTweetImageList = StringUtils.EmptyArray;
model.AddItem((CefMenuCommand)MenuCopyLinkUrl, "Copy account address"); ContextInfo = default(KeyValuePair<string, string>);
model.AddItem((CefMenuCommand)MenuCopyUsername, "Copy account username");
} }
else{ else{
model.AddItem((CefMenuCommand)MenuOpenLinkUrl, "Open link in browser"); lastHighlightedTweetAuthors = TweetDeckBridge.LastHighlightedTweetAuthorsArray;
model.AddItem((CefMenuCommand)MenuCopyLinkUrl, "Copy link address"); lastHighlightedTweetImageList = TweetDeckBridge.LastHighlightedTweetImagesArray;
}
bool hasTweetImage = IsImage;
bool hasTweetVideo = IsVideo;
string TextOpen(string name) => "Open "+name+" in browser";
string TextCopy(string name) => "Copy "+name+" address";
string TextSave(string name) => "Save "+name+" as...";
if (parameters.TypeFlags.HasFlag(ContextMenuType.Link) && !parameters.UnfilteredLinkUrl.EndsWith("tweetdeck.twitter.com/#", StringComparison.Ordinal) && !hasTweetImage && !hasTweetVideo){
if (TwitterUtils.RegexAccount.IsMatch(parameters.UnfilteredLinkUrl)){
model.AddItem(MenuOpenLinkUrl, TextOpen("account"));
model.AddItem(MenuCopyLinkUrl, TextCopy("account"));
model.AddItem(MenuCopyUsername, "Copy account username");
}
else{
model.AddItem(MenuOpenLinkUrl, TextOpen("link"));
model.AddItem(MenuCopyLinkUrl, TextCopy("link"));
} }
model.AddSeparator(); model.AddSeparator();
} }
if (parameters.TypeFlags.HasFlag(ContextMenuType.Media) && parameters.HasImageContents){ if (hasTweetVideo){
model.AddItem((CefMenuCommand)MenuOpenImage, "Open image in browser"); model.AddItem(MenuOpenMediaUrl, TextOpen("video"));
model.AddItem((CefMenuCommand)MenuSaveImage, "Save image as..."); model.AddItem(MenuCopyMediaUrl, TextCopy("video"));
model.AddItem((CefMenuCommand)MenuCopyImageUrl, "Copy image address"); model.AddItem(MenuSaveMedia, TextSave("video"));
model.AddSeparator();
}
else if ((parameters.TypeFlags.HasFlag(ContextMenuType.Media) && parameters.HasImageContents) || hasTweetImage){
model.AddItem(MenuOpenMediaUrl, TextOpen("image"));
model.AddItem(MenuCopyMediaUrl, TextCopy("image"));
model.AddItem(MenuSaveMedia, TextSave("image"));
if (lastHighlightedTweetImageList.Length > 1){
model.AddItem(MenuSaveTweetImages, TextSave("all images"));
}
model.AddSeparator(); model.AddSeparator();
} }
} }
public virtual bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){ public virtual bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){
switch((int)commandId){ switch(commandId){
case MenuOpenLinkUrl: case MenuOpenLinkUrl:
BrowserUtils.OpenExternalBrowser(parameters.LinkUrl); OpenBrowser(browserControl.AsControl(), IsLink ? ContextInfo.Value : parameters.LinkUrl);
break; break;
case MenuCopyLinkUrl: case MenuCopyLinkUrl:
SetClipboardText(string.IsNullOrEmpty(TweetDeckBridge.LastRightClickedLink) ? parameters.UnfilteredLinkUrl : TweetDeckBridge.LastRightClickedLink); SetClipboardText(browserControl.AsControl(), IsLink ? ContextInfo.Value : parameters.UnfilteredLinkUrl);
break;
case MenuOpenImage:
BrowserUtils.OpenExternalBrowser(parameters.SourceUrl);
break;
case MenuSaveImage:
string fileName = GetImageFileName(parameters.SourceUrl);
string extension = Path.GetExtension(fileName);
string saveTarget;
using(SaveFileDialog dialog = new SaveFileDialog{
AutoUpgradeEnabled = true,
OverwritePrompt = true,
Title = "Save image",
FileName = fileName,
Filter = "Image ("+(string.IsNullOrEmpty(extension) ? "unknown" : extension)+")|*.*"
}){
saveTarget = dialog.ShowDialog() == DialogResult.OK ? dialog.FileName : null;
}
if (saveTarget != null){
BrowserUtils.DownloadFileAsync(parameters.SourceUrl, saveTarget, null, ex => {
MessageBox.Show("An error occurred while downloading the image: "+ex.Message, Program.BrandName+" Has Failed :(", MessageBoxButtons.OK, MessageBoxIcon.Error);
});
}
break;
case MenuCopyImageUrl:
SetClipboardText(parameters.SourceUrl);
break; break;
case MenuCopyUsername: case MenuCopyUsername:
Match match = RegexTwitterAccount.Value.Match(parameters.UnfilteredLinkUrl); Match match = TwitterUtils.RegexAccount.Match(parameters.UnfilteredLinkUrl);
SetClipboardText(match.Success ? match.Groups[1].Value : parameters.UnfilteredLinkUrl); SetClipboardText(browserControl.AsControl(), match.Success ? match.Groups[1].Value : parameters.UnfilteredLinkUrl);
break;
case MenuOpenMediaUrl:
OpenBrowser(browserControl.AsControl(), TwitterUtils.GetMediaLink(GetMediaLink(parameters), ImageQuality));
break;
case MenuCopyMediaUrl:
SetClipboardText(browserControl.AsControl(), TwitterUtils.GetMediaLink(GetMediaLink(parameters), ImageQuality));
break;
case MenuSaveMedia:
if (IsVideo){
TwitterUtils.DownloadVideo(GetMediaLink(parameters));
}
else{
TwitterUtils.DownloadImage(GetMediaLink(parameters), lastHighlightedTweetAuthors.LastOrDefault(), ImageQuality);
}
break;
case MenuSaveTweetImages:
TwitterUtils.DownloadImages(lastHighlightedTweetImageList, lastHighlightedTweetAuthors.LastOrDefault(), ImageQuality);
break; break;
case MenuOpenDevTools: case MenuOpenDevTools:
@@ -103,18 +136,24 @@ namespace TweetDuck.Core.Handling{
return false; return false;
} }
public virtual void OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame){} public virtual void OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame){
ContextInfo = default(KeyValuePair<string, string>);
}
public virtual bool RunContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback){ public virtual bool RunContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback){
return false; return false;
} }
protected void SetClipboardText(string text){ protected void OpenBrowser(Control control, string url){
form.InvokeAsyncSafe(() => WindowsUtils.SetClipboard(text, TextDataFormat.UnicodeText)); control.InvokeAsyncSafe(() => BrowserUtils.OpenExternalBrowser(url));
} }
protected void AddDebugMenuItems(IMenuModel model){ protected void SetClipboardText(Control control, string text){
model.AddItem((CefMenuCommand)MenuOpenDevTools, "Open dev tools"); control.InvokeAsyncSafe(() => WindowsUtils.SetClipboard(text, TextDataFormat.UnicodeText));
}
protected static void AddDebugMenuItems(IMenuModel model){
model.AddItem(MenuOpenDevTools, "Open dev tools");
} }
protected static void RemoveSeparatorIfLast(IMenuModel model){ protected static void RemoveSeparatorIfLast(IMenuModel model){
@@ -128,21 +167,5 @@ namespace TweetDuck.Core.Handling{
model.AddSeparator(); model.AddSeparator();
} }
} }
private static string GetImageFileName(string url){
// twimg adds a colon after file extension
int dot = url.LastIndexOf('.');
if (dot != -1){
int colon = url.IndexOf(':', dot);
if (colon != -1){
url = url.Substring(0, colon);
}
}
// return file name
return BrowserUtils.GetFileNameFromUrl(url) ?? "unknown";
}
} }
} }

View File

@@ -5,31 +5,31 @@ using TweetDuck.Core.Controls;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
namespace TweetDuck.Core.Handling{ namespace TweetDuck.Core.Handling{
class ContextMenuBrowser : ContextMenuBase{ sealed class ContextMenuBrowser : ContextMenuBase{
private const int MenuGlobal = 26600; private const CefMenuCommand MenuGlobal = (CefMenuCommand)26600;
private const int MenuMute = 26601; private const CefMenuCommand MenuMute = (CefMenuCommand)26601;
private const int MenuSettings = 26602; private const CefMenuCommand MenuSettings = (CefMenuCommand)26602;
private const int MenuPlugins = 26003; private const CefMenuCommand MenuPlugins = (CefMenuCommand)26003;
private const int MenuAbout = 26604; private const CefMenuCommand MenuAbout = (CefMenuCommand)26604;
private const int MenuOpenTweetUrl = 26610; private const CefMenuCommand MenuOpenTweetUrl = (CefMenuCommand)26610;
private const int MenuCopyTweetUrl = 26611; private const CefMenuCommand MenuCopyTweetUrl = (CefMenuCommand)26611;
private const int MenuOpenQuotedTweetUrl = 26612; private const CefMenuCommand MenuOpenQuotedTweetUrl = (CefMenuCommand)26612;
private const int MenuCopyQuotedTweetUrl = 26613; private const CefMenuCommand MenuCopyQuotedTweetUrl = (CefMenuCommand)26613;
private const int MenuScreenshotTweet = 26614; private const CefMenuCommand MenuScreenshotTweet = (CefMenuCommand)26614;
private const string TitleReloadBrowser = "Reload browser"; private const string TitleReloadBrowser = "Reload browser";
private const string TitleMuteNotifications = "Mute notifications"; private const string TitleMuteNotifications = "Mute notifications";
private const string TitleSettings = "Settings"; private const string TitleSettings = "Options";
private const string TitlePlugins = "Plugins"; private const string TitlePlugins = "Plugins";
private const string TitleAboutProgram = "About "+Program.BrandName; private const string TitleAboutProgram = "About "+Program.BrandName;
private readonly FormBrowser form; private readonly FormBrowser form;
private string lastHighlightedTweet; private string lastHighlightedTweetUrl;
private string lastHighlightedQuotedTweet; private string lastHighlightedQuoteUrl;
public ContextMenuBrowser(FormBrowser form) : base(form){ public ContextMenuBrowser(FormBrowser form){
this.form = form; this.form = form;
} }
@@ -46,23 +46,23 @@ namespace TweetDuck.Core.Handling{
base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model); base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model);
lastHighlightedTweet = TweetDeckBridge.LastHighlightedTweet; lastHighlightedTweetUrl = TweetDeckBridge.LastHighlightedTweetUrl;
lastHighlightedQuotedTweet = TweetDeckBridge.LastHighlightedQuotedTweet; lastHighlightedQuoteUrl = TweetDeckBridge.LastHighlightedQuoteUrl;
if (!BrowserUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){ if (!TwitterUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){
lastHighlightedTweet = string.Empty; lastHighlightedTweetUrl = string.Empty;
lastHighlightedQuotedTweet = string.Empty; lastHighlightedQuoteUrl = string.Empty;
} }
if (!string.IsNullOrEmpty(lastHighlightedTweet) && (parameters.TypeFlags & (ContextMenuType.Editable | ContextMenuType.Selection)) == 0){ if (!string.IsNullOrEmpty(lastHighlightedTweetUrl) && (parameters.TypeFlags & (ContextMenuType.Editable | ContextMenuType.Selection)) == 0){
model.AddItem((CefMenuCommand)MenuOpenTweetUrl, "Open tweet in browser"); model.AddItem(MenuOpenTweetUrl, "Open tweet in browser");
model.AddItem((CefMenuCommand)MenuCopyTweetUrl, "Copy tweet address"); model.AddItem(MenuCopyTweetUrl, "Copy tweet address");
model.AddItem((CefMenuCommand)MenuScreenshotTweet, "Screenshot tweet to clipboard"); model.AddItem(MenuScreenshotTweet, "Screenshot tweet to clipboard");
if (!string.IsNullOrEmpty(lastHighlightedQuotedTweet)){ if (!string.IsNullOrEmpty(lastHighlightedQuoteUrl)){
model.AddSeparator(); model.AddSeparator();
model.AddItem((CefMenuCommand)MenuOpenQuotedTweetUrl, "Open quoted tweet in browser"); model.AddItem(MenuOpenQuotedTweetUrl, "Open quoted tweet in browser");
model.AddItem((CefMenuCommand)MenuCopyQuotedTweetUrl, "Copy quoted tweet address"); model.AddItem(MenuCopyQuotedTweetUrl, "Copy quoted tweet address");
} }
model.AddSeparator(); model.AddSeparator();
@@ -71,16 +71,16 @@ namespace TweetDuck.Core.Handling{
if ((parameters.TypeFlags & (ContextMenuType.Editable | ContextMenuType.Selection)) == 0){ if ((parameters.TypeFlags & (ContextMenuType.Editable | ContextMenuType.Selection)) == 0){
AddSeparator(model); AddSeparator(model);
IMenuModel globalMenu = model.Count == 0 ? model : model.AddSubMenu((CefMenuCommand)MenuGlobal, Program.BrandName); IMenuModel globalMenu = model.Count == 0 ? model : model.AddSubMenu(MenuGlobal, Program.BrandName);
globalMenu.AddItem(CefMenuCommand.Reload, TitleReloadBrowser); globalMenu.AddItem(CefMenuCommand.Reload, TitleReloadBrowser);
globalMenu.AddCheckItem((CefMenuCommand)MenuMute, TitleMuteNotifications); globalMenu.AddCheckItem(MenuMute, TitleMuteNotifications);
globalMenu.SetChecked((CefMenuCommand)MenuMute, Program.UserConfig.MuteNotifications); globalMenu.SetChecked(MenuMute, Program.UserConfig.MuteNotifications);
globalMenu.AddSeparator(); globalMenu.AddSeparator();
globalMenu.AddItem((CefMenuCommand)MenuSettings, TitleSettings); globalMenu.AddItem(MenuSettings, TitleSettings);
globalMenu.AddItem((CefMenuCommand)MenuPlugins, TitlePlugins); globalMenu.AddItem(MenuPlugins, TitlePlugins);
globalMenu.AddItem((CefMenuCommand)MenuAbout, TitleAboutProgram); globalMenu.AddItem(MenuAbout, TitleAboutProgram);
if (HasDevTools){ if (HasDevTools){
globalMenu.AddSeparator(); globalMenu.AddSeparator();
@@ -96,8 +96,8 @@ namespace TweetDuck.Core.Handling{
return true; return true;
} }
switch((int)commandId){ switch(commandId){
case (int)CefMenuCommand.Reload: case CefMenuCommand.Reload:
form.InvokeAsyncSafe(form.ReloadToTweetDeck); form.InvokeAsyncSafe(form.ReloadToTweetDeck);
return true; return true;
@@ -118,11 +118,11 @@ namespace TweetDuck.Core.Handling{
return true; return true;
case MenuOpenTweetUrl: case MenuOpenTweetUrl:
BrowserUtils.OpenExternalBrowser(lastHighlightedTweet); OpenBrowser(form, lastHighlightedTweetUrl);
return true; return true;
case MenuCopyTweetUrl: case MenuCopyTweetUrl:
SetClipboardText(lastHighlightedTweet); SetClipboardText(form, lastHighlightedTweetUrl);
return true; return true;
case MenuScreenshotTweet: case MenuScreenshotTweet:
@@ -130,11 +130,11 @@ namespace TweetDuck.Core.Handling{
return true; return true;
case MenuOpenQuotedTweetUrl: case MenuOpenQuotedTweetUrl:
BrowserUtils.OpenExternalBrowser(lastHighlightedQuotedTweet); OpenBrowser(form, lastHighlightedQuoteUrl);
return true; return true;
case MenuCopyQuotedTweetUrl: case MenuCopyQuotedTweetUrl:
SetClipboardText(lastHighlightedQuotedTweet); SetClipboardText(form, lastHighlightedQuoteUrl);
return true; return true;
} }

View File

@@ -3,16 +3,17 @@ using TweetDuck.Core.Controls;
using TweetDuck.Core.Notification; using TweetDuck.Core.Notification;
namespace TweetDuck.Core.Handling{ namespace TweetDuck.Core.Handling{
class ContextMenuNotification : ContextMenuBase{ sealed class ContextMenuNotification : ContextMenuBase{
private const int MenuSkipTweet = 26600; private const CefMenuCommand MenuViewDetail = (CefMenuCommand)26600;
private const int MenuFreeze = 26601; private const CefMenuCommand MenuSkipTweet = (CefMenuCommand)26601;
private const int MenuCopyTweetUrl = 26602; private const CefMenuCommand MenuFreeze = (CefMenuCommand)26602;
private const int MenuCopyQuotedTweetUrl = 26603; private const CefMenuCommand MenuCopyTweetUrl = (CefMenuCommand)26603;
private const CefMenuCommand MenuCopyQuotedTweetUrl = (CefMenuCommand)26604;
private readonly FormNotificationBase form; private readonly FormNotificationBase form;
private readonly bool enableCustomMenu; private readonly bool enableCustomMenu;
public ContextMenuNotification(FormNotificationBase form, bool enableCustomMenu) : base(form){ public ContextMenuNotification(FormNotificationBase form, bool enableCustomMenu){
this.form = form; this.form = form;
this.enableCustomMenu = enableCustomMenu; this.enableCustomMenu = enableCustomMenu;
} }
@@ -28,23 +29,26 @@ namespace TweetDuck.Core.Handling{
base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model); base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model);
if (enableCustomMenu){ if (enableCustomMenu){
model.AddItem((CefMenuCommand)MenuSkipTweet, "Skip tweet"); if (form.CanViewDetail){
model.AddCheckItem((CefMenuCommand)MenuFreeze, "Freeze"); model.AddItem(MenuViewDetail, "View detail");
model.SetChecked((CefMenuCommand)MenuFreeze, form.FreezeTimer);
model.AddSeparator();
if (!string.IsNullOrEmpty(form.CurrentTweetUrl)){
model.AddItem((CefMenuCommand)MenuCopyTweetUrl, "Copy tweet address");
if (!string.IsNullOrEmpty(form.CurrentQuoteUrl)){
model.AddItem((CefMenuCommand)MenuCopyQuotedTweetUrl, "Copy quoted tweet address");
} }
model.AddItem(MenuSkipTweet, "Skip tweet");
model.AddCheckItem(MenuFreeze, "Freeze");
model.SetChecked(MenuFreeze, form.FreezeTimer);
if (!string.IsNullOrEmpty(form.CurrentTweetUrl)){
model.AddSeparator(); model.AddSeparator();
model.AddItem(MenuCopyTweetUrl, "Copy tweet address");
if (!string.IsNullOrEmpty(form.CurrentQuoteUrl)){
model.AddItem(MenuCopyQuotedTweetUrl, "Copy quoted tweet address");
}
} }
} }
if (HasDevTools){ if (HasDevTools){
model.AddSeparator();
AddDebugMenuItems(model); AddDebugMenuItems(model);
} }
@@ -58,7 +62,7 @@ namespace TweetDuck.Core.Handling{
return true; return true;
} }
switch((int)commandId){ switch(commandId){
case MenuSkipTweet: case MenuSkipTweet:
form.InvokeAsyncSafe(form.FinishCurrentNotification); form.InvokeAsyncSafe(form.FinishCurrentNotification);
return true; return true;
@@ -67,12 +71,16 @@ namespace TweetDuck.Core.Handling{
form.InvokeAsyncSafe(() => form.FreezeTimer = !form.FreezeTimer); form.InvokeAsyncSafe(() => form.FreezeTimer = !form.FreezeTimer);
return true; return true;
case MenuViewDetail:
form.InvokeSafe(form.ShowTweetDetail);
return true;
case MenuCopyTweetUrl: case MenuCopyTweetUrl:
SetClipboardText(form.CurrentTweetUrl); SetClipboardText(form, form.CurrentTweetUrl);
return true; return true;
case MenuCopyQuotedTweetUrl: case MenuCopyQuotedTweetUrl:
SetClipboardText(form.CurrentQuoteUrl); SetClipboardText(form, form.CurrentQuoteUrl);
return true; return true;
} }

View File

@@ -0,0 +1,19 @@
using System.Collections.Generic;
using CefSharp;
namespace TweetDuck.Core.Handling{
sealed class DragHandlerBrowser : IDragHandler{
public bool OnDragEnter(IWebBrowser browserControl, IBrowser browser, IDragData dragData, DragOperationsMask mask){
if (dragData.IsLink){
browserControl.ExecuteScriptAsync("window.TDGF_onGlobalDragStart", "link", dragData.LinkUrl);
}
else{
browserControl.ExecuteScriptAsync("window.TDGF_onGlobalDragStart", "unknown");
}
return false;
}
public void OnDraggableRegionsChanged(IWebBrowser browserControl, IBrowser browser, IList<DraggableRegion> regions){}
}
}

View File

@@ -1,8 +1,8 @@
using CefSharp; using System;
using System; using CefSharp;
namespace TweetDuck.Core.Handling{ namespace TweetDuck.Core.Handling.General{
class BrowserProcessHandler : IBrowserProcessHandler{ sealed class BrowserProcessHandler : IBrowserProcessHandler{
void IBrowserProcessHandler.OnContextInitialized(){ void IBrowserProcessHandler.OnContextInitialized(){
using(IRequestContext ctx = Cef.GetGlobalRequestContext()){ using(IRequestContext ctx = Cef.GetGlobalRequestContext()){
ctx.SetPreference("browser.enable_spellchecking", Program.UserConfig.EnableSpellCheck, out string _); ctx.SetPreference("browser.enable_spellchecking", Program.UserConfig.EnableSpellCheck, out string _);

View File

@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using CefSharp;
namespace TweetDuck.Core.Handling.General{
sealed class FileDialogHandler : IDialogHandler{
public bool OnFileDialog(IWebBrowser browserControl, IBrowser browser, CefFileDialogMode mode, string title, string defaultFilePath, List<string> acceptFilters, int selectedAcceptFilter, IFileDialogCallback callback){
CefFileDialogMode dialogType = mode & CefFileDialogMode.TypeMask;
if (dialogType == CefFileDialogMode.Open || dialogType == CefFileDialogMode.OpenMultiple){
string allFilters = string.Join(";", acceptFilters.Select(filter => "*"+filter));
using(OpenFileDialog dialog = new OpenFileDialog{
AutoUpgradeEnabled = true,
DereferenceLinks = true,
Multiselect = dialogType == CefFileDialogMode.OpenMultiple,
Title = "Open Files",
Filter = $"All Supported Formats ({allFilters})|{allFilters}|All Files (*.*)|*.*"
}){
if (dialog.ShowDialog() == DialogResult.OK){
string ext = Path.GetExtension(dialog.FileName);
callback.Continue(acceptFilters.FindIndex(filter => filter.Equals(ext, StringComparison.OrdinalIgnoreCase)), dialog.FileNames.ToList());
}
else{
callback.Cancel();
}
callback.Dispose();
}
return true;
}
else{
callback.Dispose();
return false;
}
}
}
}

View File

@@ -0,0 +1,90 @@
using System.Drawing;
using System.Windows.Forms;
using CefSharp;
using CefSharp.WinForms;
using TweetDuck.Core.Controls;
using TweetDuck.Core.Other;
using TweetDuck.Core.Utils;
namespace TweetDuck.Core.Handling.General{
sealed class JavaScriptDialogHandler : IJsDialogHandler{
private static FormMessage CreateMessageForm(string caption, string text){
MessageBoxIcon icon = MessageBoxIcon.None;
int pipe = text.IndexOf('|');
if (pipe != -1){
switch(text.Substring(0, pipe)){
case "error": icon = MessageBoxIcon.Error; break;
case "warning": icon = MessageBoxIcon.Warning; break;
case "info": icon = MessageBoxIcon.Information; break;
case "question": icon = MessageBoxIcon.Question; break;
default: return new FormMessage(caption, text, icon);
}
text = text.Substring(pipe+1);
}
return new FormMessage(caption, text, icon);
}
bool IJsDialogHandler.OnJSDialog(IWebBrowser browserControl, IBrowser browser, string originUrl, CefJsDialogType dialogType, string messageText, string defaultPromptText, IJsDialogCallback callback, ref bool suppressMessage){
((ChromiumWebBrowser)browserControl).InvokeSafe(() => {
FormMessage form;
TextBox input = null;
if (dialogType == CefJsDialogType.Alert){
form = CreateMessageForm("Browser Message", messageText);
form.AddButton(FormMessage.OK, ControlType.Accept | ControlType.Focused);
}
else if (dialogType == CefJsDialogType.Confirm){
form = CreateMessageForm("Browser Confirmation", messageText);
form.AddButton(FormMessage.No, DialogResult.No, ControlType.Cancel);
form.AddButton(FormMessage.Yes, ControlType.Focused);
}
else if (dialogType == CefJsDialogType.Prompt){
form = CreateMessageForm("Browser Prompt", messageText);
form.AddButton(FormMessage.Cancel, DialogResult.Cancel, ControlType.Cancel);
form.AddButton(FormMessage.OK, ControlType.Accept | ControlType.Focused);
float dpiScale = form.GetDPIScale();
int inputPad = form.HasIcon ? 43 : 0;
input = new TextBox{
Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom,
Location = new Point(BrowserUtils.Scale(22+inputPad, dpiScale), form.ActionPanelY-BrowserUtils.Scale(46, dpiScale)),
Size = new Size(form.ClientSize.Width-BrowserUtils.Scale(44+inputPad, dpiScale), 20)
};
form.Controls.Add(input);
form.ActiveControl = input;
form.Height += input.Size.Height+input.Margin.Vertical;
}
else{
callback.Continue(false);
return;
}
bool success = form.ShowDialog() == DialogResult.OK;
if (input == null){
callback.Continue(success);
}
else{
callback.Continue(success, input.Text);
input.Dispose();
}
form.Dispose();
});
return true;
}
bool IJsDialogHandler.OnJSBeforeUnload(IWebBrowser browserControl, IBrowser browser, string message, bool isReload, IJsDialogCallback callback){
return false;
}
void IJsDialogHandler.OnResetDialogState(IWebBrowser browserControl, IBrowser browser){}
void IJsDialogHandler.OnDialogClosed(IWebBrowser browserControl, IBrowser browser){}
}
}

View File

@@ -1,8 +1,9 @@
using CefSharp; using CefSharp;
using TweetDuck.Core.Controls;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
namespace TweetDuck.Core.Handling{ namespace TweetDuck.Core.Handling.General{
class LifeSpanHandler : ILifeSpanHandler{ sealed class LifeSpanHandler : ILifeSpanHandler{
public bool OnBeforePopup(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser){ public bool OnBeforePopup(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser){
newBrowser = null; newBrowser = null;
@@ -11,7 +12,7 @@ namespace TweetDuck.Core.Handling{
case WindowOpenDisposition.NewForegroundTab: case WindowOpenDisposition.NewForegroundTab:
case WindowOpenDisposition.NewPopup: case WindowOpenDisposition.NewPopup:
case WindowOpenDisposition.NewWindow: case WindowOpenDisposition.NewWindow:
BrowserUtils.OpenExternalBrowser(targetUrl); browserControl.AsControl().InvokeAsyncSafe(() => BrowserUtils.OpenExternalBrowser(targetUrl));
return true; return true;
default: default:

View File

@@ -1,8 +1,8 @@
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using CefSharp; using CefSharp;
namespace TweetDuck.Core.Handling{ namespace TweetDuck.Core.Handling.General{
abstract class RequestHandler : IRequestHandler{ abstract class RequestHandlerBase : IRequestHandler{
// Browser // Browser
public virtual void OnRenderViewReady(IWebBrowser browserControl, IBrowser browser){} public virtual void OnRenderViewReady(IWebBrowser browserControl, IBrowser browser){}

View File

@@ -1,60 +0,0 @@
using CefSharp;
using CefSharp.WinForms;
using System.Drawing;
using System.Windows.Forms;
using TweetDuck.Core.Controls;
using TweetDuck.Core.Other;
namespace TweetDuck.Core.Handling {
class JavaScriptDialogHandler : IJsDialogHandler{
bool IJsDialogHandler.OnJSDialog(IWebBrowser browserControl, IBrowser browser, string originUrl, CefJsDialogType dialogType, string messageText, string defaultPromptText, IJsDialogCallback callback, ref bool suppressMessage){
((ChromiumWebBrowser)browserControl).InvokeSafe(() => {
FormMessage form = new FormMessage(Program.BrandName, messageText, MessageBoxIcon.None);
TextBox input = null;
if (dialogType == CefJsDialogType.Alert){
form.AcceptButton = form.AddButton("OK");
}
else if (dialogType == CefJsDialogType.Confirm){
form.CancelButton = form.AddButton("No", DialogResult.No);
form.AcceptButton = form.AddButton("Yes");
}
else if (dialogType == CefJsDialogType.Prompt){
form.CancelButton = form.AddButton("Cancel", DialogResult.Cancel);
form.AcceptButton = form.AddButton("OK");
input = new TextBox{
Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom,
Location = new Point(27, form.ActionPanelY-46),
Size = new Size(form.ClientSize.Width-54, 20)
};
form.Controls.Add(input);
form.ActiveControl = input;
form.Height += input.Size.Height+input.Margin.Vertical;
}
bool success = form.ShowDialog() == DialogResult.OK;
if (input == null){
callback.Continue(success);
}
else{
callback.Continue(success, input.Text);
input.Dispose();
}
form.Dispose();
});
return true;
}
bool IJsDialogHandler.OnJSBeforeUnload(IWebBrowser browserControl, IBrowser browser, string message, bool isReload, IJsDialogCallback callback){
return false;
}
void IJsDialogHandler.OnResetDialogState(IWebBrowser browserControl, IBrowser browser){}
void IJsDialogHandler.OnDialogClosed(IWebBrowser browserControl, IBrowser browser){}
}
}

View File

@@ -0,0 +1,20 @@
using System.Windows.Forms;
using CefSharp;
namespace TweetDuck.Core.Handling{
sealed class KeyboardHandlerBrowser : IKeyboardHandler{
private readonly FormBrowser form;
public KeyboardHandlerBrowser(FormBrowser form){
this.form = form;
}
bool IKeyboardHandler.OnPreKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut){
return type == KeyType.RawKeyDown && form.ProcessBrowserKey((Keys)windowsKeyCode);
}
bool IKeyboardHandler.OnKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey){
return false;
}
}
}

View File

@@ -0,0 +1,38 @@
using CefSharp;
using System.Windows.Forms;
using TweetDuck.Core.Controls;
using TweetDuck.Core.Notification;
namespace TweetDuck.Core.Handling {
sealed class KeyboardHandlerNotification : IKeyboardHandler{
private readonly FormNotificationBase notification;
public KeyboardHandlerNotification(FormNotificationBase notification){
this.notification = notification;
}
bool IKeyboardHandler.OnPreKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut){
if (type == KeyType.RawKeyDown && !browser.FocusedFrame.Url.StartsWith("chrome-devtools://")){
switch((Keys)windowsKeyCode){
case Keys.Enter:
notification.InvokeAsyncSafe(notification.FinishCurrentNotification);
return true;
case Keys.Escape:
notification.InvokeAsyncSafe(() => notification.HideNotification(true));
return true;
case Keys.Space:
notification.InvokeAsyncSafe(() => notification.FreezeTimer = !notification.FreezeTimer);
return true;
}
}
return false;
}
bool IKeyboardHandler.OnKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey){
return false;
}
}
}

View File

@@ -1,9 +1,18 @@
using CefSharp; using CefSharp;
using TweetDuck.Core.Handling.General;
namespace TweetDuck.Core.Handling{ namespace TweetDuck.Core.Handling{
class RequestHandlerBrowser : RequestHandler{ sealed class RequestHandlerBrowser : RequestHandlerBase{
public override void OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status){ public override void OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status){
browser.Reload(); browser.Reload();
} }
public override CefReturnValue OnBeforeResourceLoad(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback){
if (request.ResourceType == ResourceType.Script && request.Url.Contains("analytics.")){
return CefReturnValue.Cancel;
}
return CefReturnValue.Continue;
}
} }
} }

View File

@@ -4,7 +4,7 @@ using System.IO;
using System.Text; using System.Text;
namespace TweetDuck.Core.Handling{ namespace TweetDuck.Core.Handling{
class ResourceHandlerNotification : IResourceHandler{ sealed class ResourceHandlerNotification : IResourceHandler{
private readonly NameValueCollection headers = new NameValueCollection(0); private readonly NameValueCollection headers = new NameValueCollection(0);
private MemoryStream dataIn; private MemoryStream dataIn;

View File

@@ -6,6 +6,8 @@ using System.Windows.Forms;
using TweetDuck.Configuration; using TweetDuck.Configuration;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Handling; using TweetDuck.Core.Handling;
using TweetDuck.Core.Handling.General;
using TweetDuck.Core.Other.Management;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
namespace TweetDuck.Core.Notification{ namespace TweetDuck.Core.Notification{
@@ -57,34 +59,41 @@ namespace TweetDuck.Core.Notification{
set{ set{
Visible = (base.Location = value) != ControlExtensions.InvisibleLocation; Visible = (base.Location = value) != ControlExtensions.InvisibleLocation;
FormBorderStyle = GetBorderStyle(CanResizeWindow);
if (WindowsUtils.ShouldAvoidToolWindow){
FormBorderStyle = Visible ? FormBorderStyle.FixedSingle : FormBorderStyle.FixedToolWindow; // workaround for alt+tab
}
} }
} }
public Func<bool> CanMoveWindow = () => true; public bool CanResizeWindow{
get => FormBorderStyle == FormBorderStyle.Sizable || FormBorderStyle == FormBorderStyle.SizableToolWindow;
set => FormBorderStyle = GetBorderStyle(value);
}
public Func<bool> CanMoveWindow { get; set; } = () => true;
protected override bool ShowWithoutActivation => true; protected override bool ShowWithoutActivation => true;
protected readonly Form owner; protected double SizeScale => dpiScale*Program.UserConfig.ZoomMultiplier;
protected readonly FormBrowser owner;
protected readonly ChromiumWebBrowser browser; protected readonly ChromiumWebBrowser browser;
private readonly ResourceHandlerNotification resourceHandler = new ResourceHandlerNotification(); private readonly ResourceHandlerNotification resourceHandler = new ResourceHandlerNotification();
private readonly float dpiScale;
private string currentColumn; private TweetNotification currentNotification;
private int pauseCounter; private int pauseCounter;
public string CurrentTweetUrl => currentNotification?.TweetUrl;
public string CurrentQuoteUrl => currentNotification?.QuoteUrl;
public bool CanViewDetail => currentNotification != null && !string.IsNullOrEmpty(currentNotification.ColumnId) && !string.IsNullOrEmpty(currentNotification.ChirpId);
public bool IsPaused => pauseCounter > 0; public bool IsPaused => pauseCounter > 0;
public bool FreezeTimer { get; set; } public bool FreezeTimer { get; set; }
public bool ContextMenuOpen { get; set; } public bool ContextMenuOpen { get; set; }
public string CurrentTweetUrl { get; private set; }
public string CurrentQuoteUrl { get; private set; }
public event EventHandler Initialized; public event EventHandler Initialized;
public FormNotificationBase(Form owner, bool enableContextMenu){ protected FormNotificationBase(FormBrowser owner, bool enableContextMenu){
InitializeComponent(); InitializeComponent();
this.owner = owner; this.owner = owner;
@@ -92,6 +101,7 @@ namespace TweetDuck.Core.Notification{
this.browser = new ChromiumWebBrowser("about:blank"){ this.browser = new ChromiumWebBrowser("about:blank"){
MenuHandler = new ContextMenuNotification(this, enableContextMenu), MenuHandler = new ContextMenuNotification(this, enableContextMenu),
JsDialogHandler = new JavaScriptDialogHandler(),
LifeSpanHandler = new LifeSpanHandler() LifeSpanHandler = new LifeSpanHandler()
}; };
@@ -103,8 +113,10 @@ namespace TweetDuck.Core.Notification{
this.browser.ConsoleMessage += BrowserUtils.HandleConsoleMessage; this.browser.ConsoleMessage += BrowserUtils.HandleConsoleMessage;
#endif #endif
this.dpiScale = this.GetDPIScale();
DefaultResourceHandlerFactory handlerFactory = (DefaultResourceHandlerFactory)browser.ResourceHandlerFactory; DefaultResourceHandlerFactory handlerFactory = (DefaultResourceHandlerFactory)browser.ResourceHandlerFactory;
handlerFactory.RegisterHandler("https://tweetdeck.twitter.com", this.resourceHandler); handlerFactory.RegisterHandler(TwitterUtils.TweetDeckURL, this.resourceHandler);
Controls.Add(browser); Controls.Add(browser);
@@ -133,7 +145,10 @@ namespace TweetDuck.Core.Notification{
private void Browser_IsBrowserInitializedChanged(object sender, IsBrowserInitializedChangedEventArgs e){ private void Browser_IsBrowserInitializedChanged(object sender, IsBrowserInitializedChangedEventArgs e){
if (e.IsBrowserInitialized){ if (e.IsBrowserInitialized){
Initialized?.Invoke(this, new EventArgs()); Initialized?.Invoke(this, EventArgs.Empty);
int identifier = browser.GetBrowser().Identifier;
Disposed += (sender2, args2) => BrowserProcesses.Forget(identifier);
} }
} }
@@ -145,7 +160,7 @@ namespace TweetDuck.Core.Notification{
} }
Location = ControlExtensions.InvisibleLocation; Location = ControlExtensions.InvisibleLocation;
currentColumn = null; currentNotification = null;
} }
public virtual void FinishCurrentNotification(){} public virtual void FinishCurrentNotification(){}
@@ -168,24 +183,22 @@ namespace TweetDuck.Core.Notification{
} }
protected virtual void LoadTweet(TweetNotification tweet){ protected virtual void LoadTweet(TweetNotification tweet){
CurrentTweetUrl = tweet.TweetUrl; currentNotification = tweet;
CurrentQuoteUrl = tweet.QuoteUrl;
currentColumn = tweet.Column;
resourceHandler.SetHTML(GetTweetHTML(tweet)); resourceHandler.SetHTML(GetTweetHTML(tweet));
browser.Load("https://tweetdeck.twitter.com"); browser.Load(TwitterUtils.TweetDeckURL);
} }
protected virtual void SetNotificationSize(int width, int height){ protected virtual void SetNotificationSize(int width, int height){
browser.ClientSize = ClientSize = new Size((int)Math.Round(width*Program.UserConfig.ZoomMultiplier), (int)Math.Round(height*Program.UserConfig.ZoomMultiplier)); browser.ClientSize = ClientSize = new Size(BrowserUtils.Scale(width, SizeScale), BrowserUtils.Scale(height, SizeScale));
}
protected virtual void OnNotificationReady(){
MoveToVisibleLocation();
} }
protected virtual void UpdateTitle(){ protected virtual void UpdateTitle(){
Text = string.IsNullOrEmpty(currentColumn) || !Program.UserConfig.DisplayNotificationColumn ? Program.BrandName : Program.BrandName+" - "+currentColumn; string title = currentNotification?.ColumnTitle;
Text = string.IsNullOrEmpty(title) || !Program.UserConfig.DisplayNotificationColumn ? Program.BrandName : Program.BrandName+" - "+title;
}
public void ShowTweetDetail(){
owner.ShowTweetDetail(currentNotification.ColumnId, currentNotification.ChirpId, currentNotification.TweetUrl);
} }
public void MoveToVisibleLocation(){ public void MoveToVisibleLocation(){
@@ -207,5 +220,14 @@ namespace TweetDuck.Core.Notification{
toolTip.Show(text, this, position); toolTip.Show(text, this, position);
} }
} }
private FormBorderStyle GetBorderStyle(bool sizable){
if (WindowsUtils.ShouldAvoidToolWindow && Visible){ // Visible = workaround for alt+tab
return sizable ? FormBorderStyle.Sizable : FormBorderStyle.FixedSingle;
}
else{
return sizable ? FormBorderStyle.SizableToolWindow : FormBorderStyle.FixedToolWindow;
}
}
} }
} }

View File

@@ -49,7 +49,7 @@
this.progressBarTimer.Margin = new System.Windows.Forms.Padding(0); this.progressBarTimer.Margin = new System.Windows.Forms.Padding(0);
this.progressBarTimer.Maximum = 1000; this.progressBarTimer.Maximum = 1000;
this.progressBarTimer.Name = "progressBarTimer"; this.progressBarTimer.Name = "progressBarTimer";
this.progressBarTimer.Size = new System.Drawing.Size(284, 4); this.progressBarTimer.Size = new System.Drawing.Size(284, TimerBarHeight);
this.progressBarTimer.TabIndex = 1; this.progressBarTimer.TabIndex = 1;
// //
// FormNotification // FormNotification

View File

@@ -4,7 +4,9 @@ using System.Drawing;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Bridge; using TweetDuck.Core.Bridge;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Handling;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetDuck.Data;
using TweetDuck.Plugins; using TweetDuck.Plugins;
using TweetDuck.Plugins.Enums; using TweetDuck.Plugins.Enums;
using TweetDuck.Resources; using TweetDuck.Resources;
@@ -12,32 +14,10 @@ using TweetDuck.Resources;
namespace TweetDuck.Core.Notification{ namespace TweetDuck.Core.Notification{
partial class FormNotificationMain : FormNotificationBase{ partial class FormNotificationMain : FormNotificationBase{
private const string NotificationScriptFile = "notification.js"; private const string NotificationScriptFile = "notification.js";
private const int TimerBarHeight = 4;
private static readonly string NotificationScriptIdentifier = ScriptLoader.GetRootIdentifier(NotificationScriptFile); private static readonly string NotificationScriptIdentifier = ScriptLoader.GetRootIdentifier(NotificationScriptFile);
private static readonly string PluginScriptIdentifier = ScriptLoader.GetRootIdentifier(PluginManager.PluginNotificationScriptFile); private static readonly string NotificationJS = ScriptLoader.LoadResource(NotificationScriptFile);
private static readonly string NotificationJS, PluginJS;
static FormNotificationMain(){
NotificationJS = ScriptLoader.LoadResource(NotificationScriptFile);
PluginJS = ScriptLoader.LoadResource(PluginManager.PluginNotificationScriptFile);
}
private static int BaseClientWidth{
get{
int level = TweetNotification.FontSizeLevel;
int width = level == 0 ? 284 : (int)Math.Round(284.0*(1.0+0.05*level));
return (int)Math.Round(width*Program.UserConfig.ZoomMultiplier);
}
}
private static int BaseClientHeight{
get{
int level = TweetNotification.FontSizeLevel;
int height = level == 0 ? 118 : (int)Math.Round(118.0*(1.0+0.075*level));
return (int)Math.Round(height*Program.UserConfig.ZoomMultiplier);
}
}
private readonly PluginManager plugins; private readonly PluginManager plugins;
@@ -51,9 +31,9 @@ namespace TweetDuck.Core.Notification{
private bool? prevDisplayTimer; private bool? prevDisplayTimer;
private int? prevFontSize; private int? prevFontSize;
private bool RequiresResize{ public bool RequiresResize{
get{ get{
return !prevDisplayTimer.HasValue || !prevFontSize.HasValue || prevDisplayTimer != Program.UserConfig.DisplayNotificationTimer || prevFontSize != TweetNotification.FontSizeLevel; return !prevDisplayTimer.HasValue || !prevFontSize.HasValue || prevDisplayTimer != Program.UserConfig.DisplayNotificationTimer || prevFontSize != TweetNotification.FontSizeLevel || CanResizeWindow;
} }
set{ set{
@@ -68,11 +48,41 @@ namespace TweetDuck.Core.Notification{
} }
} }
private int BaseClientWidth{
get{
switch(Program.UserConfig.NotificationSize){
default:
return BrowserUtils.Scale(284, SizeScale*(1.0+0.05*TweetNotification.FontSizeLevel));
case TweetNotification.Size.Custom:
return Program.UserConfig.CustomNotificationSize.Width;
}
}
}
private int BaseClientHeight{
get{
switch(Program.UserConfig.NotificationSize){
default:
return BrowserUtils.Scale(122, SizeScale*(1.0+0.075*TweetNotification.FontSizeLevel));
case TweetNotification.Size.Custom:
return Program.UserConfig.CustomNotificationSize.Height;
}
}
}
public Size BrowserSize{
get => Program.UserConfig.DisplayNotificationTimer ? new Size(ClientSize.Width, ClientSize.Height-TimerBarHeight) : ClientSize;
}
public FormNotificationMain(FormBrowser owner, PluginManager pluginManager, bool enableContextMenu) : base(owner, enableContextMenu){ public FormNotificationMain(FormBrowser owner, PluginManager pluginManager, bool enableContextMenu) : base(owner, enableContextMenu){
InitializeComponent(); InitializeComponent();
this.plugins = pluginManager; this.plugins = pluginManager;
browser.KeyboardHandler = new KeyboardHandlerNotification(this);
browser.RegisterAsyncJsObject("$TD", new TweetDeckBridge(owner, this)); browser.RegisterAsyncJsObject("$TD", new TweetDeckBridge(owner, this));
browser.RegisterAsyncJsObject("$TDP", plugins.Bridge); browser.RegisterAsyncJsObject("$TDP", plugins.Bridge);
@@ -103,8 +113,8 @@ namespace TweetDuck.Core.Notification{
if (nCode == 0){ if (nCode == 0){
int eventType = wParam.ToInt32(); int eventType = wParam.ToInt32();
if (eventType == NativeMethods.WM_MOUSEWHEEL && browser.Bounds.Contains(PointToClient(Cursor.Position)) && !ContainsFocus && !owner.ContainsFocus){ if (eventType == NativeMethods.WM_MOUSEWHEEL && browser.Bounds.Contains(PointToClient(Cursor.Position))){
browser.SendMouseWheelEvent(0, 0, 0, NativeMethods.GetMouseHookData(lParam), CefEventFlags.None); browser.SendMouseWheelEvent(0, 0, 0, BrowserUtils.Scale(NativeMethods.GetMouseHookData(lParam), Program.UserConfig.NotificationScrollSpeed/100.0), CefEventFlags.None);
return NativeMethods.HOOK_HANDLED; return NativeMethods.HOOK_HANDLED;
} }
else if (eventType == NativeMethods.WM_XBUTTONDOWN && DesktopBounds.Contains(Cursor.Position)){ else if (eventType == NativeMethods.WM_XBUTTONDOWN && DesktopBounds.Contains(Cursor.Position)){
@@ -154,14 +164,9 @@ namespace TweetDuck.Core.Notification{
private void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){ private void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
if (e.Frame.IsMain && NotificationJS != null && browser.Address != "about:blank"){ if (e.Frame.IsMain && NotificationJS != null && browser.Address != "about:blank"){
e.Frame.ExecuteJavaScriptAsync(PropertyBridge.GenerateScript(PropertyBridge.Properties.AllNotification)); e.Frame.ExecuteJavaScriptAsync(PropertyBridge.GenerateScript(PropertyBridge.Environment.Notification));
ScriptLoader.ExecuteScript(e.Frame, NotificationJS, NotificationScriptIdentifier); ScriptLoader.ExecuteScript(e.Frame, NotificationJS, NotificationScriptIdentifier);
plugins.ExecutePlugins(e.Frame, PluginEnvironment.Notification);
if (plugins.HasAnyPlugin(PluginEnvironment.Notification)){
ScriptLoader.ExecuteScript(e.Frame, PluginJS, PluginScriptIdentifier);
ScriptLoader.ExecuteFile(e.Frame, PluginManager.PluginGlobalScriptFile);
plugins.ExecutePlugins(e.Frame, PluginEnvironment.Notification, false);
}
} }
} }
@@ -175,7 +180,7 @@ namespace TweetDuck.Core.Notification{
timeLeft -= timerProgress.Interval; timeLeft -= timerProgress.Interval;
int value = (int)Math.Round(1025.0*(totalTime-timeLeft)/totalTime); int value = BrowserUtils.Scale(1025, (totalTime-timeLeft)/(double)totalTime);
progressBarTimer.SetValueInstant(Math.Min(1000, Math.Max(0, Program.UserConfig.NotificationTimerCountDown ? 1000-value : value))); progressBarTimer.SetValueInstant(Math.Min(1000, Math.Max(0, Program.UserConfig.NotificationTimerCountDown ? 1000-value : value)));
if (timeLeft <= 0){ if (timeLeft <= 0){
@@ -253,7 +258,7 @@ namespace TweetDuck.Core.Notification{
protected override void SetNotificationSize(int width, int height){ protected override void SetNotificationSize(int width, int height){
if (Program.UserConfig.DisplayNotificationTimer){ if (Program.UserConfig.DisplayNotificationTimer){
ClientSize = new Size(width, height+4); ClientSize = new Size(width, height+TimerBarHeight);
progressBarTimer.Visible = true; progressBarTimer.Visible = true;
} }
else{ else{
@@ -274,7 +279,7 @@ namespace TweetDuck.Core.Notification{
StartMouseHook(); StartMouseHook();
} }
protected override void OnNotificationReady(){ protected virtual void OnNotificationReady(){
PrepareAndDisplayWindow(); PrepareAndDisplayWindow();
timerProgress.Start(); timerProgress.Start();
} }

View File

@@ -2,24 +2,40 @@
using System.Drawing; using System.Drawing;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.Windows.Forms; using System.Windows.Forms;
using CefSharp;
using TweetDuck.Core.Bridge; using TweetDuck.Core.Bridge;
using TweetDuck.Core.Other;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetDuck.Data;
using TweetDuck.Plugins;
using TweetDuck.Resources; using TweetDuck.Resources;
namespace TweetDuck.Core.Notification.Screenshot{ namespace TweetDuck.Core.Notification.Screenshot{
sealed class FormNotificationScreenshotable : FormNotificationBase{ sealed class FormNotificationScreenshotable : FormNotificationBase{
public FormNotificationScreenshotable(Action callback, Form owner) : base(owner, false){ private readonly PluginManager plugins;
public FormNotificationScreenshotable(Action callback, FormBrowser owner, PluginManager pluginManager) : base(owner, false){
this.plugins = pluginManager;
browser.RegisterAsyncJsObject("$TD_NotificationScreenshot", new CallbackBridge(this, callback)); browser.RegisterAsyncJsObject("$TD_NotificationScreenshot", new CallbackBridge(this, callback));
browser.FrameLoadEnd += (sender, args) => { browser.LoadingStateChanged += (sender, args) => {
if (args.Frame.IsMain && browser.Address != "about:blank"){ if (!args.IsLoading){
ScriptLoader.ExecuteScript(args.Frame, "window.setTimeout($TD_NotificationScreenshot.trigger, 67)", "gen:screenshot"); using(IFrame frame = args.Browser.MainFrame){
ScriptLoader.ExecuteScript(frame, "window.setTimeout($TD_NotificationScreenshot.trigger, 129)", "gen:screenshot");
}
} }
}; };
} }
protected override string GetTweetHTML(TweetNotification tweet){ protected override string GetTweetHTML(TweetNotification tweet){
return tweet.GenerateHtml(enableCustomCSS: false); string html = tweet.GenerateHtml("td-screenshot", false);
foreach(InjectedHTML injection in plugins.Bridge.NotificationInjections){
html = injection.Inject(html);
}
return html;
} }
public void LoadNotificationForScreenshot(TweetNotification tweet, int width, int height){ public void LoadNotificationForScreenshot(TweetNotification tweet, int width, int height){
@@ -31,7 +47,7 @@ namespace TweetDuck.Core.Notification.Screenshot{
IntPtr context = NativeMethods.GetDC(this.Handle); IntPtr context = NativeMethods.GetDC(this.Handle);
if (context == IntPtr.Zero){ if (context == IntPtr.Zero){
MessageBox.Show("Could not retrieve a graphics context handle for the notification window to take the screenshot.", "Screenshot Failed", MessageBoxButtons.OK, MessageBoxIcon.Error); FormMessage.Error("Screenshot Failed", "Could not retrieve a graphics context handle for the notification window to take the screenshot.", FormMessage.OK);
} }
else{ else{
using(Bitmap bmp = new Bitmap(ClientSize.Width, ClientSize.Height, PixelFormat.Format32bppRgb)){ using(Bitmap bmp = new Bitmap(ClientSize.Width, ClientSize.Height, PixelFormat.Format32bppRgb)){

View File

@@ -4,17 +4,20 @@
using System; using System;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Plugins;
namespace TweetDuck.Core.Notification.Screenshot{ namespace TweetDuck.Core.Notification.Screenshot{
sealed class TweetScreenshotManager : IDisposable{ sealed class TweetScreenshotManager : IDisposable{
private readonly Form owner; private readonly FormBrowser owner;
private readonly PluginManager plugins;
private readonly Timer timeout; private readonly Timer timeout;
private readonly Timer disposer; private readonly Timer disposer;
private FormNotificationScreenshotable screenshot; private FormNotificationScreenshotable screenshot;
public TweetScreenshotManager(Form owner){ public TweetScreenshotManager(FormBrowser owner, PluginManager pluginManager){
this.owner = owner; this.owner = owner;
this.plugins = pluginManager;
this.timeout = new Timer{ Interval = 8000 }; this.timeout = new Timer{ Interval = 8000 };
this.timeout.Tick += timeout_Tick; this.timeout.Tick += timeout_Tick;
@@ -25,8 +28,7 @@ namespace TweetDuck.Core.Notification.Screenshot{
private void timeout_Tick(object sender, EventArgs e){ private void timeout_Tick(object sender, EventArgs e){
timeout.Stop(); timeout.Stop();
screenshot.Location = ControlExtensions.InvisibleLocation; OnFinished();
disposer.Start();
} }
private void disposer_Tick(object sender, EventArgs e){ private void disposer_Tick(object sender, EventArgs e){
@@ -40,13 +42,17 @@ namespace TweetDuck.Core.Notification.Screenshot{
return; return;
} }
screenshot = new FormNotificationScreenshotable(Callback, owner){ screenshot = new FormNotificationScreenshotable(Callback, owner, plugins){
CanMoveWindow = () => false CanMoveWindow = () => false
}; };
screenshot.LoadNotificationForScreenshot(new TweetNotification(string.Empty, html, 0, string.Empty, string.Empty), width, height); screenshot.LoadNotificationForScreenshot(new TweetNotification(string.Empty, string.Empty, string.Empty, html, 0, string.Empty, string.Empty), width, height);
screenshot.Show(); screenshot.Show();
timeout.Start(); timeout.Start();
#if !(DEBUG && NO_HIDE_SCREENSHOTS)
owner.IsWaiting = true;
#endif
} }
private void Callback(){ private void Callback(){
@@ -58,14 +64,19 @@ namespace TweetDuck.Core.Notification.Screenshot{
screenshot.TakeScreenshot(); screenshot.TakeScreenshot();
#if !(DEBUG && NO_HIDE_SCREENSHOTS) #if !(DEBUG && NO_HIDE_SCREENSHOTS)
screenshot.Location = ControlExtensions.InvisibleLocation; OnFinished();
disposer.Start();
#else #else
screenshot.MoveToVisibleLocation(); screenshot.MoveToVisibleLocation();
screenshot.FormClosed += (sender, args) => disposer.Start(); screenshot.FormClosed += (sender, args) => disposer.Start();
#endif #endif
} }
private void OnFinished(){
screenshot.Location = ControlExtensions.InvisibleLocation;
owner.IsWaiting = false;
disposer.Start();
}
public void Dispose(){ public void Dispose(){
timeout.Dispose(); timeout.Dispose();
disposer.Dispose(); disposer.Dispose();

View File

@@ -1,12 +0,0 @@
using System;
namespace TweetDuck.Core.Notification.Sound{
interface ISoundNotificationPlayer : IDisposable{
string SupportedFormats { get; }
event EventHandler<PlaybackErrorEventArgs> PlaybackError;
void Play(string file);
void Stop();
}
}

View File

@@ -1,28 +1,32 @@
using System.Runtime.InteropServices; using System;
using TweetDuck.Core.Notification.Sound; using TweetLib.Audio;
namespace TweetDuck.Core.Notification{ namespace TweetDuck.Core.Notification{
static class SoundNotification{ sealed class SoundNotification : IDisposable{
private static bool? IsWMPAvailable; public string SupportedFormats => player.SupportedFormats;
public event EventHandler<PlaybackErrorEventArgs> PlaybackError;
public static ISoundNotificationPlayer New(){ private readonly AudioPlayer player;
if (IsWMPAvailable.HasValue){
if (IsWMPAvailable.Value){ public SoundNotification(){
return new SoundPlayerImplWMP(); this.player = AudioPlayer.New();
} this.player.PlaybackError += Player_PlaybackError;
else{
return new SoundPlayerImplFallback();
}
} }
try{ public void Play(string file){
SoundPlayerImplWMP implWMP = new SoundPlayerImplWMP(); player.Play(file);
IsWMPAvailable = true;
return implWMP;
}catch(COMException){
IsWMPAvailable = false;
return new SoundPlayerImplFallback();
} }
public bool SetVolume(int volume){
return player.SetVolume(volume);
}
private void Player_PlaybackError(object sender, PlaybackErrorEventArgs e){
PlaybackError?.Invoke(this, e);
}
public void Dispose(){
player.Dispose();
} }
} }
} }

View File

@@ -9,7 +9,7 @@ namespace TweetDuck.Core.Notification{
private const string DefaultFontSizeClass = "medium"; private const string DefaultFontSizeClass = "medium";
private const string DefaultHeadTag = @"<meta charset='utf-8'><meta http-equiv='X-UA-Compatible' content='chrome=1'><link rel='stylesheet' href='https://ton.twimg.com/tweetdeck-web/web/css/font.5ef884f9f9.css'><link rel='stylesheet' href='https://ton.twimg.com/tweetdeck-web/web/css/app-dark.5631e0dd42.css'><style type='text/css'>body{background:#222426}</style>"; private const string DefaultHeadTag = @"<meta charset='utf-8'><meta http-equiv='X-UA-Compatible' content='chrome=1'><link rel='stylesheet' href='https://ton.twimg.com/tweetdeck-web/web/css/font.5ef884f9f9.css'><link rel='stylesheet' href='https://ton.twimg.com/tweetdeck-web/web/css/app-dark.5631e0dd42.css'><style type='text/css'>body{background:#222426}</style>";
private const string CustomCSS = @"body:before{content:none}body{overflow-y:auto}.scroll-styled-v::-webkit-scrollbar{width:7px}.scroll-styled-v::-webkit-scrollbar-thumb{border-radius:0}.scroll-styled-v::-webkit-scrollbar-track{border-left:0}#td-skip{opacity:0;cursor:pointer;transition:opacity 0.15s ease}.td-hover #td-skip{opacity:0.75}#td-skip:hover{opacity:1}"; private const string CustomCSS = @"body:before{content:none}body{overflow-y:auto}.scroll-styled-v::-webkit-scrollbar{width:7px}.scroll-styled-v::-webkit-scrollbar-thumb{border-radius:0}.scroll-styled-v::-webkit-scrollbar-track{border-left:0}#td-skip{opacity:0;cursor:pointer;transition:opacity 0.15s ease}.td-hover #td-skip{opacity:0.75}#td-skip:hover{opacity:1}.media-size-medium{height:calc(100vh - 16px)!important;max-height:240px;border-radius:1px!important}.js-quote-detail .media-size-medium{height:calc(100vh - 28px)!important;}.js-media.margin-vm, .js-media-preview-container.margin-vm{margin-bottom:0!important}";
public static int FontSizeLevel{ public static int FontSizeLevel{
get{ get{
@@ -31,11 +31,11 @@ namespace TweetDuck.Core.Notification{
ExampleTweetHTML = ScriptLoader.LoadResource("pages/example.html", true); ExampleTweetHTML = ScriptLoader.LoadResource("pages/example.html", true);
#if DEBUG #if DEBUG
ExampleTweetHTML = ExampleTweetHTML.Replace("</p>", @"</p><div style='margin-top:64px'>Scrollbar test padding...</div>"); ExampleTweetHTML = ExampleTweetHTML.Replace("</p>", @"</p><div style='margin-top:256px'>Scrollbar test padding...</div>");
#endif #endif
} }
return new TweetNotification("Home", ExampleTweetHTML, 95, string.Empty, string.Empty, true); return new TweetNotification(string.Empty, string.Empty, "Home", ExampleTweetHTML, 95, string.Empty, string.Empty, true);
} }
} }
@@ -51,7 +51,14 @@ namespace TweetDuck.Core.Notification{
TopLeft, TopRight, BottomLeft, BottomRight, Custom TopLeft, TopRight, BottomLeft, BottomRight, Custom
} }
public string Column { get; } public enum Size{
Auto, Custom
}
public string ColumnId { get; }
public string ChirpId { get; }
public string ColumnTitle { get; }
public string TweetUrl { get; } public string TweetUrl { get; }
public string QuoteUrl { get; } public string QuoteUrl { get; }
@@ -59,10 +66,13 @@ namespace TweetDuck.Core.Notification{
private readonly int characters; private readonly int characters;
private readonly bool isExample; private readonly bool isExample;
public TweetNotification(string column, string html, int characters, string tweetUrl, string quoteUrl) : this(column, html, characters, tweetUrl, quoteUrl, false){} public TweetNotification(string columnId, string chirpId, string title, string html, int characters, string tweetUrl, string quoteUrl) : this(columnId, chirpId, title, html, characters, tweetUrl, quoteUrl, false){}
private TweetNotification(string column, string html, int characters, string tweetUrl, string quoteUrl, bool isExample){ private TweetNotification(string columnId, string chirpId, string title, string html, int characters, string tweetUrl, string quoteUrl, bool isExample){
this.Column = column; this.ColumnId = columnId;
this.ChirpId = chirpId;
this.ColumnTitle = title;
this.TweetUrl = tweetUrl; this.TweetUrl = tweetUrl;
this.QuoteUrl = quoteUrl; this.QuoteUrl = quoteUrl;

View File

@@ -11,7 +11,7 @@ namespace TweetDuck.Core.Other{
Text = "About "+Program.BrandName+" "+Program.VersionTag; Text = "About "+Program.BrandName+" "+Program.VersionTag;
labelDescription.Text = Program.BrandName+" was created by chylex as a replacement to the discontinued official TweetDeck client for Windows.\n\nThe program is available for free under the open source MIT license."; labelDescription.Text = "TweetDuck was created by chylex as a replacement to the discontinued official TweetDeck client for Windows.\n\nThe program is available for free under the open source MIT license.";
labelWebsite.Links.Add(new LinkLabel.Link(0, labelWebsite.Text.Length, Program.Website)); labelWebsite.Links.Add(new LinkLabel.Link(0, labelWebsite.Text.Length, Program.Website));
labelTips.Links.Add(new LinkLabel.Link(0, labelTips.Text.Length, TipsLink)); labelTips.Links.Add(new LinkLabel.Link(0, labelTips.Text.Length, TipsLink));

View File

@@ -1,11 +1,66 @@
using System; using System;
using System.Drawing; using System.Drawing;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Controls;
using TweetDuck.Core.Utils;
namespace TweetDuck.Core.Other{ namespace TweetDuck.Core.Other{
[Flags]
enum ControlType{
None = 0,
Accept = 1, // triggered by pressing enter when a non-button is focused
Cancel = 2, // triggered by closing the dialog without pressing a button
Focused = 4 // active control after the dialog is showed
}
sealed partial class FormMessage : Form{ sealed partial class FormMessage : Form{
public const string OK = "OK";
public const string Yes = "Yes";
public const string No = "No";
public const string Cancel = "Cancel";
public const string Retry = "Retry";
public const string Ignore = "Ignore";
public const string Exit = "Exit";
public static bool Information(string caption, string text, string buttonAccept, string buttonCancel = null){
return Show(caption, text, MessageBoxIcon.Information, buttonAccept, buttonCancel);
}
public static bool Warning(string caption, string text, string buttonAccept, string buttonCancel = null){
return Show(caption, text, MessageBoxIcon.Warning, buttonAccept, buttonCancel);
}
public static bool Error(string caption, string text, string buttonAccept, string buttonCancel = null){
return Show(caption, text, MessageBoxIcon.Error, buttonAccept, buttonCancel);
}
public static bool Question(string caption, string text, string buttonAccept, string buttonCancel = null){
return Show(caption, text, MessageBoxIcon.Question, buttonAccept, buttonCancel);
}
public static bool Show(string caption, string text, MessageBoxIcon icon, string button){
return Show(caption, text, icon, button, null);
}
public static bool Show(string caption, string text, MessageBoxIcon icon, string buttonAccept, string buttonCancel){
using(FormMessage message = new FormMessage(caption, text, icon)){
if (buttonCancel == null){
message.AddButton(buttonAccept, DialogResult.OK, ControlType.Cancel | ControlType.Focused);
}
else{
message.AddButton(buttonCancel, DialogResult.Cancel, ControlType.Cancel);
message.AddButton(buttonAccept, DialogResult.OK, ControlType.Accept | ControlType.Focused);
}
return message.ShowDialog() == DialogResult.OK;
}
}
// Instance
public Button ClickedButton { get; private set; } public Button ClickedButton { get; private set; }
public bool HasIcon => icon != null;
public int ActionPanelY => panelActions.Location.Y; public int ActionPanelY => panelActions.Location.Y;
private int ClientWidth{ private int ClientWidth{
@@ -13,8 +68,13 @@ namespace TweetDuck.Core.Other{
set => ClientSize = new Size(value, ClientSize.Height); set => ClientSize = new Size(value, ClientSize.Height);
} }
private int ButtonDistance{
get => BrowserUtils.Scale(96, dpiScale);
}
private readonly Icon icon; private readonly Icon icon;
private readonly bool isReady; private readonly bool isReady;
private readonly float dpiScale;
private int realFormWidth, minFormWidth; private int realFormWidth, minFormWidth;
private int buttonCount; private int buttonCount;
@@ -24,9 +84,11 @@ namespace TweetDuck.Core.Other{
public FormMessage(string caption, string text, MessageBoxIcon messageIcon){ public FormMessage(string caption, string text, MessageBoxIcon messageIcon){
InitializeComponent(); InitializeComponent();
this.dpiScale = this.GetDPIScale();
this.prevLabelWidth = labelMessage.Width; this.prevLabelWidth = labelMessage.Width;
this.prevLabelHeight = labelMessage.Height; this.prevLabelHeight = labelMessage.Height;
this.minFormWidth = 40; this.minFormWidth = BrowserUtils.Scale(42, dpiScale);
switch(messageIcon){ switch(messageIcon){
case MessageBoxIcon.Information: case MessageBoxIcon.Information:
@@ -47,26 +109,30 @@ namespace TweetDuck.Core.Other{
default: default:
icon = null; icon = null;
labelMessage.Location = new Point(labelMessage.Location.X-38, labelMessage.Location.Y); labelMessage.Location = new Point(BrowserUtils.Scale(19, dpiScale), labelMessage.Location.Y); // 19 instead of 9 due to larger height
break; break;
} }
this.isReady = true; this.isReady = true;
this.Text = caption; this.Text = caption;
this.labelMessage.Text = text; this.labelMessage.Text = text.Replace("\r", "").Replace("\n", Environment.NewLine);
} }
private void FormMessage_SizeChanged(object sender, EventArgs e){ private void FormMessage_SizeChanged(object sender, EventArgs e){
RecalculateButtonLocation(); RecalculateButtonLocation();
} }
public Button AddButton(string title, DialogResult result = DialogResult.OK){ public Button AddButton(string title, ControlType type){
return AddButton(title, DialogResult.OK, type);
}
public Button AddButton(string title, DialogResult result = DialogResult.OK, ControlType type = ControlType.None){
Button button = new Button{ Button button = new Button{
Anchor = AnchorStyles.Bottom, Anchor = AnchorStyles.Bottom,
Font = SystemFonts.MessageBoxFont, Font = SystemFonts.MessageBoxFont,
Location = new Point(0, 12), Location = new Point(0, 12),
Size = new Size(88, 26), Size = new Size(BrowserUtils.Scale(88, dpiScale), BrowserUtils.Scale(26, dpiScale)),
TabIndex = buttonCount, TabIndex = buttonCount,
Text = title, Text = title,
UseVisualStyleBackColor = true UseVisualStyleBackColor = true
@@ -81,24 +147,41 @@ namespace TweetDuck.Core.Other{
panelActions.Controls.Add(button); panelActions.Controls.Add(button);
++buttonCount; ++buttonCount;
minFormWidth += 96; minFormWidth += ButtonDistance;
ClientWidth = Math.Max(realFormWidth, minFormWidth); ClientWidth = Math.Max(realFormWidth, minFormWidth);
RecalculateButtonLocation(); RecalculateButtonLocation();
if (type.HasFlag(ControlType.Accept)){
AcceptButton = button;
}
if (type.HasFlag(ControlType.Cancel)){
CancelButton = button;
}
if (type.HasFlag(ControlType.Focused)){
ActiveControl = button;
}
return button; return button;
} }
public void AddActionControl(Control control){ public void AddActionControl(Control control){
panelActions.Controls.Add(control); panelActions.Controls.Add(control);
control.Size = new Size(BrowserUtils.Scale(control.Width, dpiScale), BrowserUtils.Scale(control.Height, dpiScale));
minFormWidth += control.Width+control.Margin.Horizontal; minFormWidth += control.Width+control.Margin.Horizontal;
ClientWidth = Math.Max(realFormWidth, minFormWidth); ClientWidth = Math.Max(realFormWidth, minFormWidth);
} }
private void RecalculateButtonLocation(){ private void RecalculateButtonLocation(){
int dist = ButtonDistance;
int start = ClientWidth-dist;
for(int index = 0; index < buttonCount; index++){ for(int index = 0; index < buttonCount; index++){
Control control = panelActions.Controls[index]; Control control = panelActions.Controls[index];
control.Location = new Point(ClientWidth-97-index*96, control.Location.Y); control.Location = new Point(start-index*dist, control.Location.Y);
} }
} }
@@ -108,17 +191,18 @@ namespace TweetDuck.Core.Other{
} }
bool isMultiline = labelMessage.Height > labelMessage.MinimumSize.Height; bool isMultiline = labelMessage.Height > labelMessage.MinimumSize.Height;
int labelOffset = BrowserUtils.Scale(8, dpiScale);
if (isMultiline && !wasLabelMultiline){ if (isMultiline && !wasLabelMultiline){
labelMessage.Location = new Point(labelMessage.Location.X, labelMessage.Location.Y-8); labelMessage.Location = new Point(labelMessage.Location.X, labelMessage.Location.Y-labelOffset);
prevLabelHeight += 8; prevLabelHeight += labelOffset;
} }
else if (!isMultiline && wasLabelMultiline){ else if (!isMultiline && wasLabelMultiline){
labelMessage.Location = new Point(labelMessage.Location.X, labelMessage.Location.Y+8); labelMessage.Location = new Point(labelMessage.Location.X, labelMessage.Location.Y+labelOffset);
prevLabelHeight -= 8; prevLabelHeight -= labelOffset;
} }
realFormWidth = ClientWidth-(icon == null ? 50 : 0)+labelMessage.Width-prevLabelWidth; realFormWidth = ClientWidth-(icon == null ? BrowserUtils.Scale(50, dpiScale) : 0)+labelMessage.Width-prevLabelWidth;
ClientWidth = Math.Max(realFormWidth, minFormWidth); ClientWidth = Math.Max(realFormWidth, minFormWidth);
Height += labelMessage.Height-prevLabelHeight; Height += labelMessage.Height-prevLabelHeight;
@@ -129,7 +213,7 @@ namespace TweetDuck.Core.Other{
protected override void OnPaint(PaintEventArgs e){ protected override void OnPaint(PaintEventArgs e){
if (icon != null){ if (icon != null){
e.Graphics.DrawIcon(icon, 25, 26); e.Graphics.DrawIcon(icon, BrowserUtils.Scale(25, dpiScale), 1+BrowserUtils.Scale(25, dpiScale));
} }
base.OnPaint(e); base.OnPaint(e);

View File

@@ -26,7 +26,7 @@
this.btnClose = new System.Windows.Forms.Button(); this.btnClose = new System.Windows.Forms.Button();
this.btnReload = new System.Windows.Forms.Button(); this.btnReload = new System.Windows.Forms.Button();
this.btnOpenFolder = new System.Windows.Forms.Button(); this.btnOpenFolder = new System.Windows.Forms.Button();
this.tabPanelPlugins = new TweetDuck.Core.Controls.TabPanel(); this.flowLayoutPlugins = new TweetDuck.Plugins.Controls.PluginListFlowLayout();
this.SuspendLayout(); this.SuspendLayout();
// //
// btnClose // btnClose
@@ -68,22 +68,26 @@
this.btnOpenFolder.UseVisualStyleBackColor = true; this.btnOpenFolder.UseVisualStyleBackColor = true;
this.btnOpenFolder.Click += new System.EventHandler(this.btnOpenFolder_Click); this.btnOpenFolder.Click += new System.EventHandler(this.btnOpenFolder_Click);
// //
// tabPanelPlugins // flowLayoutPlugins
// //
this.tabPanelPlugins.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) this.flowLayoutPlugins.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right))); | System.Windows.Forms.AnchorStyles.Right)));
this.tabPanelPlugins.Location = new System.Drawing.Point(12, 12); this.flowLayoutPlugins.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.tabPanelPlugins.Name = "tabPanelPlugins"; this.flowLayoutPlugins.FlowDirection = System.Windows.Forms.FlowDirection.TopDown;
this.tabPanelPlugins.Size = new System.Drawing.Size(680, 421); this.flowLayoutPlugins.Location = new System.Drawing.Point(12, 12);
this.tabPanelPlugins.TabIndex = 0; this.flowLayoutPlugins.Name = "flowLayoutPlugins";
this.flowLayoutPlugins.Size = new System.Drawing.Size(680, 421);
this.flowLayoutPlugins.TabIndex = 0;
this.flowLayoutPlugins.WrapContents = false;
this.flowLayoutPlugins.Resize += new System.EventHandler(this.flowLayoutPlugins_Resize);
// //
// FormPlugins // FormPlugins
// //
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(704, 474); this.ClientSize = new System.Drawing.Size(704, 474);
this.Controls.Add(this.tabPanelPlugins); this.Controls.Add(this.flowLayoutPlugins);
this.Controls.Add(this.btnOpenFolder); this.Controls.Add(this.btnOpenFolder);
this.Controls.Add(this.btnReload); this.Controls.Add(this.btnReload);
this.Controls.Add(this.btnClose); this.Controls.Add(this.btnClose);
@@ -101,6 +105,6 @@
private System.Windows.Forms.Button btnClose; private System.Windows.Forms.Button btnClose;
private System.Windows.Forms.Button btnReload; private System.Windows.Forms.Button btnReload;
private System.Windows.Forms.Button btnOpenFolder; private System.Windows.Forms.Button btnOpenFolder;
private TweetDuck.Core.Controls.TabPanel tabPanelPlugins; private Plugins.Controls.PluginListFlowLayout flowLayoutPlugins;
} }
} }

View File

@@ -3,19 +3,12 @@ using System.Diagnostics;
using System.Drawing; using System.Drawing;
using System.Linq; using System.Linq;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Controls;
using TweetDuck.Plugins; using TweetDuck.Plugins;
using TweetDuck.Plugins.Controls; using TweetDuck.Plugins.Controls;
using TweetDuck.Plugins.Enums;
using TweetDuck.Plugins.Events;
namespace TweetDuck.Core.Other{ namespace TweetDuck.Core.Other{
sealed partial class FormPlugins : Form{ sealed partial class FormPlugins : Form{
private readonly PluginManager pluginManager; private readonly PluginManager pluginManager;
private readonly TabButton tabBtnOfficial, tabBtnCustom;
private readonly PluginListFlowLayout flowLayoutPlugins;
private PluginGroup? selectedGroup;
public FormPlugins(){ public FormPlugins(){
InitializeComponent(); InitializeComponent();
@@ -25,66 +18,41 @@ namespace TweetDuck.Core.Other{
public FormPlugins(PluginManager pluginManager) : this(){ public FormPlugins(PluginManager pluginManager) : this(){
this.pluginManager = pluginManager; this.pluginManager = pluginManager;
this.pluginManager.Reloaded += pluginManager_Reloaded;
this.flowLayoutPlugins = new PluginListFlowLayout();
this.flowLayoutPlugins.Resize += flowLayoutPlugins_Resize;
this.tabPanelPlugins.SetupTabPanel(90);
this.tabPanelPlugins.ReplaceContent(flowLayoutPlugins);
this.tabBtnOfficial = tabPanelPlugins.AddButton("", () => SelectGroup(PluginGroup.Official));
this.tabBtnCustom = tabPanelPlugins.AddButton("", () => SelectGroup(PluginGroup.Custom));
this.pluginManager_Reloaded(pluginManager, null);
Shown += (sender, args) => { Shown += (sender, args) => {
Program.UserConfig.PluginsWindow.Restore(this, false); Program.UserConfig.PluginsWindow.Restore(this, false);
this.tabPanelPlugins.SelectTab(tabBtnOfficial); ReloadPluginList();
}; };
FormClosed += (sender, args) => { FormClosed += (sender, args) => {
Program.UserConfig.PluginsWindow.Save(this); Program.UserConfig.PluginsWindow.Save(this);
Program.UserConfig.Save(); Program.UserConfig.Save();
}; };
Disposed += (sender, args) => this.pluginManager.Reloaded -= pluginManager_Reloaded;
} }
private void SelectGroup(PluginGroup group){ private int GetPluginOrderIndex(Plugin plugin){
if (selectedGroup.HasValue && selectedGroup == group)return; return !plugin.CanRun ? 0 : pluginManager.Config.IsEnabled(plugin) ? 1 : 2;
selectedGroup = group;
ReloadPluginTab();
} }
public void ReloadPluginTab(){ private void ReloadPluginList(){
if (!selectedGroup.HasValue)return;
flowLayoutPlugins.SuspendLayout(); flowLayoutPlugins.SuspendLayout();
flowLayoutPlugins.Controls.Clear(); flowLayoutPlugins.Controls.Clear();
Plugin[] plugins = pluginManager.GetPluginsByGroup(selectedGroup.Value).OrderBy(plugin => !plugin.CanRun ? 0 : pluginManager.Config.IsEnabled(plugin) ? 1 : 2).ThenBy(plugin => plugin.Name).ToArray(); foreach(Plugin plugin in pluginManager.Plugins.OrderBy(GetPluginOrderIndex).ThenBy(plugin => plugin.Name)){
flowLayoutPlugins.Controls.Add(new PluginControl(pluginManager, plugin));
for(int index = 0; index < plugins.Length; index++){
flowLayoutPlugins.Controls.Add(new PluginControl(pluginManager, plugins[index]));
if (index < plugins.Length-1){
flowLayoutPlugins.Controls.Add(new Panel{ flowLayoutPlugins.Controls.Add(new Panel{
BackColor = Color.DimGray, BackColor = Color.DimGray,
Margin = new Padding(0),
Size = new Size(1, 1) Size = new Size(1, 1)
}); });
} }
}
flowLayoutPlugins.ResumeLayout(true); flowLayoutPlugins.ResumeLayout(true);
flowLayoutPlugins_Resize(flowLayoutPlugins, new EventArgs());
}
private void pluginManager_Reloaded(object sender, PluginErrorEventArgs e){ // sorry, I guess...
tabBtnOfficial.Text = "Official: "+pluginManager.CountPluginByGroup(PluginGroup.Official); Padding = new Padding(Padding.Left, Padding.Top, Padding.Right+1, Padding.Bottom);
tabBtnCustom.Text = "Custom: "+pluginManager.CountPluginByGroup(PluginGroup.Custom); Padding = new Padding(Padding.Left, Padding.Top, Padding.Right-1, Padding.Bottom);
} }
private void flowLayoutPlugins_Resize(object sender, EventArgs e){ private void flowLayoutPlugins_Resize(object sender, EventArgs e){
@@ -103,6 +71,7 @@ namespace TweetDuck.Core.Other{
control.Width = flowLayoutPlugins.Width-control.Margin.Horizontal-horizontalOffset; control.Width = flowLayoutPlugins.Width-control.Margin.Horizontal-horizontalOffset;
} }
lastControl.Visible = !showScrollBar;
flowLayoutPlugins.Focus(); flowLayoutPlugins.Focus();
} }
@@ -111,9 +80,9 @@ namespace TweetDuck.Core.Other{
} }
private void btnReload_Click(object sender, EventArgs e){ private void btnReload_Click(object sender, EventArgs e){
if (MessageBox.Show("This will also reload the browser window. Do you want to proceed?", "Reloading Plugins", MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) == DialogResult.Yes){ if (FormMessage.Warning("Reloading Plugins", "This will also reload the browser window. Do you want to proceed?", FormMessage.Yes, FormMessage.No)){
pluginManager.Reload(); pluginManager.Reload();
ReloadPluginTab(); ReloadPluginList();
} }
} }

View File

@@ -24,51 +24,70 @@
/// </summary> /// </summary>
private void InitializeComponent() { private void InitializeComponent() {
this.btnClose = new System.Windows.Forms.Button(); this.btnClose = new System.Windows.Forms.Button();
this.labelTip = new System.Windows.Forms.Label(); this.panelContents = new System.Windows.Forms.Panel();
this.tabPanel = new TweetDuck.Core.Controls.TabPanel(); this.panelButtons = new System.Windows.Forms.Panel();
this.btnManageOptions = new System.Windows.Forms.Button();
this.SuspendLayout(); this.SuspendLayout();
// //
// btnClose // btnClose
// //
this.btnClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.btnClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnClose.AutoSize = true; this.btnClose.AutoSize = true;
this.btnClose.Location = new System.Drawing.Point(443, 349); this.btnClose.Location = new System.Drawing.Point(449, 447);
this.btnClose.Name = "btnClose"; this.btnClose.Name = "btnClose";
this.btnClose.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0); this.btnClose.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
this.btnClose.Size = new System.Drawing.Size(49, 23); this.btnClose.Size = new System.Drawing.Size(49, 23);
this.btnClose.TabIndex = 4; this.btnClose.TabIndex = 3;
this.btnClose.Text = "Close"; this.btnClose.Text = "Close";
this.btnClose.UseVisualStyleBackColor = true; this.btnClose.UseVisualStyleBackColor = true;
this.btnClose.Click += new System.EventHandler(this.btnClose_Click); this.btnClose.Click += new System.EventHandler(this.btnClose_Click);
// //
// labelTip // panelContents
// //
this.labelTip.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.panelContents.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
this.labelTip.AutoSize = true;
this.labelTip.Location = new System.Drawing.Point(12, 351);
this.labelTip.Name = "labelTip";
this.labelTip.Size = new System.Drawing.Size(310, 13);
this.labelTip.TabIndex = 5;
this.labelTip.Text = "Tip: Move your cursor over an option to see detailed explanation";
//
// tabPanel
//
this.tabPanel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right))); | System.Windows.Forms.AnchorStyles.Right)));
this.tabPanel.Location = new System.Drawing.Point(12, 12); this.panelContents.AutoScroll = true;
this.tabPanel.Name = "tabPanel"; this.panelContents.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.tabPanel.Size = new System.Drawing.Size(480, 331); this.panelContents.Location = new System.Drawing.Point(135, 12);
this.tabPanel.TabIndex = 3; this.panelContents.Margin = new System.Windows.Forms.Padding(0, 3, 3, 3);
this.panelContents.Name = "panelContents";
this.panelContents.Size = new System.Drawing.Size(363, 429);
this.panelContents.TabIndex = 1;
//
// panelButtons
//
this.panelButtons.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)));
this.panelButtons.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.panelButtons.Location = new System.Drawing.Point(12, 12);
this.panelButtons.Margin = new System.Windows.Forms.Padding(3, 3, 0, 3);
this.panelButtons.Name = "panelButtons";
this.panelButtons.Size = new System.Drawing.Size(124, 429);
this.panelButtons.TabIndex = 0;
//
// btnManageOptions
//
this.btnManageOptions.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.btnManageOptions.AutoSize = true;
this.btnManageOptions.Location = new System.Drawing.Point(12, 447);
this.btnManageOptions.Name = "btnManageOptions";
this.btnManageOptions.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
this.btnManageOptions.Size = new System.Drawing.Size(101, 23);
this.btnManageOptions.TabIndex = 4;
this.btnManageOptions.Text = "Manage Options";
this.btnManageOptions.UseVisualStyleBackColor = true;
this.btnManageOptions.Click += new System.EventHandler(this.btnManageOptions_Click);
// //
// FormSettings // FormSettings
// //
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(504, 384); this.ClientSize = new System.Drawing.Size(510, 482);
this.Controls.Add(this.labelTip); this.Controls.Add(this.btnManageOptions);
this.Controls.Add(this.panelContents);
this.Controls.Add(this.panelButtons);
this.Controls.Add(this.btnClose); this.Controls.Add(this.btnClose);
this.Controls.Add(this.tabPanel);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.Icon = global::TweetDuck.Properties.Resources.icon; this.Icon = global::TweetDuck.Properties.Resources.icon;
this.MaximizeBox = false; this.MaximizeBox = false;
@@ -82,9 +101,9 @@
} }
#endregion #endregion
private Controls.TabPanel tabPanel;
private System.Windows.Forms.Button btnClose; private System.Windows.Forms.Button btnClose;
private System.Windows.Forms.Label labelTip; private System.Windows.Forms.Panel panelContents;
private System.Windows.Forms.Panel panelButtons;
private System.Windows.Forms.Button btnManageOptions;
} }
} }

View File

@@ -1,68 +1,175 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Drawing;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Controls;
using TweetDuck.Core.Other.Settings; using TweetDuck.Core.Other.Settings;
using TweetDuck.Core.Other.Settings.Dialogs;
using TweetDuck.Core.Utils;
using TweetDuck.Plugins; using TweetDuck.Plugins;
using TweetDuck.Updates; using TweetDuck.Updates;
namespace TweetDuck.Core.Other{ namespace TweetDuck.Core.Other{
sealed partial class FormSettings : Form{ sealed partial class FormSettings : Form{
public const int TabIndexNotification = 1;
private readonly FormBrowser browser; private readonly FormBrowser browser;
private readonly Dictionary<Type, BaseTabSettings> tabs = new Dictionary<Type, BaseTabSettings>(4); private readonly PluginManager plugins;
public FormSettings(FormBrowser browser, PluginManager plugins, UpdateHandler updates, int startTabIndex = 0){ private readonly int buttonHeight;
private readonly Dictionary<Type, SettingsTab> tabs = new Dictionary<Type, SettingsTab>(4);
private SettingsTab currentTab;
public bool ShouldReloadBrowser { get; private set; }
public FormSettings(FormBrowser browser, PluginManager plugins, UpdateHandler updates, Type startTab){
InitializeComponent(); InitializeComponent();
Text = Program.BrandName+" Settings"; Text = Program.BrandName+" Options";
this.browser = browser; this.browser = browser;
this.browser.PauseNotification(); this.browser.PauseNotification();
this.tabPanel.SetupTabPanel(100); this.plugins = plugins;
this.tabPanel.AddButton("General", () => SelectTab(() => new TabSettingsGeneral(updates)));
this.tabPanel.AddButton("Notifications", () => SelectTab(() => new TabSettingsNotifications(browser.CreateNotificationForm(false))));
this.tabPanel.AddButton("Sounds", () => SelectTab(() => new TabSettingsSounds()));
this.tabPanel.AddButton("Advanced", () => SelectTab(() => new TabSettingsAdvanced(browser.ReinjectCustomCSS, plugins)));
this.tabPanel.SelectTab(tabPanel.Buttons.ElementAt(startTabIndex)); this.buttonHeight = BrowserUtils.Scale(39, this.GetDPIScale()) | 1;
}
private void SelectTab<T>(Func<T> constructor) where T : BaseTabSettings{ AddButton("General", () => new TabSettingsGeneral(updates));
if (tabs.TryGetValue(typeof(T), out BaseTabSettings control)){ AddButton("System Tray", () => new TabSettingsTray());
tabPanel.ReplaceContent(control); AddButton("Notifications", () => new TabSettingsNotifications(browser.CreateNotificationForm(false)));
} AddButton("Sounds", () => new TabSettingsSounds());
else{ AddButton("Advanced", () => new TabSettingsAdvanced(browser.ReinjectCustomCSS));
control = tabs[typeof(T)] = constructor();
control.OnReady(); SelectTab(tabs[startTab ?? typeof(TabSettingsGeneral)]);
tabPanel.ReplaceContent(control);
}
} }
private void FormSettings_FormClosing(object sender, FormClosingEventArgs e){ private void FormSettings_FormClosing(object sender, FormClosingEventArgs e){
foreach(BaseTabSettings control in tabs.Values){ foreach(SettingsTab tab in tabs.Values){
control.OnClosing(); if (tab.IsInitialized){
tab.Control.OnClosing();
tab.Control.Dispose();
}
} }
Program.UserConfig.Save(); Program.UserConfig.Save();
browser.ResumeNotification();
foreach(BaseTabSettings control in tabs.Values){
control.Dispose();
} }
private void btnManageOptions_Click(object sender, EventArgs e){
foreach(SettingsTab tab in tabs.Values){
if (tab.IsInitialized){
tab.Control.OnClosing();
}
}
using(DialogSettingsManage dialog = new DialogSettingsManage(plugins)){
if (dialog.ShowDialog() == DialogResult.OK){
FormClosing -= FormSettings_FormClosing;
browser.ResumeNotification(); browser.ResumeNotification();
ShouldReloadBrowser = dialog.ShouldReloadBrowser;
Close();
}
}
} }
private void btnClose_Click(object sender, EventArgs e){ private void btnClose_Click(object sender, EventArgs e){
Close(); Close();
} }
public void ReloadUI(){ private void AddButton<T>(string title, Func<T> constructor) where T : BaseTabSettings{
tabs.Clear(); FlatButton btn = new FlatButton{
tabPanel.Content.Controls.Clear(); BackColor = SystemColors.Control,
tabPanel.ActiveButton.Callback(); FlatStyle = FlatStyle.Flat,
Location = new Point(0, (buttonHeight+1)*(panelButtons.Controls.Count/2)),
Margin = new Padding(0),
Size = new Size(panelButtons.Width, buttonHeight),
Text = title,
UseVisualStyleBackColor = true
};
btn.FlatAppearance.BorderSize = 0;
btn.FlatAppearance.MouseDownBackColor = Color.FromArgb(179, 213, 232);
btn.FlatAppearance.MouseOverBackColor = Color.FromArgb(216, 230, 237);
panelButtons.Controls.Add(btn);
panelButtons.Controls.Add(new Panel{
BackColor = Color.DimGray,
Location = new Point(0, panelButtons.Controls[panelButtons.Controls.Count-1].Location.Y+buttonHeight),
Margin = new Padding(0),
Size = new Size(panelButtons.Width, 1)
});
tabs.Add(typeof(T), new SettingsTab(btn, constructor));
btn.Click += (sender, args) => SelectTab<T>();
}
private void SelectTab<T>() where T : BaseTabSettings{
SelectTab(tabs[typeof(T)]);
}
private void SelectTab(SettingsTab tab){
if (currentTab != null){
currentTab.Button.BackColor = SystemColors.Control;
}
tab.Button.BackColor = tab.Button.FlatAppearance.MouseDownBackColor;
if (!tab.IsInitialized){
foreach(Control control in tab.Control.InteractiveControls){
if (control is ComboBox){
control.MouseLeave += control_MouseLeave;
}
else if (control is TrackBar){
control.MouseWheel += control_MouseWheel;
}
}
tab.Control.OnReady();
}
panelContents.VerticalScroll.Enabled = false; // required to stop animation that would otherwise break everything
panelContents.PerformLayout();
panelContents.SuspendLayout();
panelContents.VerticalScroll.Value = 0; // https://gfycat.com/GrotesqueTastyAstarte
panelContents.Controls.Clear();
panelContents.Controls.Add(tab.Control);
panelContents.ResumeLayout(true);
panelContents.VerticalScroll.Enabled = true;
panelContents.Focus();
currentTab = tab;
}
private void control_MouseLeave(object sender, EventArgs e){
panelContents.Focus();
}
private void control_MouseWheel(object sender, MouseEventArgs e){
((HandledMouseEventArgs)e).Handled = true;
panelContents.Focus();
}
private sealed class SettingsTab{
public Button Button { get; }
public BaseTabSettings Control{
get => control ?? (control = constructor());
set => control = value;
}
public bool IsInitialized => control != null;
private readonly Func<BaseTabSettings> constructor;
private BaseTabSettings control;
public SettingsTab(Button button, Func<BaseTabSettings> constructor){
this.Button = button;
this.constructor = constructor;
}
} }
} }
} }

View File

@@ -1,120 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -0,0 +1,27 @@
using System.Collections.Generic;
using System.Diagnostics;
using CefSharp;
using TweetDuck.Core.Utils;
namespace TweetDuck.Core.Other.Management{
static class BrowserProcesses{
private static readonly Dictionary<int, int> PIDs = new Dictionary<int, int>();
public static void Link(int identifier, int pid){
PIDs[identifier] = pid;
}
public static void Forget(int identifier){
PIDs.Remove(identifier);
}
public static Process FindProcess(IBrowser browser){
if (PIDs.TryGetValue(browser.Identifier, out int pid) && WindowsUtils.IsChildProcess(pid)){ // child process is checked in two places for safety
return Process.GetProcessById(pid);
}
else{
return null;
}
}
}
}

View File

@@ -0,0 +1,91 @@
using System;
using System.Diagnostics;
using System.Timers;
using System.Windows.Forms;
using CefSharp;
using Timer = System.Timers.Timer;
namespace TweetDuck.Core.Other.Management{
sealed class MemoryUsageTracker : IDisposable{
private const int IntervalMemoryCheck = 60000*30; // 30 minutes
private const int IntervalCleanupAttempt = 60000*5; // 5 minutes
private readonly string script;
private readonly Timer timer;
private Form owner;
private IBrowser browser;
private long threshold;
private bool needsCleanup;
public MemoryUsageTracker(string cleanupFunctionName){
this.script = $"window.{cleanupFunctionName} && window.{cleanupFunctionName}()";
this.timer = new Timer{ Interval = IntervalMemoryCheck };
this.timer.Elapsed += timer_Elapsed;
}
public void Start(Form owner, IBrowser browser, int thresholdMB){
Stop();
this.owner = owner;
this.browser = browser;
this.threshold = thresholdMB*1024L*1024L;
this.timer.SynchronizingObject = owner;
this.timer.Start();
}
public void Stop(){
timer.Stop();
timer.SynchronizingObject = null;
owner = null;
browser = null;
SetNeedsCleanup(false);
}
public void Dispose(){
timer.SynchronizingObject = null;
timer.Dispose();
owner = null;
browser = null;
}
private void SetNeedsCleanup(bool value){
if (needsCleanup != value){
needsCleanup = value;
timer.Interval = value ? IntervalCleanupAttempt : IntervalMemoryCheck; // restarts timer
}
}
private void timer_Elapsed(object sender, ElapsedEventArgs e){
if (owner == null || browser == null){
return;
}
if (needsCleanup){
if (!owner.ContainsFocus){
using(IFrame frame = browser.MainFrame){
frame.EvaluateScriptAsync(script).ContinueWith(task => {
JavascriptResponse response = task.Result;
if (response.Success && (response.Result as bool? ?? false)){
SetNeedsCleanup(false);
}
});
}
}
}
else{
try{
using(Process process = BrowserProcesses.FindProcess(browser)){
if (process?.PrivateMemorySize64 > threshold){
SetNeedsCleanup(true);
}
}
}catch{
// ignore I guess?
}
}
}
}
}

View File

@@ -0,0 +1,179 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
using TweetDuck.Core.Controls;
using TweetDuck.Core.Utils;
using TweetLib.Communication;
namespace TweetDuck.Core.Other.Management{
sealed class VideoPlayer : IDisposable{
private readonly string PlayerExe = Path.Combine(Program.ProgramPath, "TweetDuck.Video.exe");
public bool Running{
get{
if (currentProcess == null){
return false;
}
currentProcess.Refresh();
return !currentProcess.HasExited;
}
}
public event EventHandler ProcessExited;
private readonly Form owner;
private string lastUrl;
private Process currentProcess;
private DuplexPipe.Server currentPipe;
private bool isClosing;
public VideoPlayer(Form owner){
this.owner = owner;
this.owner.FormClosing += owner_FormClosing;
}
public void Launch(string url){
if (Running){
Destroy();
isClosing = false;
}
lastUrl = url;
try{
currentPipe = DuplexPipe.CreateServer();
currentPipe.DataIn += currentPipe_DataIn;
if ((currentProcess = Process.Start(new ProcessStartInfo{
FileName = PlayerExe,
Arguments = $"{owner.Handle} {Program.UserConfig.VideoPlayerVolume} \"{url}\" \"{currentPipe.GenerateToken()}\"",
UseShellExecute = false,
RedirectStandardOutput = true
})) != null){
currentProcess.EnableRaisingEvents = true;
currentProcess.Exited += process_Exited;
#if DEBUG
currentProcess.BeginOutputReadLine();
currentProcess.OutputDataReceived += (sender, args) => Debug.WriteLine("VideoPlayer: "+args.Data);
#endif
}
currentPipe.DisposeToken();
}catch(Exception e){
Program.Reporter.HandleException("Video Playback Error", "Error launching video player.", true, e);
}
}
public void SendKeyEvent(Keys key){
currentPipe?.Write("key", ((int)key).ToString());
}
private void currentPipe_DataIn(object sender, DuplexPipe.PipeReadEventArgs e){
owner.InvokeSafe(() => {
switch(e.Key){
case "vol":
if (int.TryParse(e.Data, out int volume) && volume != Program.UserConfig.VideoPlayerVolume){
Program.UserConfig.VideoPlayerVolume = volume;
Program.UserConfig.Save();
}
break;
case "download":
TwitterUtils.DownloadVideo(lastUrl);
break;
case "rip":
currentPipe.Dispose();
currentPipe = null;
currentProcess.Dispose();
currentProcess = null;
isClosing = false;
TriggerProcessExitEventUnsafe();
break;
}
});
}
public void Close(){
if (currentProcess != null){
if (isClosing){
Destroy();
isClosing = false;
}
else{
isClosing = true;
currentProcess.Exited -= process_Exited;
currentPipe.Write("die");
}
}
}
public void Dispose(){
ProcessExited = null;
isClosing = true;
Destroy();
}
private void Destroy(){
if (currentProcess != null){
try{
currentProcess.Kill();
}catch{
// kill me instead then
}
currentProcess.Dispose();
currentProcess = null;
currentPipe.Dispose();
currentPipe = null;
TriggerProcessExitEventUnsafe();
}
}
private void owner_FormClosing(object sender, FormClosingEventArgs e){
if (currentProcess != null){
currentProcess.Exited -= process_Exited;
}
}
private void process_Exited(object sender, EventArgs e){
switch(currentProcess.ExitCode){
case 3: // CODE_LAUNCH_FAIL
if (FormMessage.Error("Video Playback Error", "Error launching video player, this may be caused by missing Windows Media Player. Do you want to open the video in a browser?", FormMessage.Yes, FormMessage.No)){
BrowserUtils.OpenExternalBrowser(lastUrl);
}
break;
case 4: // CODE_MEDIA_ERROR
if (FormMessage.Error("Video Playback Error", "The video could not be loaded, most likely due to unknown format. Do you want to open the video in a browser?", FormMessage.Yes, FormMessage.No)){
BrowserUtils.OpenExternalBrowser(lastUrl);
}
break;
}
currentProcess.Dispose();
currentProcess = null;
currentPipe.Dispose();
currentPipe = null;
owner.InvokeAsyncSafe(TriggerProcessExitEventUnsafe);
}
private void TriggerProcessExitEventUnsafe(){
ProcessExited?.Invoke(this, EventArgs.Empty);
}
}
}

View File

@@ -1,11 +1,23 @@
using System.Windows.Forms; using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using TweetDuck.Configuration; using TweetDuck.Configuration;
namespace TweetDuck.Core.Other.Settings{ namespace TweetDuck.Core.Other.Settings{
class BaseTabSettings : UserControl{ class BaseTabSettings : UserControl{
protected static UserConfig Config => Program.UserConfig; protected static UserConfig Config => Program.UserConfig;
public BaseTabSettings(){ public IEnumerable<Control> InteractiveControls{
get{
foreach(Panel panel in Controls.OfType<Panel>()){
foreach(Control control in panel.Controls){
yield return control;
}
}
}
}
protected BaseTabSettings(){
Padding = new Padding(6); Padding = new Padding(6);
} }
@@ -13,7 +25,7 @@ namespace TweetDuck.Core.Other.Settings{
public virtual void OnClosing(){} public virtual void OnClosing(){}
protected static void PromptRestart(){ protected static void PromptRestart(){
if (MessageBox.Show("The application must restart for the setting to take place. Do you want to restart now?", Program.BrandName+" Settings", MessageBoxButtons.YesNo, MessageBoxIcon.Information) == DialogResult.Yes){ if (FormMessage.Information("TweetDuck Options", "The application must restart for the option to take place. Do you want to restart now?", FormMessage.Yes, FormMessage.No)){
Program.Restart(); Program.Restart();
} }
} }

View File

@@ -42,16 +42,14 @@
// //
// textBoxBrowserCSS // textBoxBrowserCSS
// //
this.textBoxBrowserCSS.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) this.textBoxBrowserCSS.Dock = System.Windows.Forms.DockStyle.Bottom;
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.textBoxBrowserCSS.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); this.textBoxBrowserCSS.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.textBoxBrowserCSS.Location = new System.Drawing.Point(0, 16); this.textBoxBrowserCSS.Location = new System.Drawing.Point(0, 16);
this.textBoxBrowserCSS.Margin = new System.Windows.Forms.Padding(0, 3, 0, 0); this.textBoxBrowserCSS.Margin = new System.Windows.Forms.Padding(0, 3, 0, 0);
this.textBoxBrowserCSS.Multiline = true; this.textBoxBrowserCSS.Multiline = true;
this.textBoxBrowserCSS.Name = "textBoxBrowserCSS"; this.textBoxBrowserCSS.Name = "textBoxBrowserCSS";
this.textBoxBrowserCSS.ScrollBars = System.Windows.Forms.ScrollBars.Both; this.textBoxBrowserCSS.ScrollBars = System.Windows.Forms.ScrollBars.Both;
this.textBoxBrowserCSS.Size = new System.Drawing.Size(373, 253); this.textBoxBrowserCSS.Size = new System.Drawing.Size(378, 253);
this.textBoxBrowserCSS.TabIndex = 1; this.textBoxBrowserCSS.TabIndex = 1;
this.textBoxBrowserCSS.WordWrap = false; this.textBoxBrowserCSS.WordWrap = false;
this.textBoxBrowserCSS.KeyUp += new System.Windows.Forms.KeyEventHandler(this.textBoxBrowserCSS_KeyUp); this.textBoxBrowserCSS.KeyUp += new System.Windows.Forms.KeyEventHandler(this.textBoxBrowserCSS_KeyUp);
@@ -100,7 +98,7 @@
this.splitContainer.Panel2.Controls.Add(this.textBoxNotificationCSS); this.splitContainer.Panel2.Controls.Add(this.textBoxNotificationCSS);
this.splitContainer.Panel2MinSize = 64; this.splitContainer.Panel2MinSize = 64;
this.splitContainer.Size = new System.Drawing.Size(760, 269); this.splitContainer.Size = new System.Drawing.Size(760, 269);
this.splitContainer.SplitterDistance = 373; this.splitContainer.SplitterDistance = 378;
this.splitContainer.SplitterWidth = 5; this.splitContainer.SplitterWidth = 5;
this.splitContainer.TabIndex = 0; this.splitContainer.TabIndex = 0;
// //
@@ -126,16 +124,14 @@
// //
// textBoxNotificationCSS // textBoxNotificationCSS
// //
this.textBoxNotificationCSS.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) this.textBoxNotificationCSS.Dock = System.Windows.Forms.DockStyle.Bottom;
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.textBoxNotificationCSS.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); this.textBoxNotificationCSS.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.textBoxNotificationCSS.Location = new System.Drawing.Point(0, 16); this.textBoxNotificationCSS.Location = new System.Drawing.Point(0, 16);
this.textBoxNotificationCSS.Margin = new System.Windows.Forms.Padding(0, 3, 0, 0); this.textBoxNotificationCSS.Margin = new System.Windows.Forms.Padding(0, 3, 0, 0);
this.textBoxNotificationCSS.Multiline = true; this.textBoxNotificationCSS.Multiline = true;
this.textBoxNotificationCSS.Name = "textBoxNotificationCSS"; this.textBoxNotificationCSS.Name = "textBoxNotificationCSS";
this.textBoxNotificationCSS.ScrollBars = System.Windows.Forms.ScrollBars.Both; this.textBoxNotificationCSS.ScrollBars = System.Windows.Forms.ScrollBars.Both;
this.textBoxNotificationCSS.Size = new System.Drawing.Size(372, 253); this.textBoxNotificationCSS.Size = new System.Drawing.Size(377, 253);
this.textBoxNotificationCSS.TabIndex = 1; this.textBoxNotificationCSS.TabIndex = 1;
this.textBoxNotificationCSS.WordWrap = false; this.textBoxNotificationCSS.WordWrap = false;
// //
@@ -152,7 +148,6 @@
// btnOpenWiki // btnOpenWiki
// //
this.btnOpenWiki.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.btnOpenWiki.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.btnOpenWiki.AutoSize = true;
this.btnOpenWiki.Location = new System.Drawing.Point(12, 287); this.btnOpenWiki.Location = new System.Drawing.Point(12, 287);
this.btnOpenWiki.Name = "btnOpenWiki"; this.btnOpenWiki.Name = "btnOpenWiki";
this.btnOpenWiki.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0); this.btnOpenWiki.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);

View File

@@ -13,7 +13,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
public DialogSettingsCSS(Action<string> reinjectBrowserCSS){ public DialogSettingsCSS(Action<string> reinjectBrowserCSS){
InitializeComponent(); InitializeComponent();
Text = Program.BrandName+" Settings - CSS"; Text = Program.BrandName+" Options - CSS";
this.reinjectBrowserCSS = reinjectBrowserCSS; this.reinjectBrowserCSS = reinjectBrowserCSS;

View File

@@ -69,7 +69,6 @@
// btnHelp // btnHelp
// //
this.btnHelp.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.btnHelp.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.btnHelp.AutoSize = true;
this.btnHelp.Location = new System.Drawing.Point(12, 227); this.btnHelp.Location = new System.Drawing.Point(12, 227);
this.btnHelp.Name = "btnHelp"; this.btnHelp.Name = "btnHelp";
this.btnHelp.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0); this.btnHelp.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);

View File

@@ -2,6 +2,7 @@
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetDuck.Data;
namespace TweetDuck.Core.Other.Settings.Dialogs{ namespace TweetDuck.Core.Other.Settings.Dialogs{
sealed partial class DialogSettingsCefArgs : Form{ sealed partial class DialogSettingsCefArgs : Form{
@@ -10,7 +11,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
public DialogSettingsCefArgs(){ public DialogSettingsCefArgs(){
InitializeComponent(); InitializeComponent();
Text = Program.BrandName+" Settings - CEF Arguments"; Text = Program.BrandName+" Options - CEF Arguments";
textBoxArgs.EnableMultilineShortcuts(); textBoxArgs.EnableMultilineShortcuts();
textBoxArgs.Text = Program.UserConfig.CustomCefArgs ?? ""; textBoxArgs.Text = Program.UserConfig.CustomCefArgs ?? "";
@@ -30,10 +31,10 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
return; return;
} }
int count = CommandLineArgsParser.ReadCefArguments(CefArgs).Count; int count = CommandLineArgs.ReadCefArguments(CefArgs).Count;
string prompt = count == 0 && !string.IsNullOrWhiteSpace(prevArgs) ? "All arguments will be removed from the settings. Continue?" : count+(count == 1 ? " argument" : " arguments")+" will be added to the settings. Continue?"; string prompt = count == 0 && !string.IsNullOrWhiteSpace(prevArgs) ? "All current arguments will be removed. Continue?" : count+(count == 1 ? " argument was" : " arguments were")+" detected. Continue?";
if (MessageBox.Show(prompt, "Confirm CEF Arguments", MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == DialogResult.OK){ if (FormMessage.Question("Confirm CEF Arguments", prompt, FormMessage.OK, FormMessage.Cancel)){
DialogResult = DialogResult.OK; DialogResult = DialogResult.OK;
Close(); Close();
} }

View File

@@ -1,130 +0,0 @@
namespace TweetDuck.Core.Other.Settings.Dialogs {
partial class DialogSettingsExport {
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.components = new System.ComponentModel.Container();
this.btnCancel = new System.Windows.Forms.Button();
this.btnApply = new System.Windows.Forms.Button();
this.cbConfig = new System.Windows.Forms.CheckBox();
this.cbSession = new System.Windows.Forms.CheckBox();
this.cbPluginData = new System.Windows.Forms.CheckBox();
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
this.SuspendLayout();
//
// btnCancel
//
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnCancel.AutoSize = true;
this.btnCancel.Location = new System.Drawing.Point(176, 97);
this.btnCancel.Name = "btnCancel";
this.btnCancel.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
this.btnCancel.Size = new System.Drawing.Size(56, 23);
this.btnCancel.TabIndex = 4;
this.btnCancel.Text = "Cancel";
this.btnCancel.UseVisualStyleBackColor = true;
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
//
// btnApply
//
this.btnApply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnApply.AutoSize = true;
this.btnApply.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.btnApply.Location = new System.Drawing.Point(144, 97);
this.btnApply.Name = "btnApply";
this.btnApply.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
this.btnApply.Size = new System.Drawing.Size(26, 23);
this.btnApply.TabIndex = 3;
this.btnApply.Text = " ";
this.btnApply.UseVisualStyleBackColor = true;
this.btnApply.Click += new System.EventHandler(this.btnApply_Click);
//
// cbConfig
//
this.cbConfig.AutoSize = true;
this.cbConfig.Location = new System.Drawing.Point(13, 13);
this.cbConfig.Name = "cbConfig";
this.cbConfig.Size = new System.Drawing.Size(106, 17);
this.cbConfig.TabIndex = 0;
this.cbConfig.Text = "Program Settings";
this.toolTip.SetToolTip(this.cbConfig, "Interface, notification, and update settings.\r\nIncludes a list of disabled plugin" +
"s.");
this.cbConfig.UseVisualStyleBackColor = true;
this.cbConfig.CheckedChanged += new System.EventHandler(this.cbConfig_CheckedChanged);
//
// cbSession
//
this.cbSession.AutoSize = true;
this.cbSession.Location = new System.Drawing.Point(13, 37);
this.cbSession.Name = "cbSession";
this.cbSession.Size = new System.Drawing.Size(92, 17);
this.cbSession.TabIndex = 1;
this.cbSession.Text = "Login Session";
this.toolTip.SetToolTip(this.cbSession, "A token that allows logging into the\r\ncurrent TweetDeck account.");
this.cbSession.UseVisualStyleBackColor = true;
this.cbSession.CheckedChanged += new System.EventHandler(this.cbSession_CheckedChanged);
//
// cbPluginData
//
this.cbPluginData.AutoSize = true;
this.cbPluginData.Location = new System.Drawing.Point(13, 61);
this.cbPluginData.Name = "cbPluginData";
this.cbPluginData.Size = new System.Drawing.Size(81, 17);
this.cbPluginData.TabIndex = 2;
this.cbPluginData.Text = "Plugin Data";
this.toolTip.SetToolTip(this.cbPluginData, "Data files generated by plugins.\r\nDoes not include the plugins themselves.");
this.cbPluginData.UseVisualStyleBackColor = true;
this.cbPluginData.CheckedChanged += new System.EventHandler(this.cbPluginData_CheckedChanged);
//
// DialogSettingsExport
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(244, 132);
this.Controls.Add(this.cbPluginData);
this.Controls.Add(this.cbSession);
this.Controls.Add(this.cbConfig);
this.Controls.Add(this.btnApply);
this.Controls.Add(this.btnCancel);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.MinimumSize = new System.Drawing.Size(200, 170);
this.Name = "DialogSettingsExport";
this.ShowIcon = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button btnCancel;
private System.Windows.Forms.Button btnApply;
private System.Windows.Forms.CheckBox cbConfig;
private System.Windows.Forms.CheckBox cbSession;
private System.Windows.Forms.CheckBox cbPluginData;
private System.Windows.Forms.ToolTip toolTip;
}
}

View File

@@ -1,80 +0,0 @@
using System;
using System.Windows.Forms;
using TweetDuck.Core.Other.Settings.Export;
namespace TweetDuck.Core.Other.Settings.Dialogs{
sealed partial class DialogSettingsExport : Form{
public static DialogSettingsExport Import(ExportFileFlags flags){
return new DialogSettingsExport(flags);
}
public static DialogSettingsExport Export(){
return new DialogSettingsExport(ExportFileFlags.None);
}
public ExportFileFlags Flags{
get => selectedFlags;
set{
// this will call events and SetFlag, which also updates the UI
cbConfig.Checked = value.HasFlag(ExportFileFlags.Config);
cbSession.Checked = value.HasFlag(ExportFileFlags.Session);
cbPluginData.Checked = value.HasFlag(ExportFileFlags.PluginData);
}
}
private readonly bool isExporting;
private ExportFileFlags selectedFlags = ExportFileFlags.None;
private DialogSettingsExport(ExportFileFlags importFlags){
InitializeComponent();
this.isExporting = importFlags == ExportFileFlags.None;
if (isExporting){
Text = "Export Profile";
btnApply.Text = "Export Profile";
Flags = ExportFileFlags.All & ~ExportFileFlags.Session;
}
else{
Text = "Import Profile";
Flags = importFlags;
cbConfig.Enabled = cbConfig.Checked;
cbSession.Enabled = cbSession.Checked;
cbPluginData.Enabled = cbPluginData.Checked;
}
}
private void SetFlag(ExportFileFlags flag, bool enable){
selectedFlags = enable ? selectedFlags | flag : selectedFlags & ~flag;
btnApply.Enabled = selectedFlags != ExportFileFlags.None;
if (!isExporting){
btnApply.Text = selectedFlags.HasFlag(ExportFileFlags.Session) ? "Import && Restart" : "Import Profile";
}
}
private void cbConfig_CheckedChanged(object sender, EventArgs e){
SetFlag(ExportFileFlags.Config, cbConfig.Checked);
}
private void cbSession_CheckedChanged(object sender, EventArgs e){
SetFlag(ExportFileFlags.Session, cbSession.Checked);
}
private void cbPluginData_CheckedChanged(object sender, EventArgs e){
SetFlag(ExportFileFlags.PluginData, cbPluginData.Checked);
}
private void btnApply_Click(object sender, EventArgs e){
DialogResult = DialogResult.OK;
Close();
}
private void btnCancel_Click(object sender, EventArgs e){
DialogResult = DialogResult.Cancel;
Close();
}
}
}

View File

@@ -0,0 +1,201 @@
namespace TweetDuck.Core.Other.Settings.Dialogs {
partial class DialogSettingsManage {
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.components = new System.ComponentModel.Container();
this.btnCancel = new System.Windows.Forms.Button();
this.btnContinue = new System.Windows.Forms.Button();
this.cbConfig = new System.Windows.Forms.CheckBox();
this.cbSession = new System.Windows.Forms.CheckBox();
this.cbPluginData = new System.Windows.Forms.CheckBox();
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
this.panelExport = new System.Windows.Forms.Panel();
this.panelDecision = new System.Windows.Forms.Panel();
this.radioReset = new System.Windows.Forms.RadioButton();
this.radioExport = new System.Windows.Forms.RadioButton();
this.radioImport = new System.Windows.Forms.RadioButton();
this.panelExport.SuspendLayout();
this.panelDecision.SuspendLayout();
this.SuspendLayout();
//
// btnCancel
//
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnCancel.Location = new System.Drawing.Point(176, 97);
this.btnCancel.Name = "btnCancel";
this.btnCancel.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
this.btnCancel.Size = new System.Drawing.Size(56, 23);
this.btnCancel.TabIndex = 4;
this.btnCancel.Text = "Cancel";
this.btnCancel.UseVisualStyleBackColor = true;
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
//
// btnContinue
//
this.btnContinue.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnContinue.AutoSize = true;
this.btnContinue.Enabled = false;
this.btnContinue.Location = new System.Drawing.Point(125, 97);
this.btnContinue.Name = "btnContinue";
this.btnContinue.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
this.btnContinue.Size = new System.Drawing.Size(45, 23);
this.btnContinue.TabIndex = 3;
this.btnContinue.Text = "Next";
this.btnContinue.UseVisualStyleBackColor = true;
this.btnContinue.Click += new System.EventHandler(this.btnContinue_Click);
//
// cbConfig
//
this.cbConfig.AutoSize = true;
this.cbConfig.Location = new System.Drawing.Point(0, 3);
this.cbConfig.Name = "cbConfig";
this.cbConfig.Size = new System.Drawing.Size(104, 17);
this.cbConfig.TabIndex = 0;
this.cbConfig.Text = "Program Options";
this.toolTip.SetToolTip(this.cbConfig, "Interface, notification, and update options.");
this.cbConfig.UseVisualStyleBackColor = true;
this.cbConfig.CheckedChanged += new System.EventHandler(this.cbConfig_CheckedChanged);
//
// cbSession
//
this.cbSession.AutoSize = true;
this.cbSession.Location = new System.Drawing.Point(0, 27);
this.cbSession.Name = "cbSession";
this.cbSession.Size = new System.Drawing.Size(92, 17);
this.cbSession.TabIndex = 1;
this.cbSession.Text = "Login Session";
this.toolTip.SetToolTip(this.cbSession, "A token that allows logging into the\r\ncurrent TweetDeck account.");
this.cbSession.UseVisualStyleBackColor = true;
this.cbSession.CheckedChanged += new System.EventHandler(this.cbSession_CheckedChanged);
//
// cbPluginData
//
this.cbPluginData.AutoSize = true;
this.cbPluginData.Location = new System.Drawing.Point(0, 51);
this.cbPluginData.Name = "cbPluginData";
this.cbPluginData.Size = new System.Drawing.Size(81, 17);
this.cbPluginData.TabIndex = 2;
this.cbPluginData.Text = "Plugin Data";
this.toolTip.SetToolTip(this.cbPluginData, "Data files generated by plugins.\r\nDoes not include the plugins themselves.");
this.cbPluginData.UseVisualStyleBackColor = true;
this.cbPluginData.CheckedChanged += new System.EventHandler(this.cbPluginData_CheckedChanged);
//
// panelExport
//
this.panelExport.Controls.Add(this.cbConfig);
this.panelExport.Controls.Add(this.cbPluginData);
this.panelExport.Controls.Add(this.cbSession);
this.panelExport.Location = new System.Drawing.Point(12, 12);
this.panelExport.Name = "panelExport";
this.panelExport.Size = new System.Drawing.Size(220, 79);
this.panelExport.TabIndex = 5;
this.panelExport.Visible = false;
//
// panelDecision
//
this.panelDecision.Controls.Add(this.radioReset);
this.panelDecision.Controls.Add(this.radioExport);
this.panelDecision.Controls.Add(this.radioImport);
this.panelDecision.Location = new System.Drawing.Point(12, 12);
this.panelDecision.Name = "panelDecision";
this.panelDecision.Size = new System.Drawing.Size(220, 79);
this.panelDecision.TabIndex = 6;
//
// radioReset
//
this.radioReset.AutoSize = true;
this.radioReset.Location = new System.Drawing.Point(3, 49);
this.radioReset.Name = "radioReset";
this.radioReset.Size = new System.Drawing.Size(104, 17);
this.radioReset.TabIndex = 2;
this.radioReset.TabStop = true;
this.radioReset.Text = "Restore Defaults";
this.radioReset.UseVisualStyleBackColor = true;
this.radioReset.CheckedChanged += new System.EventHandler(this.radioDecision_CheckedChanged);
//
// radioExport
//
this.radioExport.AutoSize = true;
this.radioExport.Location = new System.Drawing.Point(3, 26);
this.radioExport.Name = "radioExport";
this.radioExport.Size = new System.Drawing.Size(87, 17);
this.radioExport.TabIndex = 1;
this.radioExport.TabStop = true;
this.radioExport.Text = "Export Profile";
this.radioExport.UseVisualStyleBackColor = true;
this.radioExport.CheckedChanged += new System.EventHandler(this.radioDecision_CheckedChanged);
//
// radioImport
//
this.radioImport.AutoSize = true;
this.radioImport.Location = new System.Drawing.Point(3, 3);
this.radioImport.Name = "radioImport";
this.radioImport.Size = new System.Drawing.Size(86, 17);
this.radioImport.TabIndex = 0;
this.radioImport.TabStop = true;
this.radioImport.Text = "Import Profile";
this.radioImport.UseVisualStyleBackColor = true;
this.radioImport.CheckedChanged += new System.EventHandler(this.radioDecision_CheckedChanged);
//
// DialogSettingsManage
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(244, 132);
this.Controls.Add(this.panelDecision);
this.Controls.Add(this.panelExport);
this.Controls.Add(this.btnContinue);
this.Controls.Add(this.btnCancel);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.MinimumSize = new System.Drawing.Size(200, 170);
this.Name = "DialogSettingsManage";
this.ShowIcon = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Manage Options";
this.panelExport.ResumeLayout(false);
this.panelExport.PerformLayout();
this.panelDecision.ResumeLayout(false);
this.panelDecision.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button btnCancel;
private System.Windows.Forms.Button btnContinue;
private System.Windows.Forms.CheckBox cbConfig;
private System.Windows.Forms.CheckBox cbSession;
private System.Windows.Forms.CheckBox cbPluginData;
private System.Windows.Forms.ToolTip toolTip;
private System.Windows.Forms.Panel panelExport;
private System.Windows.Forms.Panel panelDecision;
private System.Windows.Forms.RadioButton radioReset;
private System.Windows.Forms.RadioButton radioExport;
private System.Windows.Forms.RadioButton radioImport;
}
}

View File

@@ -0,0 +1,203 @@
using System;
using System.IO;
using System.Windows.Forms;
using TweetDuck.Configuration;
using TweetDuck.Core.Other.Settings.Export;
using TweetDuck.Plugins;
namespace TweetDuck.Core.Other.Settings.Dialogs{
sealed partial class DialogSettingsManage : Form{
private enum State{
Deciding, Reset, Import, Export
}
public ExportFileFlags Flags{
get => selectedFlags;
set{
// this will call events and SetFlag, which also updates the UI
cbConfig.Checked = value.HasFlag(ExportFileFlags.Config);
cbSession.Checked = value.HasFlag(ExportFileFlags.Session);
cbPluginData.Checked = value.HasFlag(ExportFileFlags.PluginData);
}
}
public bool ShouldReloadBrowser { get; private set; }
private readonly PluginManager plugins;
private State currentState;
private ExportManager importManager;
private ExportFileFlags selectedFlags = ExportFileFlags.None;
public DialogSettingsManage(PluginManager plugins){
InitializeComponent();
this.plugins = plugins;
this.currentState = State.Deciding;
}
private void radioDecision_CheckedChanged(object sender, EventArgs e){
btnContinue.Enabled = true;
}
private void cbConfig_CheckedChanged(object sender, EventArgs e){
SetFlag(ExportFileFlags.Config, cbConfig.Checked);
}
private void cbSession_CheckedChanged(object sender, EventArgs e){
SetFlag(ExportFileFlags.Session, cbSession.Checked);
}
private void cbPluginData_CheckedChanged(object sender, EventArgs e){
SetFlag(ExportFileFlags.PluginData, cbPluginData.Checked);
}
private void btnContinue_Click(object sender, EventArgs e){
string file;
switch(currentState){
case State.Deciding:
// Reset
if (radioReset.Checked){
currentState = State.Reset;
Text = "Restore Defaults";
Flags = ExportFileFlags.Config;
}
// Import
else if (radioImport.Checked){
using(OpenFileDialog dialog = new OpenFileDialog{
AutoUpgradeEnabled = true,
DereferenceLinks = true,
Title = "Import TweetDuck Profile",
Filter = "TweetDuck Profile (*.tdsettings)|*.tdsettings"
}){
if (dialog.ShowDialog() != DialogResult.OK){
return;
}
file = dialog.FileName;
}
importManager = new ExportManager(file, plugins);
currentState = State.Import;
Text = "Import Profile";
Flags = importManager.GetImportFlags();
cbConfig.Enabled = cbConfig.Checked;
cbSession.Enabled = cbSession.Checked;
cbPluginData.Enabled = cbPluginData.Checked;
}
// Export
else if (radioExport.Checked){
currentState = State.Export;
Text = "Export Profile";
btnContinue.Text = "Export Profile";
Flags = ExportFileFlags.All & ~ExportFileFlags.Session;
}
// Continue...
panelDecision.Visible = false;
panelExport.Visible = true;
break;
case State.Reset:
if (FormMessage.Warning("Reset TweetDuck Options", "This will reset the selected items. Are you sure you want to proceed?", FormMessage.Yes, FormMessage.No)){
if (Flags.HasFlag(ExportFileFlags.Config)){
Program.ResetConfig();
}
if (Flags.HasFlag(ExportFileFlags.PluginData)){
try{
File.Delete(Program.PluginConfigFilePath);
Directory.Delete(Program.PluginDataPath, true);
}catch(Exception ex){
Program.Reporter.HandleException("Plugin Data Reset Error", "Could not delete plugin data.", true, ex);
}
}
if (Flags.HasFlag(ExportFileFlags.Session)){
Program.Restart(Arguments.ArgDeleteCookies);
}
else{
ShouldReloadBrowser = true;
}
DialogResult = DialogResult.OK;
Close();
}
break;
case State.Import:
if (importManager.Import(Flags)){
Program.UserConfig.Reload();
if (importManager.IsRestarting){
Program.Restart(Arguments.ArgImportCookies);
}
else{
ShouldReloadBrowser = true;
}
}
else{
Program.Reporter.HandleException("Profile Import Error", "An exception happened while importing TweetDuck profile.", true, importManager.LastException);
}
DialogResult = DialogResult.OK;
Close();
break;
case State.Export:
using(SaveFileDialog dialog = new SaveFileDialog{
AddExtension = true,
AutoUpgradeEnabled = true,
OverwritePrompt = true,
DefaultExt = "tdsettings",
FileName = "TweetDuck.tdsettings",
Title = "Export TweetDuck Profile",
Filter = "TweetDuck Profile (*.tdsettings)|*.tdsettings"
}){
if (dialog.ShowDialog() != DialogResult.OK){
return;
}
file = dialog.FileName;
}
Program.UserConfig.Save();
ExportManager manager = new ExportManager(file, plugins);
if (!manager.Export(Flags)){
Program.Reporter.HandleException("Profile Export Error", "An exception happened while exporting TweetDuck profile.", true, manager.LastException);
}
DialogResult = DialogResult.OK;
Close();
break;
}
}
private void btnCancel_Click(object sender, EventArgs e){
DialogResult = DialogResult.Cancel;
Close();
}
private void SetFlag(ExportFileFlags flag, bool enable){
selectedFlags = enable ? selectedFlags | flag : selectedFlags & ~flag;
btnContinue.Enabled = selectedFlags != ExportFileFlags.None;
if (currentState == State.Import){
btnContinue.Text = selectedFlags.HasFlag(ExportFileFlags.Session) ? "Import && Restart" : "Import Profile";
}
else if (currentState == State.Reset){
btnContinue.Text = selectedFlags.HasFlag(ExportFileFlags.Session) ? "Restore && Restart" : "Restore Defaults";
}
}
}
}

View File

@@ -29,21 +29,22 @@
this.cbLogging = new System.Windows.Forms.CheckBox(); this.cbLogging = new System.Windows.Forms.CheckBox();
this.toolTip = new System.Windows.Forms.ToolTip(this.components); this.toolTip = new System.Windows.Forms.ToolTip(this.components);
this.cbDebugUpdates = new System.Windows.Forms.CheckBox(); this.cbDebugUpdates = new System.Windows.Forms.CheckBox();
this.labelLocale = new System.Windows.Forms.Label();
this.comboLocale = new System.Windows.Forms.ComboBox(); this.comboLocale = new System.Windows.Forms.ComboBox();
this.labelDataFolder = new System.Windows.Forms.Label();
this.tbDataFolder = new System.Windows.Forms.TextBox(); this.tbDataFolder = new System.Windows.Forms.TextBox();
this.tbShortcutTarget = new System.Windows.Forms.TextBox();
this.labelLocale = new System.Windows.Forms.Label();
this.labelDataFolder = new System.Windows.Forms.Label();
this.labelShortcutTarget = new System.Windows.Forms.Label();
this.SuspendLayout(); this.SuspendLayout();
// //
// btnCancel // btnCancel
// //
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnCancel.AutoSize = true; this.btnCancel.Location = new System.Drawing.Point(216, 217);
this.btnCancel.Location = new System.Drawing.Point(160, 171);
this.btnCancel.Name = "btnCancel"; this.btnCancel.Name = "btnCancel";
this.btnCancel.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0); this.btnCancel.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
this.btnCancel.Size = new System.Drawing.Size(56, 23); this.btnCancel.Size = new System.Drawing.Size(56, 23);
this.btnCancel.TabIndex = 7; this.btnCancel.TabIndex = 9;
this.btnCancel.Text = "Cancel"; this.btnCancel.Text = "Cancel";
this.btnCancel.UseVisualStyleBackColor = true; this.btnCancel.UseVisualStyleBackColor = true;
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click); this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
@@ -51,12 +52,11 @@
// btnRestart // btnRestart
// //
this.btnRestart.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.btnRestart.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnRestart.AutoSize = true; this.btnRestart.Location = new System.Drawing.Point(153, 217);
this.btnRestart.Location = new System.Drawing.Point(97, 171);
this.btnRestart.Name = "btnRestart"; this.btnRestart.Name = "btnRestart";
this.btnRestart.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0); this.btnRestart.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
this.btnRestart.Size = new System.Drawing.Size(57, 23); this.btnRestart.Size = new System.Drawing.Size(57, 23);
this.btnRestart.TabIndex = 6; this.btnRestart.TabIndex = 8;
this.btnRestart.Text = "Restart"; this.btnRestart.Text = "Restart";
this.btnRestart.UseVisualStyleBackColor = true; this.btnRestart.UseVisualStyleBackColor = true;
this.btnRestart.Click += new System.EventHandler(this.btnRestart_Click); this.btnRestart.Click += new System.EventHandler(this.btnRestart_Click);
@@ -69,7 +69,7 @@
this.cbLogging.Size = new System.Drawing.Size(64, 17); this.cbLogging.Size = new System.Drawing.Size(64, 17);
this.cbLogging.TabIndex = 0; this.cbLogging.TabIndex = 0;
this.cbLogging.Text = "Logging"; this.cbLogging.Text = "Logging";
this.toolTip.SetToolTip(this.cbLogging, "Logging JavaScript output into a\r\ndebug.txt file in the data folder."); this.toolTip.SetToolTip(this.cbLogging, "Logging JavaScript output into TD_Console.txt file in the data folder.");
this.cbLogging.UseVisualStyleBackColor = true; this.cbLogging.UseVisualStyleBackColor = true;
// //
// cbDebugUpdates // cbDebugUpdates
@@ -83,6 +83,40 @@
this.toolTip.SetToolTip(this.cbDebugUpdates, "Allows updating to pre-releases."); this.toolTip.SetToolTip(this.cbDebugUpdates, "Allows updating to pre-releases.");
this.cbDebugUpdates.UseVisualStyleBackColor = true; this.cbDebugUpdates.UseVisualStyleBackColor = true;
// //
// comboLocale
//
this.comboLocale.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.comboLocale.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboLocale.FormattingEnabled = true;
this.comboLocale.Location = new System.Drawing.Point(15, 83);
this.comboLocale.Name = "comboLocale";
this.comboLocale.Size = new System.Drawing.Size(257, 21);
this.comboLocale.TabIndex = 3;
this.toolTip.SetToolTip(this.comboLocale, "Language used for spell checking.");
//
// tbDataFolder
//
this.tbDataFolder.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.tbDataFolder.Location = new System.Drawing.Point(15, 135);
this.tbDataFolder.Name = "tbDataFolder";
this.tbDataFolder.Size = new System.Drawing.Size(257, 20);
this.tbDataFolder.TabIndex = 5;
this.toolTip.SetToolTip(this.tbDataFolder, "Path to the data folder. Must be either an absolute path,\r\nor a simple folder name that will be created in LocalAppData.");
//
// tbShortcutTarget
//
this.tbShortcutTarget.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.tbShortcutTarget.Cursor = System.Windows.Forms.Cursors.Hand;
this.tbShortcutTarget.Location = new System.Drawing.Point(15, 186);
this.tbShortcutTarget.Name = "tbShortcutTarget";
this.tbShortcutTarget.ReadOnly = true;
this.tbShortcutTarget.Size = new System.Drawing.Size(257, 20);
this.tbShortcutTarget.TabIndex = 7;
this.tbShortcutTarget.Click += new System.EventHandler(this.tbShortcutTarget_Click);
//
// labelLocale // labelLocale
// //
this.labelLocale.AutoSize = true; this.labelLocale.AutoSize = true;
@@ -93,16 +127,6 @@
this.labelLocale.TabIndex = 2; this.labelLocale.TabIndex = 2;
this.labelLocale.Text = "Locale"; this.labelLocale.Text = "Locale";
// //
// comboLocale
//
this.comboLocale.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.comboLocale.FormattingEnabled = true;
this.comboLocale.Location = new System.Drawing.Point(15, 83);
this.comboLocale.Name = "comboLocale";
this.comboLocale.Size = new System.Drawing.Size(201, 21);
this.comboLocale.TabIndex = 3;
//
// labelDataFolder // labelDataFolder
// //
this.labelDataFolder.AutoSize = true; this.labelDataFolder.AutoSize = true;
@@ -113,20 +137,23 @@
this.labelDataFolder.TabIndex = 4; this.labelDataFolder.TabIndex = 4;
this.labelDataFolder.Text = "Data Folder"; this.labelDataFolder.Text = "Data Folder";
// //
// tbDataFolder // labelShortcutTarget
// //
this.tbDataFolder.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) this.labelShortcutTarget.AutoSize = true;
| System.Windows.Forms.AnchorStyles.Right))); this.labelShortcutTarget.Location = new System.Drawing.Point(12, 170);
this.tbDataFolder.Location = new System.Drawing.Point(15, 135); this.labelShortcutTarget.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.tbDataFolder.Name = "tbDataFolder"; this.labelShortcutTarget.Name = "labelShortcutTarget";
this.tbDataFolder.Size = new System.Drawing.Size(201, 20); this.labelShortcutTarget.Size = new System.Drawing.Size(155, 13);
this.tbDataFolder.TabIndex = 5; this.labelShortcutTarget.TabIndex = 6;
this.labelShortcutTarget.Text = "Shortcut Target (click to select)";
// //
// DialogSettingsRestart // DialogSettingsRestart
// //
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(228, 206); this.ClientSize = new System.Drawing.Size(284, 252);
this.Controls.Add(this.tbShortcutTarget);
this.Controls.Add(this.labelShortcutTarget);
this.Controls.Add(this.tbDataFolder); this.Controls.Add(this.tbDataFolder);
this.Controls.Add(this.labelDataFolder); this.Controls.Add(this.labelDataFolder);
this.Controls.Add(this.comboLocale); this.Controls.Add(this.comboLocale);
@@ -157,5 +184,7 @@
private System.Windows.Forms.ComboBox comboLocale; private System.Windows.Forms.ComboBox comboLocale;
private System.Windows.Forms.Label labelDataFolder; private System.Windows.Forms.Label labelDataFolder;
private System.Windows.Forms.TextBox tbDataFolder; private System.Windows.Forms.TextBox tbDataFolder;
private System.Windows.Forms.TextBox tbShortcutTarget;
private System.Windows.Forms.Label labelShortcutTarget;
} }
} }

View File

@@ -3,7 +3,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Configuration; using TweetDuck.Configuration;
using TweetDuck.Core.Utils; using TweetDuck.Data;
namespace TweetDuck.Core.Other.Settings.Dialogs{ namespace TweetDuck.Core.Other.Settings.Dialogs{
sealed partial class DialogSettingsRestart : Form{ sealed partial class DialogSettingsRestart : Form{
@@ -24,12 +24,26 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
cbLogging.Checked = currentArgs.HasFlag(Arguments.ArgLogging); cbLogging.Checked = currentArgs.HasFlag(Arguments.ArgLogging);
cbDebugUpdates.Checked = currentArgs.HasFlag(Arguments.ArgDebugUpdates); cbDebugUpdates.Checked = currentArgs.HasFlag(Arguments.ArgDebugUpdates);
comboLocale.SelectedItem = currentArgs.GetValue(Arguments.ArgLocale, DefaultLocale); comboLocale.SelectedItem = currentArgs.GetValue(Arguments.ArgLocale, DefaultLocale);
cbLogging.CheckedChanged += control_Change;
cbDebugUpdates.CheckedChanged += control_Change;
comboLocale.SelectedValueChanged += control_Change;
if (Program.IsPortable){
tbDataFolder.Text = "Not available in portable version";
tbDataFolder.Enabled = false;
}
else{
tbDataFolder.Text = currentArgs.GetValue(Arguments.ArgDataFolder, string.Empty); tbDataFolder.Text = currentArgs.GetValue(Arguments.ArgDataFolder, string.Empty);
tbDataFolder.TextChanged += control_Change;
}
control_Change(this, EventArgs.Empty);
Text = Program.BrandName+" Arguments"; Text = Program.BrandName+" Arguments";
} }
private void btnRestart_Click(object sender, EventArgs e){ private void control_Change(object sender, EventArgs e){
Args = new CommandLineArgs(); Args = new CommandLineArgs();
if (cbLogging.Checked){ if (cbLogging.Checked){
@@ -46,10 +60,21 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
Args.SetValue(Arguments.ArgLocale, locale); Args.SetValue(Arguments.ArgLocale, locale);
} }
if (!string.IsNullOrWhiteSpace(tbDataFolder.Text)){ if (!string.IsNullOrWhiteSpace(tbDataFolder.Text) && tbDataFolder.Enabled){
Args.SetValue(Arguments.ArgDataFolder, tbDataFolder.Text); Args.SetValue(Arguments.ArgDataFolder, tbDataFolder.Text);
} }
tbShortcutTarget.Text = $@"""{Application.ExecutablePath}""{(Args.Count > 0 ? " " : "")}{Args}";
tbShortcutTarget.Select(tbShortcutTarget.Text.Length, 0);
}
private void tbShortcutTarget_Click(object sender, EventArgs e){
if (tbShortcutTarget.SelectionLength == 0){
tbShortcutTarget.SelectAll();
}
}
private void btnRestart_Click(object sender, EventArgs e){
DialogResult = DialogResult.OK; DialogResult = DialogResult.OK;
Close(); Close();
} }

View File

@@ -2,8 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Windows.Forms; using TweetDuck.Data;
using TweetDuck.Configuration;
using TweetDuck.Plugins; using TweetDuck.Plugins;
using TweetDuck.Plugins.Enums; using TweetDuck.Plugins.Enums;
@@ -38,7 +37,7 @@ namespace TweetDuck.Core.Other.Settings.Export{
try{ try{
stream.WriteFile(new string[]{ "plugin.data", plugin.Identifier, path.Relative }, path.Full); stream.WriteFile(new string[]{ "plugin.data", plugin.Identifier, path.Relative }, path.Full);
}catch(ArgumentOutOfRangeException e){ }catch(ArgumentOutOfRangeException e){
MessageBox.Show("Could not include a plugin file in the export. "+e.Message, "Export Profile", MessageBoxButtons.OK, MessageBoxIcon.Warning); FormMessage.Warning("Export Profile", "Could not include a plugin file in the export. "+e.Message, FormMessage.OK);
} }
} }
} }
@@ -138,14 +137,7 @@ namespace TweetDuck.Core.Other.Settings.Export{
} }
if (missingPlugins.Count > 0){ if (missingPlugins.Count > 0){
MessageBox.Show("Detected missing plugins when importing plugin data:"+Environment.NewLine+string.Join(Environment.NewLine, missingPlugins), "Importing "+Program.BrandName+" Profile", MessageBoxButtons.OK, MessageBoxIcon.Information); FormMessage.Information("Importing TweetDuck Profile", "Detected missing plugins when importing plugin data:\n"+string.Join("\n", missingPlugins), FormMessage.OK);
}
if (IsRestarting){
Program.Restart(new string[]{ Arguments.ArgImportCookies });
}
else{
Program.ReloadConfig();
} }
return true; return true;
@@ -169,6 +161,16 @@ namespace TweetDuck.Core.Other.Settings.Export{
} }
} }
public static void DeleteCookies(){
try{
if (File.Exists(CookiesPath)){
File.Delete(CookiesPath);
}
}catch(Exception e){
Program.Reporter.HandleException("Session Reset Error", "Could not remove the cookie file to reset the login session.", true, e);
}
}
private static IEnumerable<PathInfo> EnumerateFilesRelative(string root){ private static IEnumerable<PathInfo> EnumerateFilesRelative(string root){
return Directory.Exists(root) ? Directory.EnumerateFiles(root, "*.*", SearchOption.AllDirectories).Select(fullPath => new PathInfo{ return Directory.Exists(root) ? Directory.EnumerateFiles(root, "*.*", SearchOption.AllDirectories).Select(fullPath => new PathInfo{
Full = fullPath, Full = fullPath,
@@ -176,7 +178,7 @@ namespace TweetDuck.Core.Other.Settings.Export{
}) : Enumerable.Empty<PathInfo>(); }) : Enumerable.Empty<PathInfo>();
} }
private class PathInfo{ private sealed class PathInfo{
public string Full { get; set; } public string Full { get; set; }
public string Relative { get; set; } public string Relative { get; set; }
} }

View File

@@ -33,46 +33,50 @@
this.btnRestart = new System.Windows.Forms.Button(); this.btnRestart = new System.Windows.Forms.Button();
this.btnOpenAppFolder = new System.Windows.Forms.Button(); this.btnOpenAppFolder = new System.Windows.Forms.Button();
this.btnOpenDataFolder = new System.Windows.Forms.Button(); this.btnOpenDataFolder = new System.Windows.Forms.Button();
this.btnReset = new System.Windows.Forms.Button(); this.numMemoryThreshold = new TweetDuck.Core.Controls.NumericUpDownEx();
this.btnImport = new System.Windows.Forms.Button(); this.checkBrowserGCReload = new System.Windows.Forms.CheckBox();
this.btnExport = new System.Windows.Forms.Button(); this.labelApp = new System.Windows.Forms.Label();
this.groupPerformance = new System.Windows.Forms.GroupBox(); this.panelApp = new System.Windows.Forms.Panel();
this.groupConfiguration = new System.Windows.Forms.GroupBox(); this.labelPerformance = new System.Windows.Forms.Label();
this.groupApp = new System.Windows.Forms.GroupBox(); this.panelPerformance = new System.Windows.Forms.Panel();
this.groupPerformance.SuspendLayout(); this.labelMemoryUsage = new System.Windows.Forms.Label();
this.groupConfiguration.SuspendLayout(); this.panelConfiguration = new System.Windows.Forms.Panel();
this.groupApp.SuspendLayout(); this.labelConfiguration = new System.Windows.Forms.Label();
((System.ComponentModel.ISupportInitialize)(this.numMemoryThreshold)).BeginInit();
this.panelApp.SuspendLayout();
this.panelPerformance.SuspendLayout();
this.panelConfiguration.SuspendLayout();
this.SuspendLayout(); this.SuspendLayout();
// //
// btnClearCache // btnClearCache
// //
this.btnClearCache.Location = new System.Drawing.Point(6, 44); this.btnClearCache.Location = new System.Drawing.Point(5, 28);
this.btnClearCache.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
this.btnClearCache.Name = "btnClearCache"; this.btnClearCache.Name = "btnClearCache";
this.btnClearCache.Size = new System.Drawing.Size(171, 23); this.btnClearCache.Size = new System.Drawing.Size(144, 23);
this.btnClearCache.TabIndex = 1; this.btnClearCache.TabIndex = 1;
this.btnClearCache.Text = "Clear Cache (calculating)"; this.btnClearCache.Text = "Clear Cache (calculating)";
this.toolTip.SetToolTip(this.btnClearCache, "Clearing cache will free up space taken by downloaded images and other resources." + this.toolTip.SetToolTip(this.btnClearCache, "Clearing cache will free up space taken by downloaded images and other resources.");
"");
this.btnClearCache.UseVisualStyleBackColor = true; this.btnClearCache.UseVisualStyleBackColor = true;
// //
// checkHardwareAcceleration // checkHardwareAcceleration
// //
this.checkHardwareAcceleration.AutoSize = true; this.checkHardwareAcceleration.AutoSize = true;
this.checkHardwareAcceleration.Location = new System.Drawing.Point(9, 21); this.checkHardwareAcceleration.Location = new System.Drawing.Point(6, 5);
this.checkHardwareAcceleration.Margin = new System.Windows.Forms.Padding(6, 5, 3, 3); this.checkHardwareAcceleration.Margin = new System.Windows.Forms.Padding(6, 5, 3, 3);
this.checkHardwareAcceleration.Name = "checkHardwareAcceleration"; this.checkHardwareAcceleration.Name = "checkHardwareAcceleration";
this.checkHardwareAcceleration.Size = new System.Drawing.Size(134, 17); this.checkHardwareAcceleration.Size = new System.Drawing.Size(134, 17);
this.checkHardwareAcceleration.TabIndex = 0; this.checkHardwareAcceleration.TabIndex = 0;
this.checkHardwareAcceleration.Text = "Hardware Acceleration"; this.checkHardwareAcceleration.Text = "Hardware Acceleration";
this.toolTip.SetToolTip(this.checkHardwareAcceleration, "Uses your graphics card to improve performance.\r\nDisable if you experience issues" + this.toolTip.SetToolTip(this.checkHardwareAcceleration, "Uses graphics card to improve performance. Disable if you experience\r\nvisual glitches. This option will not be exported in a profile.");
" with rendering.");
this.checkHardwareAcceleration.UseVisualStyleBackColor = true; this.checkHardwareAcceleration.UseVisualStyleBackColor = true;
// //
// btnEditCefArgs // btnEditCefArgs
// //
this.btnEditCefArgs.Location = new System.Drawing.Point(6, 19); this.btnEditCefArgs.Location = new System.Drawing.Point(5, 3);
this.btnEditCefArgs.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
this.btnEditCefArgs.Name = "btnEditCefArgs"; this.btnEditCefArgs.Name = "btnEditCefArgs";
this.btnEditCefArgs.Size = new System.Drawing.Size(171, 23); this.btnEditCefArgs.Size = new System.Drawing.Size(144, 23);
this.btnEditCefArgs.TabIndex = 0; this.btnEditCefArgs.TabIndex = 0;
this.btnEditCefArgs.Text = "Edit CEF Arguments"; this.btnEditCefArgs.Text = "Edit CEF Arguments";
this.toolTip.SetToolTip(this.btnEditCefArgs, "Set custom command line arguments for Chromium Embedded Framework."); this.toolTip.SetToolTip(this.btnEditCefArgs, "Set custom command line arguments for Chromium Embedded Framework.");
@@ -80,9 +84,9 @@
// //
// btnEditCSS // btnEditCSS
// //
this.btnEditCSS.Location = new System.Drawing.Point(6, 48); this.btnEditCSS.Location = new System.Drawing.Point(155, 3);
this.btnEditCSS.Name = "btnEditCSS"; this.btnEditCSS.Name = "btnEditCSS";
this.btnEditCSS.Size = new System.Drawing.Size(171, 23); this.btnEditCSS.Size = new System.Drawing.Size(144, 23);
this.btnEditCSS.TabIndex = 1; this.btnEditCSS.TabIndex = 1;
this.btnEditCSS.Text = "Edit CSS"; this.btnEditCSS.Text = "Edit CSS";
this.toolTip.SetToolTip(this.btnEditCSS, "Set custom CSS for browser and notification windows."); this.toolTip.SetToolTip(this.btnEditCSS, "Set custom CSS for browser and notification windows.");
@@ -90,9 +94,9 @@
// //
// btnRestartArgs // btnRestartArgs
// //
this.btnRestartArgs.Location = new System.Drawing.Point(6, 106); this.btnRestartArgs.Location = new System.Drawing.Point(155, 32);
this.btnRestartArgs.Name = "btnRestartArgs"; this.btnRestartArgs.Name = "btnRestartArgs";
this.btnRestartArgs.Size = new System.Drawing.Size(171, 23); this.btnRestartArgs.Size = new System.Drawing.Size(144, 23);
this.btnRestartArgs.TabIndex = 3; this.btnRestartArgs.TabIndex = 3;
this.btnRestartArgs.Text = "Restart with Arguments"; this.btnRestartArgs.Text = "Restart with Arguments";
this.toolTip.SetToolTip(this.btnRestartArgs, "Restarts the program with customizable\r\ncommand line arguments."); this.toolTip.SetToolTip(this.btnRestartArgs, "Restarts the program with customizable\r\ncommand line arguments.");
@@ -100,20 +104,20 @@
// //
// btnRestart // btnRestart
// //
this.btnRestart.Location = new System.Drawing.Point(6, 77); this.btnRestart.Location = new System.Drawing.Point(155, 3);
this.btnRestart.Name = "btnRestart"; this.btnRestart.Name = "btnRestart";
this.btnRestart.Size = new System.Drawing.Size(171, 23); this.btnRestart.Size = new System.Drawing.Size(144, 23);
this.btnRestart.TabIndex = 2; this.btnRestart.TabIndex = 2;
this.btnRestart.Text = "Restart the Program"; this.btnRestart.Text = "Restart the Program";
this.toolTip.SetToolTip(this.btnRestart, "Restarts the program using the same command\r\nline arguments that were used at lau" + this.toolTip.SetToolTip(this.btnRestart, "Restarts the program using the same command\r\nline arguments that were used at launch.");
"nch.");
this.btnRestart.UseVisualStyleBackColor = true; this.btnRestart.UseVisualStyleBackColor = true;
// //
// btnOpenAppFolder // btnOpenAppFolder
// //
this.btnOpenAppFolder.Location = new System.Drawing.Point(6, 19); this.btnOpenAppFolder.Location = new System.Drawing.Point(5, 3);
this.btnOpenAppFolder.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
this.btnOpenAppFolder.Name = "btnOpenAppFolder"; this.btnOpenAppFolder.Name = "btnOpenAppFolder";
this.btnOpenAppFolder.Size = new System.Drawing.Size(171, 23); this.btnOpenAppFolder.Size = new System.Drawing.Size(144, 23);
this.btnOpenAppFolder.TabIndex = 0; this.btnOpenAppFolder.TabIndex = 0;
this.btnOpenAppFolder.Text = "Open Program Folder"; this.btnOpenAppFolder.Text = "Open Program Folder";
this.toolTip.SetToolTip(this.btnOpenAppFolder, "Opens the folder where the app is located."); this.toolTip.SetToolTip(this.btnOpenAppFolder, "Opens the folder where the app is located.");
@@ -121,102 +125,142 @@
// //
// btnOpenDataFolder // btnOpenDataFolder
// //
this.btnOpenDataFolder.Location = new System.Drawing.Point(6, 48); this.btnOpenDataFolder.Location = new System.Drawing.Point(5, 32);
this.btnOpenDataFolder.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
this.btnOpenDataFolder.Name = "btnOpenDataFolder"; this.btnOpenDataFolder.Name = "btnOpenDataFolder";
this.btnOpenDataFolder.Size = new System.Drawing.Size(171, 23); this.btnOpenDataFolder.Size = new System.Drawing.Size(144, 23);
this.btnOpenDataFolder.TabIndex = 1; this.btnOpenDataFolder.TabIndex = 1;
this.btnOpenDataFolder.Text = "Open Data Folder"; this.btnOpenDataFolder.Text = "Open Data Folder";
this.toolTip.SetToolTip(this.btnOpenDataFolder, "Opens the folder where your profile data is located."); this.toolTip.SetToolTip(this.btnOpenDataFolder, "Opens the folder where your profile data is located.");
this.btnOpenDataFolder.UseVisualStyleBackColor = true; this.btnOpenDataFolder.UseVisualStyleBackColor = true;
// //
// btnReset // numMemoryThreshold
// //
this.btnReset.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.numMemoryThreshold.Increment = new decimal(new int[] {
this.btnReset.AutoSize = true; 50,
this.btnReset.Location = new System.Drawing.Point(190, 268); 0,
this.btnReset.Name = "btnReset"; 0,
this.btnReset.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0); 0});
this.btnReset.Size = new System.Drawing.Size(102, 23); this.numMemoryThreshold.Location = new System.Drawing.Point(202, 82);
this.btnReset.TabIndex = 5; this.numMemoryThreshold.Maximum = 2000;
this.btnReset.Text = "Restore Defaults"; this.numMemoryThreshold.Minimum = 200;
this.btnReset.UseVisualStyleBackColor = true; this.numMemoryThreshold.Name = "numMemoryThreshold";
this.numMemoryThreshold.Size = new System.Drawing.Size(97, 20);
this.numMemoryThreshold.TabIndex = 4;
this.numMemoryThreshold.TextSuffix = " MB";
this.toolTip.SetToolTip(this.numMemoryThreshold, "Minimum amount of memory usage by the browser process to trigger the cleanup.\r\nThis is not a limit, the usage is allowed to exceed this value.");
this.numMemoryThreshold.Value = 400;
// //
// btnImport // checkBrowserGCReload
// //
this.btnImport.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.checkBrowserGCReload.AutoSize = true;
this.btnImport.AutoSize = true; this.checkBrowserGCReload.Location = new System.Drawing.Point(6, 84);
this.btnImport.Location = new System.Drawing.Point(100, 268); this.checkBrowserGCReload.Margin = new System.Windows.Forms.Padding(6, 5, 3, 3);
this.btnImport.Name = "btnImport"; this.checkBrowserGCReload.Name = "checkBrowserGCReload";
this.btnImport.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0); this.checkBrowserGCReload.Size = new System.Drawing.Size(190, 17);
this.btnImport.Size = new System.Drawing.Size(84, 23); this.checkBrowserGCReload.TabIndex = 3;
this.btnImport.TabIndex = 4; this.checkBrowserGCReload.Text = "Enable Browser Memory Threshold";
this.btnImport.Text = "Import Profile"; this.toolTip.SetToolTip(this.checkBrowserGCReload, "Automatically reloads TweetDeck to save memory. This option only works if\r\nthe browser is in a \'default state\', i.e. all modals and drawers are closed, and\r\nall columns are scrolled to top. This option will not be exported in a profile.");
this.btnImport.UseVisualStyleBackColor = true; this.checkBrowserGCReload.UseVisualStyleBackColor = true;
// //
// btnExport // labelApp
// //
this.btnExport.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.labelApp.AutoSize = true;
this.btnExport.AutoSize = true; this.labelApp.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.btnExport.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; this.labelApp.Location = new System.Drawing.Point(6, 8);
this.btnExport.Location = new System.Drawing.Point(9, 268); this.labelApp.Margin = new System.Windows.Forms.Padding(0, 2, 0, 0);
this.btnExport.Name = "btnExport"; this.labelApp.Name = "labelApp";
this.btnExport.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0); this.labelApp.Size = new System.Drawing.Size(38, 20);
this.btnExport.Size = new System.Drawing.Size(85, 23); this.labelApp.TabIndex = 0;
this.btnExport.TabIndex = 3; this.labelApp.Text = "App";
this.btnExport.Text = "Export Profile";
this.btnExport.UseVisualStyleBackColor = true;
// //
// groupPerformance // panelApp
// //
this.groupPerformance.Controls.Add(this.checkHardwareAcceleration); this.panelApp.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
this.groupPerformance.Controls.Add(this.btnClearCache); | System.Windows.Forms.AnchorStyles.Right)));
this.groupPerformance.Location = new System.Drawing.Point(9, 9); this.panelApp.Controls.Add(this.btnOpenDataFolder);
this.groupPerformance.Name = "groupPerformance"; this.panelApp.Controls.Add(this.btnOpenAppFolder);
this.groupPerformance.Size = new System.Drawing.Size(183, 74); this.panelApp.Controls.Add(this.btnRestart);
this.groupPerformance.TabIndex = 0; this.panelApp.Controls.Add(this.btnRestartArgs);
this.groupPerformance.TabStop = false; this.panelApp.Location = new System.Drawing.Point(9, 31);
this.groupPerformance.Text = "Performance"; this.panelApp.Name = "panelApp";
this.panelApp.Size = new System.Drawing.Size(322, 59);
this.panelApp.TabIndex = 1;
// //
// groupConfiguration // labelPerformance
// //
this.groupConfiguration.Controls.Add(this.btnEditCSS); this.labelPerformance.AutoSize = true;
this.groupConfiguration.Controls.Add(this.btnEditCefArgs); this.labelPerformance.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.groupConfiguration.Location = new System.Drawing.Point(9, 89); this.labelPerformance.Location = new System.Drawing.Point(6, 114);
this.groupConfiguration.Name = "groupConfiguration"; this.labelPerformance.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0);
this.groupConfiguration.Size = new System.Drawing.Size(183, 77); this.labelPerformance.Name = "labelPerformance";
this.groupConfiguration.TabIndex = 1; this.labelPerformance.Size = new System.Drawing.Size(100, 20);
this.groupConfiguration.TabStop = false; this.labelPerformance.TabIndex = 2;
this.groupConfiguration.Text = "Configuration"; this.labelPerformance.Text = "Performance";
// //
// groupApp // panelPerformance
// //
this.groupApp.Controls.Add(this.btnOpenDataFolder); this.panelPerformance.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
this.groupApp.Controls.Add(this.btnOpenAppFolder); | System.Windows.Forms.AnchorStyles.Right)));
this.groupApp.Controls.Add(this.btnRestartArgs); this.panelPerformance.Controls.Add(this.checkBrowserGCReload);
this.groupApp.Controls.Add(this.btnRestart); this.panelPerformance.Controls.Add(this.numMemoryThreshold);
this.groupApp.Location = new System.Drawing.Point(198, 9); this.panelPerformance.Controls.Add(this.labelMemoryUsage);
this.groupApp.Name = "groupApp"; this.panelPerformance.Controls.Add(this.checkHardwareAcceleration);
this.groupApp.Size = new System.Drawing.Size(183, 135); this.panelPerformance.Controls.Add(this.btnClearCache);
this.groupApp.TabIndex = 2; this.panelPerformance.Location = new System.Drawing.Point(9, 137);
this.groupApp.TabStop = false; this.panelPerformance.Name = "panelPerformance";
this.groupApp.Text = "App"; this.panelPerformance.Size = new System.Drawing.Size(322, 105);
this.panelPerformance.TabIndex = 3;
//
// labelMemoryUsage
//
this.labelMemoryUsage.AutoSize = true;
this.labelMemoryUsage.Location = new System.Drawing.Point(3, 66);
this.labelMemoryUsage.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.labelMemoryUsage.Name = "labelMemoryUsage";
this.labelMemoryUsage.Size = new System.Drawing.Size(78, 13);
this.labelMemoryUsage.TabIndex = 2;
this.labelMemoryUsage.Text = "Memory Usage";
//
// panelConfiguration
//
this.panelConfiguration.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.panelConfiguration.Controls.Add(this.btnEditCSS);
this.panelConfiguration.Controls.Add(this.btnEditCefArgs);
this.panelConfiguration.Location = new System.Drawing.Point(9, 289);
this.panelConfiguration.Name = "panelConfiguration";
this.panelConfiguration.Size = new System.Drawing.Size(322, 29);
this.panelConfiguration.TabIndex = 5;
//
// labelConfiguration
//
this.labelConfiguration.AutoSize = true;
this.labelConfiguration.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelConfiguration.Location = new System.Drawing.Point(6, 266);
this.labelConfiguration.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0);
this.labelConfiguration.Name = "labelConfiguration";
this.labelConfiguration.Size = new System.Drawing.Size(104, 20);
this.labelConfiguration.TabIndex = 4;
this.labelConfiguration.Text = "Configuration";
// //
// TabSettingsAdvanced // TabSettingsAdvanced
// //
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.groupApp); this.Controls.Add(this.labelConfiguration);
this.Controls.Add(this.groupConfiguration); this.Controls.Add(this.panelConfiguration);
this.Controls.Add(this.groupPerformance); this.Controls.Add(this.panelPerformance);
this.Controls.Add(this.btnReset); this.Controls.Add(this.labelPerformance);
this.Controls.Add(this.btnImport); this.Controls.Add(this.panelApp);
this.Controls.Add(this.btnExport); this.Controls.Add(this.labelApp);
this.Name = "TabSettingsAdvanced"; this.Name = "TabSettingsAdvanced";
this.Size = new System.Drawing.Size(478, 300); this.Size = new System.Drawing.Size(340, 328);
this.groupPerformance.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.numMemoryThreshold)).EndInit();
this.groupPerformance.PerformLayout(); this.panelApp.ResumeLayout(false);
this.groupConfiguration.ResumeLayout(false); this.panelPerformance.ResumeLayout(false);
this.groupApp.ResumeLayout(false); this.panelPerformance.PerformLayout();
this.panelConfiguration.ResumeLayout(false);
this.ResumeLayout(false); this.ResumeLayout(false);
this.PerformLayout(); this.PerformLayout();
@@ -227,17 +271,20 @@
private System.Windows.Forms.Button btnClearCache; private System.Windows.Forms.Button btnClearCache;
private System.Windows.Forms.CheckBox checkHardwareAcceleration; private System.Windows.Forms.CheckBox checkHardwareAcceleration;
private System.Windows.Forms.ToolTip toolTip; private System.Windows.Forms.ToolTip toolTip;
private System.Windows.Forms.Button btnReset;
private System.Windows.Forms.Button btnImport;
private System.Windows.Forms.Button btnExport;
private System.Windows.Forms.GroupBox groupPerformance;
private System.Windows.Forms.GroupBox groupConfiguration;
private System.Windows.Forms.Button btnEditCefArgs; private System.Windows.Forms.Button btnEditCefArgs;
private System.Windows.Forms.Button btnEditCSS; private System.Windows.Forms.Button btnEditCSS;
private System.Windows.Forms.GroupBox groupApp;
private System.Windows.Forms.Button btnRestartArgs; private System.Windows.Forms.Button btnRestartArgs;
private System.Windows.Forms.Button btnRestart; private System.Windows.Forms.Button btnRestart;
private System.Windows.Forms.Button btnOpenAppFolder; private System.Windows.Forms.Button btnOpenAppFolder;
private System.Windows.Forms.Button btnOpenDataFolder; private System.Windows.Forms.Button btnOpenDataFolder;
private System.Windows.Forms.Label labelApp;
private System.Windows.Forms.Panel panelApp;
private System.Windows.Forms.Label labelPerformance;
private System.Windows.Forms.Panel panelPerformance;
private System.Windows.Forms.Panel panelConfiguration;
private System.Windows.Forms.Label labelConfiguration;
private System.Windows.Forms.Label labelMemoryUsage;
private Controls.NumericUpDownEx numMemoryThreshold;
private System.Windows.Forms.CheckBox checkBrowserGCReload;
} }
} }

View File

@@ -4,29 +4,31 @@ using System.Windows.Forms;
using TweetDuck.Configuration; using TweetDuck.Configuration;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Other.Settings.Dialogs; using TweetDuck.Core.Other.Settings.Dialogs;
using TweetDuck.Core.Other.Settings.Export;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetDuck.Plugins;
namespace TweetDuck.Core.Other.Settings{ namespace TweetDuck.Core.Other.Settings{
partial class TabSettingsAdvanced : BaseTabSettings{ sealed partial class TabSettingsAdvanced : BaseTabSettings{
private readonly Action<string> reinjectBrowserCSS; private static SystemConfig SysConfig => Program.SystemConfig;
private readonly PluginManager plugins;
public TabSettingsAdvanced(Action<string> reinjectBrowserCSS, PluginManager plugins){ private readonly Action<string> reinjectBrowserCSS;
public TabSettingsAdvanced(Action<string> reinjectBrowserCSS){
InitializeComponent(); InitializeComponent();
this.reinjectBrowserCSS = reinjectBrowserCSS; this.reinjectBrowserCSS = reinjectBrowserCSS;
this.plugins = plugins;
if (SystemConfig.IsHardwareAccelerationSupported){ if (SystemConfig.IsHardwareAccelerationSupported){
checkHardwareAcceleration.Checked = Program.SystemConfig.HardwareAcceleration; checkHardwareAcceleration.Checked = SysConfig.HardwareAcceleration;
} }
else{ else{
checkHardwareAcceleration.Enabled = false; checkHardwareAcceleration.Enabled = false;
checkHardwareAcceleration.Checked = false; checkHardwareAcceleration.Checked = false;
} }
checkBrowserGCReload.Checked = SysConfig.EnableBrowserGCReload;
numMemoryThreshold.Enabled = checkBrowserGCReload.Checked;
numMemoryThreshold.SetValueSafe(SysConfig.BrowserMemoryThreshold);
BrowserCache.CalculateCacheSize(bytes => this.InvokeSafe(() => { BrowserCache.CalculateCacheSize(bytes => this.InvokeSafe(() => {
if (bytes == -1L){ if (bytes == -1L){
btnClearCache.Text = "Clear Cache (unknown size)"; btnClearCache.Text = "Clear Cache (unknown size)";
@@ -41,30 +43,40 @@ namespace TweetDuck.Core.Other.Settings{
btnClearCache.Click += btnClearCache_Click; btnClearCache.Click += btnClearCache_Click;
checkHardwareAcceleration.CheckedChanged += checkHardwareAcceleration_CheckedChanged; checkHardwareAcceleration.CheckedChanged += checkHardwareAcceleration_CheckedChanged;
checkBrowserGCReload.CheckedChanged += checkBrowserGCReload_CheckedChanged;
numMemoryThreshold.ValueChanged += numMemoryThreshold_ValueChanged;
btnEditCefArgs.Click += btnEditCefArgs_Click; btnEditCefArgs.Click += btnEditCefArgs_Click;
btnEditCSS.Click += btnEditCSS_Click; btnEditCSS.Click += btnEditCSS_Click;
btnExport.Click += btnExport_Click;
btnImport.Click += btnImport_Click;
btnReset.Click += btnReset_Click;
btnOpenAppFolder.Click += btnOpenAppFolder_Click; btnOpenAppFolder.Click += btnOpenAppFolder_Click;
btnOpenDataFolder.Click += btnOpenDataFolder_Click; btnOpenDataFolder.Click += btnOpenDataFolder_Click;
btnRestart.Click += btnRestart_Click; btnRestart.Click += btnRestart_Click;
btnRestartArgs.Click += btnRestartArgs_Click; btnRestartArgs.Click += btnRestartArgs_Click;
} }
public override void OnClosing(){
SysConfig.Save();
}
private void btnClearCache_Click(object sender, EventArgs e){ private void btnClearCache_Click(object sender, EventArgs e){
btnClearCache.Enabled = false; btnClearCache.Enabled = false;
BrowserCache.SetClearOnExit(); BrowserCache.SetClearOnExit();
FormMessage.Information("Clear Cache", "Cache will be automatically cleared when TweetDuck exits.", FormMessage.OK);
MessageBox.Show("Cache will be automatically cleared when "+Program.BrandName+" exits.", "Clear Cache", MessageBoxButtons.OK, MessageBoxIcon.Information);
} }
private void checkHardwareAcceleration_CheckedChanged(object sender, EventArgs e){ private void checkHardwareAcceleration_CheckedChanged(object sender, EventArgs e){
Program.SystemConfig.HardwareAcceleration = checkHardwareAcceleration.Checked; SysConfig.HardwareAcceleration = checkHardwareAcceleration.Checked;
Program.SystemConfig.Save(); PromptRestart(); // calls OnClosing
PromptRestart(); }
private void checkBrowserGCReload_CheckedChanged(object sender, EventArgs e){
SysConfig.EnableBrowserGCReload = checkBrowserGCReload.Checked;
numMemoryThreshold.Enabled = checkBrowserGCReload.Checked;
}
private void numMemoryThreshold_ValueChanged(object sender, EventArgs e){
SysConfig.BrowserMemoryThreshold = (int)numMemoryThreshold.Value;
} }
private void btnEditCefArgs_Click(object sender, EventArgs e){ private void btnEditCefArgs_Click(object sender, EventArgs e){
@@ -75,12 +87,12 @@ namespace TweetDuck.Core.Other.Settings{
}; };
form.FormClosed += (sender2, args2) => { form.FormClosed += (sender2, args2) => {
NativeMethods.SetFormDisabled(ParentForm, false); RestoreParentForm();
if (form.DialogResult == DialogResult.OK){ if (form.DialogResult == DialogResult.OK){
Config.CustomCefArgs = form.CefArgs; Config.CustomCefArgs = form.CefArgs;
form.Dispose();
PromptRestart(); PromptRestart();
form.Dispose();
} }
else form.Dispose(); else form.Dispose();
}; };
@@ -97,7 +109,7 @@ namespace TweetDuck.Core.Other.Settings{
}; };
form.FormClosed += (sender2, args2) => { form.FormClosed += (sender2, args2) => {
NativeMethods.SetFormDisabled(ParentForm, false); RestoreParentForm();
if (form.DialogResult == DialogResult.OK){ if (form.DialogResult == DialogResult.OK){
Config.CustomBrowserCSS = form.BrowserCSS; Config.CustomBrowserCSS = form.BrowserCSS;
@@ -112,88 +124,6 @@ namespace TweetDuck.Core.Other.Settings{
NativeMethods.SetFormDisabled(ParentForm, true); NativeMethods.SetFormDisabled(ParentForm, true);
} }
private void btnExport_Click(object sender, EventArgs e){
ExportFileFlags flags;
using(DialogSettingsExport dialog = DialogSettingsExport.Export()){
if (dialog.ShowDialog() != DialogResult.OK){
return;
}
flags = dialog.Flags;
}
string file;
using(SaveFileDialog dialog = new SaveFileDialog{
AddExtension = true,
AutoUpgradeEnabled = true,
OverwritePrompt = true,
DefaultExt = "tdsettings",
FileName = Program.BrandName+".tdsettings",
Title = "Export "+Program.BrandName+" Settings",
Filter = Program.BrandName+" Settings (*.tdsettings)|*.tdsettings"
}){
if (dialog.ShowDialog() != DialogResult.OK){
return;
}
file = dialog.FileName;
}
Program.UserConfig.Save();
ExportManager manager = new ExportManager(file, plugins);
if (!manager.Export(flags)){
Program.Reporter.HandleException("Profile Export Error", "An exception happened while exporting "+Program.BrandName+" settings.", true, manager.LastException);
}
}
private void btnImport_Click(object sender, EventArgs e){
string file;
using(OpenFileDialog dialog = new OpenFileDialog{
AutoUpgradeEnabled = true,
DereferenceLinks = true,
Title = "Import "+Program.BrandName+" Settings",
Filter = Program.BrandName+" Settings (*.tdsettings)|*.tdsettings"
}){
if (dialog.ShowDialog() != DialogResult.OK){
return;
}
file = dialog.FileName;
}
ExportManager manager = new ExportManager(file, plugins);
ExportFileFlags flags;
using(DialogSettingsExport dialog = DialogSettingsExport.Import(manager.GetImportFlags())){
if (dialog.ShowDialog() != DialogResult.OK){
return;
}
flags = dialog.Flags;
}
if (manager.Import(flags)){
if (!manager.IsRestarting){
((FormSettings)ParentForm).ReloadUI();
}
}
else{
Program.Reporter.HandleException("Profile Import Error", "An exception happened while importing "+Program.BrandName+" settings.", true, manager.LastException);
}
}
private void btnReset_Click(object sender, EventArgs e){
if (MessageBox.Show("This will reset all of your program settings. Plugins will not be affected. Do you want to proceed?", "Reset "+Program.BrandName+" Settings", MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) == DialogResult.Yes){
Program.ResetConfig();
((FormSettings)ParentForm).ReloadUI();
}
}
private void btnOpenAppFolder_Click(object sender, EventArgs e){ private void btnOpenAppFolder_Click(object sender, EventArgs e){
using(Process.Start("explorer.exe", "\""+Program.ProgramPath+"\"")){} using(Process.Start("explorer.exe", "\""+Program.ProgramPath+"\"")){}
} }
@@ -213,5 +143,11 @@ namespace TweetDuck.Core.Other.Settings{
} }
} }
} }
private void RestoreParentForm(){
if (ParentForm != null){ // when the parent is closed first, ParentForm is null in FormClosed event
NativeMethods.SetFormDisabled(ParentForm, false);
}
}
} }
} }

View File

@@ -25,71 +25,46 @@
private void InitializeComponent() { private void InitializeComponent() {
this.components = new System.ComponentModel.Container(); this.components = new System.ComponentModel.Container();
this.checkExpandLinks = new System.Windows.Forms.CheckBox(); this.checkExpandLinks = new System.Windows.Forms.CheckBox();
this.comboBoxTrayType = new System.Windows.Forms.ComboBox();
this.toolTip = new System.Windows.Forms.ToolTip(this.components); this.toolTip = new System.Windows.Forms.ToolTip(this.components);
this.checkTrayHighlight = new System.Windows.Forms.CheckBox();
this.checkSpellCheck = new System.Windows.Forms.CheckBox(); this.checkSpellCheck = new System.Windows.Forms.CheckBox();
this.checkUpdateNotifications = new System.Windows.Forms.CheckBox(); this.checkUpdateNotifications = new System.Windows.Forms.CheckBox();
this.btnCheckUpdates = new System.Windows.Forms.Button(); this.btnCheckUpdates = new System.Windows.Forms.Button();
this.labelZoomValue = new System.Windows.Forms.Label(); this.labelZoomValue = new System.Windows.Forms.Label();
this.checkSwitchAccountSelectors = new System.Windows.Forms.CheckBox(); this.checkSwitchAccountSelectors = new System.Windows.Forms.CheckBox();
this.groupTray = new System.Windows.Forms.GroupBox(); this.checkBestImageQuality = new System.Windows.Forms.CheckBox();
this.labelTrayIcon = new System.Windows.Forms.Label(); this.checkOpenSearchInFirstColumn = new System.Windows.Forms.CheckBox();
this.groupInterface = new System.Windows.Forms.GroupBox();
this.trackBarZoom = new System.Windows.Forms.TrackBar(); this.trackBarZoom = new System.Windows.Forms.TrackBar();
this.labelZoom = new System.Windows.Forms.Label(); this.labelZoom = new System.Windows.Forms.Label();
this.groupUpdates = new System.Windows.Forms.GroupBox();
this.zoomUpdateTimer = new System.Windows.Forms.Timer(this.components); this.zoomUpdateTimer = new System.Windows.Forms.Timer(this.components);
this.groupTray.SuspendLayout(); this.labelUI = new System.Windows.Forms.Label();
this.groupInterface.SuspendLayout(); this.panelUI = new System.Windows.Forms.Panel();
this.panelUpdates = new System.Windows.Forms.Panel();
this.labelUpdates = new System.Windows.Forms.Label();
((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).BeginInit();
this.groupUpdates.SuspendLayout(); this.panelUI.SuspendLayout();
this.panelUpdates.SuspendLayout();
this.SuspendLayout(); this.SuspendLayout();
// //
// checkExpandLinks // checkExpandLinks
// //
this.checkExpandLinks.AutoSize = true; this.checkExpandLinks.AutoSize = true;
this.checkExpandLinks.Location = new System.Drawing.Point(9, 21); this.checkExpandLinks.Location = new System.Drawing.Point(6, 5);
this.checkExpandLinks.Margin = new System.Windows.Forms.Padding(6, 5, 3, 3); this.checkExpandLinks.Margin = new System.Windows.Forms.Padding(6, 5, 3, 3);
this.checkExpandLinks.Name = "checkExpandLinks"; this.checkExpandLinks.Name = "checkExpandLinks";
this.checkExpandLinks.Size = new System.Drawing.Size(166, 17); this.checkExpandLinks.Size = new System.Drawing.Size(166, 17);
this.checkExpandLinks.TabIndex = 0; this.checkExpandLinks.TabIndex = 0;
this.checkExpandLinks.Text = "Expand Links When Hovered"; this.checkExpandLinks.Text = "Expand Links When Hovered";
this.toolTip.SetToolTip(this.checkExpandLinks, "Expands links inside the tweets. If disabled,\r\nthe full links show up in a toolti" + this.toolTip.SetToolTip(this.checkExpandLinks, "Expands links inside the tweets. If disabled,\r\nthe full links show up in a tooltip instead.");
"p instead.");
this.checkExpandLinks.UseVisualStyleBackColor = true; this.checkExpandLinks.UseVisualStyleBackColor = true;
// //
// comboBoxTrayType
//
this.comboBoxTrayType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboBoxTrayType.FormattingEnabled = true;
this.comboBoxTrayType.Location = new System.Drawing.Point(6, 19);
this.comboBoxTrayType.Name = "comboBoxTrayType";
this.comboBoxTrayType.Size = new System.Drawing.Size(171, 21);
this.comboBoxTrayType.TabIndex = 0;
this.toolTip.SetToolTip(this.comboBoxTrayType, "Changes behavior of the Tray icon.\r\nRight-click the icon for an action menu.");
//
// checkTrayHighlight
//
this.checkTrayHighlight.AutoSize = true;
this.checkTrayHighlight.Location = new System.Drawing.Point(9, 70);
this.checkTrayHighlight.Margin = new System.Windows.Forms.Padding(6, 5, 3, 3);
this.checkTrayHighlight.Name = "checkTrayHighlight";
this.checkTrayHighlight.Size = new System.Drawing.Size(103, 17);
this.checkTrayHighlight.TabIndex = 2;
this.checkTrayHighlight.Text = "Enable Highlight";
this.toolTip.SetToolTip(this.checkTrayHighlight, "Highlights the tray icon if there are new tweets.\r\nOnly works for columns with po" +
"pup or audio notifications.\r\nThe icon resets when the main window is restored.");
this.checkTrayHighlight.UseVisualStyleBackColor = true;
//
// checkSpellCheck // checkSpellCheck
// //
this.checkSpellCheck.AutoSize = true; this.checkSpellCheck.AutoSize = true;
this.checkSpellCheck.Location = new System.Drawing.Point(9, 67); this.checkSpellCheck.Location = new System.Drawing.Point(6, 97);
this.checkSpellCheck.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3); this.checkSpellCheck.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
this.checkSpellCheck.Name = "checkSpellCheck"; this.checkSpellCheck.Name = "checkSpellCheck";
this.checkSpellCheck.Size = new System.Drawing.Size(119, 17); this.checkSpellCheck.Size = new System.Drawing.Size(119, 17);
this.checkSpellCheck.TabIndex = 2; this.checkSpellCheck.TabIndex = 4;
this.checkSpellCheck.Text = "Enable Spell Check"; this.checkSpellCheck.Text = "Enable Spell Check";
this.toolTip.SetToolTip(this.checkSpellCheck, "Underlines words that are spelled incorrectly."); this.toolTip.SetToolTip(this.checkSpellCheck, "Underlines words that are spelled incorrectly.");
this.checkSpellCheck.UseVisualStyleBackColor = true; this.checkSpellCheck.UseVisualStyleBackColor = true;
@@ -97,21 +72,21 @@
// checkUpdateNotifications // checkUpdateNotifications
// //
this.checkUpdateNotifications.AutoSize = true; this.checkUpdateNotifications.AutoSize = true;
this.checkUpdateNotifications.Location = new System.Drawing.Point(9, 21); this.checkUpdateNotifications.Location = new System.Drawing.Point(6, 5);
this.checkUpdateNotifications.Margin = new System.Windows.Forms.Padding(6, 5, 3, 3); this.checkUpdateNotifications.Margin = new System.Windows.Forms.Padding(6, 5, 3, 3);
this.checkUpdateNotifications.Name = "checkUpdateNotifications"; this.checkUpdateNotifications.Name = "checkUpdateNotifications";
this.checkUpdateNotifications.Size = new System.Drawing.Size(165, 17); this.checkUpdateNotifications.Size = new System.Drawing.Size(165, 17);
this.checkUpdateNotifications.TabIndex = 0; this.checkUpdateNotifications.TabIndex = 0;
this.checkUpdateNotifications.Text = "Check Updates Automatically"; this.checkUpdateNotifications.Text = "Check Updates Automatically";
this.toolTip.SetToolTip(this.checkUpdateNotifications, "Checks for updates every hour.\r\nIf an update is dismissed, it will not appear aga" + this.toolTip.SetToolTip(this.checkUpdateNotifications, "Checks for updates every hour.\r\nIf an update is dismissed, it will not appear again.");
"in.");
this.checkUpdateNotifications.UseVisualStyleBackColor = true; this.checkUpdateNotifications.UseVisualStyleBackColor = true;
// //
// btnCheckUpdates // btnCheckUpdates
// //
this.btnCheckUpdates.Location = new System.Drawing.Point(6, 44); this.btnCheckUpdates.Location = new System.Drawing.Point(5, 28);
this.btnCheckUpdates.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
this.btnCheckUpdates.Name = "btnCheckUpdates"; this.btnCheckUpdates.Name = "btnCheckUpdates";
this.btnCheckUpdates.Size = new System.Drawing.Size(171, 23); this.btnCheckUpdates.Size = new System.Drawing.Size(144, 23);
this.btnCheckUpdates.TabIndex = 1; this.btnCheckUpdates.TabIndex = 1;
this.btnCheckUpdates.Text = "Check Updates Now"; this.btnCheckUpdates.Text = "Check Updates Now";
this.toolTip.SetToolTip(this.btnCheckUpdates, "Forces an update check, even for updates that had been dismissed."); this.toolTip.SetToolTip(this.btnCheckUpdates, "Forces an update check, even for updates that had been dismissed.");
@@ -119,13 +94,12 @@
// //
// labelZoomValue // labelZoomValue
// //
this.labelZoomValue.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.labelZoomValue.BackColor = System.Drawing.Color.Transparent; this.labelZoomValue.BackColor = System.Drawing.Color.Transparent;
this.labelZoomValue.Location = new System.Drawing.Point(139, 116); this.labelZoomValue.Location = new System.Drawing.Point(147, 146);
this.labelZoomValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0); this.labelZoomValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
this.labelZoomValue.Name = "labelZoomValue"; this.labelZoomValue.Name = "labelZoomValue";
this.labelZoomValue.Size = new System.Drawing.Size(38, 13); this.labelZoomValue.Size = new System.Drawing.Size(38, 13);
this.labelZoomValue.TabIndex = 5; this.labelZoomValue.TabIndex = 7;
this.labelZoomValue.Text = "100%"; this.labelZoomValue.Text = "100%";
this.labelZoomValue.TextAlign = System.Drawing.ContentAlignment.TopRight; this.labelZoomValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
this.toolTip.SetToolTip(this.labelZoomValue, "Changes the zoom level.\r\nAlso affects notifications and screenshots."); this.toolTip.SetToolTip(this.labelZoomValue, "Changes the zoom level.\r\nAlso affects notifications and screenshots.");
@@ -133,128 +107,144 @@
// checkSwitchAccountSelectors // checkSwitchAccountSelectors
// //
this.checkSwitchAccountSelectors.AutoSize = true; this.checkSwitchAccountSelectors.AutoSize = true;
this.checkSwitchAccountSelectors.Location = new System.Drawing.Point(9, 44); this.checkSwitchAccountSelectors.Location = new System.Drawing.Point(6, 28);
this.checkSwitchAccountSelectors.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3); this.checkSwitchAccountSelectors.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
this.checkSwitchAccountSelectors.Name = "checkSwitchAccountSelectors"; this.checkSwitchAccountSelectors.Name = "checkSwitchAccountSelectors";
this.checkSwitchAccountSelectors.Size = new System.Drawing.Size(172, 17); this.checkSwitchAccountSelectors.Size = new System.Drawing.Size(172, 17);
this.checkSwitchAccountSelectors.TabIndex = 1; this.checkSwitchAccountSelectors.TabIndex = 1;
this.checkSwitchAccountSelectors.Text = "Shift Selects Multiple Accounts"; this.checkSwitchAccountSelectors.Text = "Shift Selects Multiple Accounts";
this.toolTip.SetToolTip(this.checkSwitchAccountSelectors, "When (re)tweeting, click to select a single account or hold Shift to\r\nselect mult" + this.toolTip.SetToolTip(this.checkSwitchAccountSelectors, "When (re)tweeting, click to select a single account or hold Shift to\r\nselect multiple accounts, instead of TweetDeck\'s default behavior.");
"iple accounts, instead of TweetDeck\'s default behavior.");
this.checkSwitchAccountSelectors.UseVisualStyleBackColor = true; this.checkSwitchAccountSelectors.UseVisualStyleBackColor = true;
// //
// groupTray // checkBestImageQuality
// //
this.groupTray.Controls.Add(this.checkTrayHighlight); this.checkBestImageQuality.AutoSize = true;
this.groupTray.Controls.Add(this.labelTrayIcon); this.checkBestImageQuality.Location = new System.Drawing.Point(6, 74);
this.groupTray.Controls.Add(this.comboBoxTrayType); this.checkBestImageQuality.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
this.groupTray.Location = new System.Drawing.Point(9, 168); this.checkBestImageQuality.Name = "checkBestImageQuality";
this.groupTray.Name = "groupTray"; this.checkBestImageQuality.Size = new System.Drawing.Size(114, 17);
this.groupTray.Size = new System.Drawing.Size(183, 93); this.checkBestImageQuality.TabIndex = 3;
this.groupTray.TabIndex = 1; this.checkBestImageQuality.Text = "Best Image Quality";
this.groupTray.TabStop = false; this.toolTip.SetToolTip(this.checkBestImageQuality, "When right-clicking a tweet image, the context menu options\r\nwill use links to the original image size (:orig in the URL).");
this.groupTray.Text = "System Tray"; this.checkBestImageQuality.UseVisualStyleBackColor = true;
// //
// labelTrayIcon // checkOpenSearchInFirstColumn
// //
this.labelTrayIcon.AutoSize = true; this.checkOpenSearchInFirstColumn.AutoSize = true;
this.labelTrayIcon.Location = new System.Drawing.Point(6, 52); this.checkOpenSearchInFirstColumn.Location = new System.Drawing.Point(6, 51);
this.labelTrayIcon.Margin = new System.Windows.Forms.Padding(3, 9, 3, 0); this.checkOpenSearchInFirstColumn.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
this.labelTrayIcon.Name = "labelTrayIcon"; this.checkOpenSearchInFirstColumn.Name = "checkOpenSearchInFirstColumn";
this.labelTrayIcon.Size = new System.Drawing.Size(52, 13); this.checkOpenSearchInFirstColumn.Size = new System.Drawing.Size(219, 17);
this.labelTrayIcon.TabIndex = 1; this.checkOpenSearchInFirstColumn.TabIndex = 2;
this.labelTrayIcon.Text = "Tray Icon"; this.checkOpenSearchInFirstColumn.Text = "Add Search Columns Before First Column";
// this.toolTip.SetToolTip(this.checkOpenSearchInFirstColumn, "By default, TweetDeck adds Search columns at the end.\r\nThis option makes them appear before the first column instead.");
// groupInterface this.checkOpenSearchInFirstColumn.UseVisualStyleBackColor = true;
//
this.groupInterface.Controls.Add(this.checkSwitchAccountSelectors);
this.groupInterface.Controls.Add(this.labelZoomValue);
this.groupInterface.Controls.Add(this.trackBarZoom);
this.groupInterface.Controls.Add(this.labelZoom);
this.groupInterface.Controls.Add(this.checkSpellCheck);
this.groupInterface.Controls.Add(this.checkExpandLinks);
this.groupInterface.Location = new System.Drawing.Point(9, 9);
this.groupInterface.Name = "groupInterface";
this.groupInterface.Size = new System.Drawing.Size(183, 153);
this.groupInterface.TabIndex = 0;
this.groupInterface.TabStop = false;
this.groupInterface.Text = "User Interface";
// //
// trackBarZoom // trackBarZoom
// //
this.trackBarZoom.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.trackBarZoom.AutoSize = false; this.trackBarZoom.AutoSize = false;
this.trackBarZoom.BackColor = System.Drawing.SystemColors.Control;
this.trackBarZoom.LargeChange = 25; this.trackBarZoom.LargeChange = 25;
this.trackBarZoom.Location = new System.Drawing.Point(6, 115); this.trackBarZoom.Location = new System.Drawing.Point(3, 145);
this.trackBarZoom.Maximum = 200; this.trackBarZoom.Maximum = 200;
this.trackBarZoom.Minimum = 50; this.trackBarZoom.Minimum = 50;
this.trackBarZoom.Name = "trackBarZoom"; this.trackBarZoom.Name = "trackBarZoom";
this.trackBarZoom.Size = new System.Drawing.Size(141, 30); this.trackBarZoom.Size = new System.Drawing.Size(148, 30);
this.trackBarZoom.SmallChange = 5; this.trackBarZoom.SmallChange = 5;
this.trackBarZoom.TabIndex = 4; this.trackBarZoom.TabIndex = 6;
this.trackBarZoom.TickFrequency = 25; this.trackBarZoom.TickFrequency = 25;
this.trackBarZoom.Value = 100; this.trackBarZoom.Value = 100;
// //
// labelZoom // labelZoom
// //
this.labelZoom.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.labelZoom.AutoSize = true; this.labelZoom.AutoSize = true;
this.labelZoom.Location = new System.Drawing.Point(5, 99); this.labelZoom.Location = new System.Drawing.Point(3, 129);
this.labelZoom.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); this.labelZoom.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.labelZoom.Name = "labelZoom"; this.labelZoom.Name = "labelZoom";
this.labelZoom.Size = new System.Drawing.Size(34, 13); this.labelZoom.Size = new System.Drawing.Size(34, 13);
this.labelZoom.TabIndex = 3; this.labelZoom.TabIndex = 5;
this.labelZoom.Text = "Zoom"; this.labelZoom.Text = "Zoom";
// //
// groupUpdates
//
this.groupUpdates.Controls.Add(this.checkUpdateNotifications);
this.groupUpdates.Controls.Add(this.btnCheckUpdates);
this.groupUpdates.Location = new System.Drawing.Point(198, 9);
this.groupUpdates.Name = "groupUpdates";
this.groupUpdates.Size = new System.Drawing.Size(183, 75);
this.groupUpdates.TabIndex = 2;
this.groupUpdates.TabStop = false;
this.groupUpdates.Text = "Updates";
//
// zoomUpdateTimer // zoomUpdateTimer
// //
this.zoomUpdateTimer.Interval = 250; this.zoomUpdateTimer.Interval = 250;
this.zoomUpdateTimer.Tick += new System.EventHandler(this.zoomUpdateTimer_Tick); this.zoomUpdateTimer.Tick += new System.EventHandler(this.zoomUpdateTimer_Tick);
// //
// labelUI
//
this.labelUI.AutoSize = true;
this.labelUI.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelUI.Location = new System.Drawing.Point(6, 8);
this.labelUI.Margin = new System.Windows.Forms.Padding(0, 2, 0, 0);
this.labelUI.Name = "labelUI";
this.labelUI.Size = new System.Drawing.Size(111, 20);
this.labelUI.TabIndex = 0;
this.labelUI.Text = "User Interface";
//
// panelUI
//
this.panelUI.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.panelUI.Controls.Add(this.checkOpenSearchInFirstColumn);
this.panelUI.Controls.Add(this.checkBestImageQuality);
this.panelUI.Controls.Add(this.checkExpandLinks);
this.panelUI.Controls.Add(this.checkSwitchAccountSelectors);
this.panelUI.Controls.Add(this.checkSpellCheck);
this.panelUI.Controls.Add(this.labelZoom);
this.panelUI.Controls.Add(this.trackBarZoom);
this.panelUI.Controls.Add(this.labelZoomValue);
this.panelUI.Location = new System.Drawing.Point(9, 31);
this.panelUI.Name = "panelUI";
this.panelUI.Size = new System.Drawing.Size(322, 179);
this.panelUI.TabIndex = 1;
//
// panelUpdates
//
this.panelUpdates.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.panelUpdates.Controls.Add(this.checkUpdateNotifications);
this.panelUpdates.Controls.Add(this.btnCheckUpdates);
this.panelUpdates.Location = new System.Drawing.Point(9, 257);
this.panelUpdates.Name = "panelUpdates";
this.panelUpdates.Size = new System.Drawing.Size(322, 55);
this.panelUpdates.TabIndex = 3;
//
// labelUpdates
//
this.labelUpdates.AutoSize = true;
this.labelUpdates.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelUpdates.Location = new System.Drawing.Point(6, 234);
this.labelUpdates.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0);
this.labelUpdates.Name = "labelUpdates";
this.labelUpdates.Size = new System.Drawing.Size(70, 20);
this.labelUpdates.TabIndex = 2;
this.labelUpdates.Text = "Updates";
//
// TabSettingsGeneral // TabSettingsGeneral
// //
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.groupUpdates); this.Controls.Add(this.labelUpdates);
this.Controls.Add(this.groupInterface); this.Controls.Add(this.panelUpdates);
this.Controls.Add(this.groupTray); this.Controls.Add(this.panelUI);
this.Controls.Add(this.labelUI);
this.Name = "TabSettingsGeneral"; this.Name = "TabSettingsGeneral";
this.Size = new System.Drawing.Size(478, 300); this.Size = new System.Drawing.Size(340, 322);
this.groupTray.ResumeLayout(false);
this.groupTray.PerformLayout();
this.groupInterface.ResumeLayout(false);
this.groupInterface.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).EndInit();
this.groupUpdates.ResumeLayout(false); this.panelUI.ResumeLayout(false);
this.groupUpdates.PerformLayout(); this.panelUI.PerformLayout();
this.panelUpdates.ResumeLayout(false);
this.panelUpdates.PerformLayout();
this.ResumeLayout(false); this.ResumeLayout(false);
this.PerformLayout();
} }
#endregion #endregion
private System.Windows.Forms.CheckBox checkExpandLinks; private System.Windows.Forms.CheckBox checkExpandLinks;
private System.Windows.Forms.ComboBox comboBoxTrayType;
private System.Windows.Forms.ToolTip toolTip; private System.Windows.Forms.ToolTip toolTip;
private System.Windows.Forms.GroupBox groupTray;
private System.Windows.Forms.GroupBox groupInterface;
private System.Windows.Forms.Label labelTrayIcon;
private System.Windows.Forms.CheckBox checkTrayHighlight;
private System.Windows.Forms.CheckBox checkSpellCheck; private System.Windows.Forms.CheckBox checkSpellCheck;
private System.Windows.Forms.GroupBox groupUpdates;
private System.Windows.Forms.CheckBox checkUpdateNotifications; private System.Windows.Forms.CheckBox checkUpdateNotifications;
private System.Windows.Forms.Button btnCheckUpdates; private System.Windows.Forms.Button btnCheckUpdates;
private System.Windows.Forms.Label labelZoom; private System.Windows.Forms.Label labelZoom;
@@ -262,5 +252,11 @@
private System.Windows.Forms.TrackBar trackBarZoom; private System.Windows.Forms.TrackBar trackBarZoom;
private System.Windows.Forms.Timer zoomUpdateTimer; private System.Windows.Forms.Timer zoomUpdateTimer;
private System.Windows.Forms.CheckBox checkSwitchAccountSelectors; private System.Windows.Forms.CheckBox checkSwitchAccountSelectors;
private System.Windows.Forms.Label labelUI;
private System.Windows.Forms.Panel panelUI;
private System.Windows.Forms.Panel panelUpdates;
private System.Windows.Forms.Label labelUpdates;
private System.Windows.Forms.CheckBox checkBestImageQuality;
private System.Windows.Forms.CheckBox checkOpenSearchInFirstColumn;
} }
} }

View File

@@ -1,11 +1,9 @@
using System; using System;
using System.Windows.Forms;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Updates; using TweetDuck.Updates;
using TweetDuck.Updates.Events;
namespace TweetDuck.Core.Other.Settings{ namespace TweetDuck.Core.Other.Settings{
partial class TabSettingsGeneral : BaseTabSettings{ sealed partial class TabSettingsGeneral : BaseTabSettings{
private readonly UpdateHandler updates; private readonly UpdateHandler updates;
private int updateCheckEventId = -1; private int updateCheckEventId = -1;
@@ -16,21 +14,15 @@ namespace TweetDuck.Core.Other.Settings{
this.updates.CheckFinished += updates_CheckFinished; this.updates.CheckFinished += updates_CheckFinished;
Disposed += (sender, args) => this.updates.CheckFinished -= updates_CheckFinished; Disposed += (sender, args) => this.updates.CheckFinished -= updates_CheckFinished;
comboBoxTrayType.Items.Add("Disabled");
comboBoxTrayType.Items.Add("Display Icon Only");
comboBoxTrayType.Items.Add("Minimize to Tray");
comboBoxTrayType.Items.Add("Close to Tray");
comboBoxTrayType.Items.Add("Combined");
comboBoxTrayType.SelectedIndex = Math.Min(Math.Max((int)Config.TrayBehavior, 0), comboBoxTrayType.Items.Count-1);
toolTip.SetToolTip(trackBarZoom, toolTip.GetToolTip(labelZoomValue)); toolTip.SetToolTip(trackBarZoom, toolTip.GetToolTip(labelZoomValue));
trackBarZoom.SetValueSafe(Config.ZoomLevel); trackBarZoom.SetValueSafe(Config.ZoomLevel);
labelZoomValue.Text = trackBarZoom.Value+"%"; labelZoomValue.Text = trackBarZoom.Value+"%";
checkExpandLinks.Checked = Config.ExpandLinksOnHover; checkExpandLinks.Checked = Config.ExpandLinksOnHover;
checkSwitchAccountSelectors.Checked = Config.SwitchAccountSelectors; checkSwitchAccountSelectors.Checked = Config.SwitchAccountSelectors;
checkOpenSearchInFirstColumn.Checked = Config.OpenSearchInFirstColumn;
checkBestImageQuality.Checked = Config.BestImageQuality;
checkSpellCheck.Checked = Config.EnableSpellCheck; checkSpellCheck.Checked = Config.EnableSpellCheck;
checkTrayHighlight.Checked = Config.EnableTrayHighlight;
checkUpdateNotifications.Checked = Config.EnableUpdateCheck; checkUpdateNotifications.Checked = Config.EnableUpdateCheck;
} }
@@ -38,12 +30,11 @@ namespace TweetDuck.Core.Other.Settings{
public override void OnReady(){ public override void OnReady(){
checkExpandLinks.CheckedChanged += checkExpandLinks_CheckedChanged; checkExpandLinks.CheckedChanged += checkExpandLinks_CheckedChanged;
checkSwitchAccountSelectors.CheckedChanged += checkSwitchAccountSelectors_CheckedChanged; checkSwitchAccountSelectors.CheckedChanged += checkSwitchAccountSelectors_CheckedChanged;
checkOpenSearchInFirstColumn.CheckedChanged += checkOpenSearchInFirstColumn_CheckedChanged;
checkBestImageQuality.CheckedChanged += checkBestImageQuality_CheckedChanged;
checkSpellCheck.CheckedChanged += checkSpellCheck_CheckedChanged; checkSpellCheck.CheckedChanged += checkSpellCheck_CheckedChanged;
trackBarZoom.ValueChanged += trackBarZoom_ValueChanged; trackBarZoom.ValueChanged += trackBarZoom_ValueChanged;
comboBoxTrayType.SelectedIndexChanged += comboBoxTrayType_SelectedIndexChanged;
checkTrayHighlight.CheckedChanged += checkTrayHighlight_CheckedChanged;
checkUpdateNotifications.CheckedChanged += checkUpdateNotifications_CheckedChanged; checkUpdateNotifications.CheckedChanged += checkUpdateNotifications_CheckedChanged;
btnCheckUpdates.Click += btnCheckUpdates_Click; btnCheckUpdates.Click += btnCheckUpdates_Click;
} }
@@ -60,54 +51,48 @@ namespace TweetDuck.Core.Other.Settings{
Config.SwitchAccountSelectors = checkSwitchAccountSelectors.Checked; Config.SwitchAccountSelectors = checkSwitchAccountSelectors.Checked;
} }
private void checkOpenSearchInFirstColumn_CheckedChanged(object sender, EventArgs e){
Config.OpenSearchInFirstColumn = checkOpenSearchInFirstColumn.Checked;
}
private void checkBestImageQuality_CheckedChanged(object sender, EventArgs e){
Config.BestImageQuality = checkBestImageQuality.Checked;
}
private void checkSpellCheck_CheckedChanged(object sender, EventArgs e){ private void checkSpellCheck_CheckedChanged(object sender, EventArgs e){
Config.EnableSpellCheck = checkSpellCheck.Checked; Config.EnableSpellCheck = checkSpellCheck.Checked;
PromptRestart(); PromptRestart();
} }
private void trackBarZoom_ValueChanged(object sender, EventArgs e){ private void trackBarZoom_ValueChanged(object sender, EventArgs e){
if (trackBarZoom.Value % trackBarZoom.SmallChange != 0){ if (trackBarZoom.AlignValueToTick()){
trackBarZoom.Value = trackBarZoom.SmallChange*(int)Math.Floor(((double)trackBarZoom.Value/trackBarZoom.SmallChange)+0.5);
}
else{
zoomUpdateTimer.Stop(); zoomUpdateTimer.Stop();
zoomUpdateTimer.Start(); zoomUpdateTimer.Start();
labelZoomValue.Text = trackBarZoom.Value+"%"; labelZoomValue.Text = trackBarZoom.Value+"%";
} }
} }
private void comboBoxTrayType_SelectedIndexChanged(object sender, EventArgs e){
Config.TrayBehavior = (TrayIcon.Behavior)comboBoxTrayType.SelectedIndex;
}
private void checkTrayHighlight_CheckedChanged(object sender, EventArgs e){
Config.EnableTrayHighlight = checkTrayHighlight.Checked;
}
private void checkUpdateNotifications_CheckedChanged(object sender, EventArgs e){ private void checkUpdateNotifications_CheckedChanged(object sender, EventArgs e){
Config.EnableUpdateCheck = checkUpdateNotifications.Checked; Config.EnableUpdateCheck = checkUpdateNotifications.Checked;
} }
private void btnCheckUpdates_Click(object sender, EventArgs e){ private void btnCheckUpdates_Click(object sender, EventArgs e){
updateCheckEventId = updates.Check(true); Config.DismissedUpdate = null;
if (updateCheckEventId == -1){
MessageBox.Show("Sorry, your system is no longer supported.", "Unsupported System", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
else{
btnCheckUpdates.Enabled = false; btnCheckUpdates.Enabled = false;
updates.DismissUpdate(string.Empty); updateCheckEventId = updates.Check(true);
}
} }
private void updates_CheckFinished(object sender, UpdateCheckEventArgs e){ private void updates_CheckFinished(object sender, UpdateEventArgs e){
this.InvokeAsyncSafe(() => {
if (e.EventId == updateCheckEventId){ if (e.EventId == updateCheckEventId){
btnCheckUpdates.Enabled = true; btnCheckUpdates.Enabled = true;
if (!e.UpdateAvailable){ if (!e.IsUpdateAvailable){
MessageBox.Show("Your version of "+Program.BrandName+" is up to date.", "No Updates Available", MessageBoxButtons.OK, MessageBoxIcon.Information); FormMessage.Information("No Updates Available", "Your version of TweetDuck is up to date.", FormMessage.OK);
} }
} }
});
} }
private void zoomUpdateTimer_Tick(object sender, EventArgs e){ private void zoomUpdateTimer_Tick(object sender, EventArgs e){

View File

@@ -24,7 +24,6 @@
/// </summary> /// </summary>
private void InitializeComponent() { private void InitializeComponent() {
this.components = new System.ComponentModel.Container(); this.components = new System.ComponentModel.Container();
this.groupNotificationLocation = new System.Windows.Forms.GroupBox();
this.labelEdgeDistanceValue = new System.Windows.Forms.Label(); this.labelEdgeDistanceValue = new System.Windows.Forms.Label();
this.labelDisplay = new System.Windows.Forms.Label(); this.labelDisplay = new System.Windows.Forms.Label();
this.comboBoxDisplay = new System.Windows.Forms.ComboBox(); this.comboBoxDisplay = new System.Windows.Forms.ComboBox();
@@ -35,14 +34,12 @@
this.radioLocTR = new System.Windows.Forms.RadioButton(); this.radioLocTR = new System.Windows.Forms.RadioButton();
this.radioLocTL = new System.Windows.Forms.RadioButton(); this.radioLocTL = new System.Windows.Forms.RadioButton();
this.trackBarEdgeDistance = new System.Windows.Forms.TrackBar(); this.trackBarEdgeDistance = new System.Windows.Forms.TrackBar();
this.groupNotificationDuration = new System.Windows.Forms.GroupBox();
this.tableLayoutDurationButtons = new System.Windows.Forms.TableLayoutPanel(); this.tableLayoutDurationButtons = new System.Windows.Forms.TableLayoutPanel();
this.btnDurationMedium = new TweetDuck.Core.Controls.FlatButton(); this.btnDurationMedium = new TweetDuck.Core.Controls.FlatButton();
this.btnDurationLong = new TweetDuck.Core.Controls.FlatButton(); this.btnDurationLong = new TweetDuck.Core.Controls.FlatButton();
this.btnDurationShort = new TweetDuck.Core.Controls.FlatButton(); this.btnDurationShort = new TweetDuck.Core.Controls.FlatButton();
this.labelDurationValue = new System.Windows.Forms.Label(); this.labelDurationValue = new System.Windows.Forms.Label();
this.trackBarDuration = new System.Windows.Forms.TrackBar(); this.trackBarDuration = new System.Windows.Forms.TrackBar();
this.groupUserInterface = new System.Windows.Forms.GroupBox();
this.checkSkipOnLinkClick = new System.Windows.Forms.CheckBox(); this.checkSkipOnLinkClick = new System.Windows.Forms.CheckBox();
this.checkColumnName = new System.Windows.Forms.CheckBox(); this.checkColumnName = new System.Windows.Forms.CheckBox();
this.labelIdlePause = new System.Windows.Forms.Label(); this.labelIdlePause = new System.Windows.Forms.Label();
@@ -51,40 +48,38 @@
this.checkTimerCountDown = new System.Windows.Forms.CheckBox(); this.checkTimerCountDown = new System.Windows.Forms.CheckBox();
this.checkNotificationTimer = new System.Windows.Forms.CheckBox(); this.checkNotificationTimer = new System.Windows.Forms.CheckBox();
this.toolTip = new System.Windows.Forms.ToolTip(this.components); this.toolTip = new System.Windows.Forms.ToolTip(this.components);
this.groupNotificationLocation.SuspendLayout(); this.radioSizeAuto = new System.Windows.Forms.RadioButton();
this.radioSizeCustom = new System.Windows.Forms.RadioButton();
this.labelGeneral = new System.Windows.Forms.Label();
this.panelGeneral = new System.Windows.Forms.Panel();
this.labelScrollSpeedValue = new System.Windows.Forms.Label();
this.trackBarScrollSpeed = new System.Windows.Forms.TrackBar();
this.labelScrollSpeed = new System.Windows.Forms.Label();
this.labelLocation = new System.Windows.Forms.Label();
this.panelLocation = new System.Windows.Forms.Panel();
this.panelTimer = new System.Windows.Forms.Panel();
this.labelDuration = new System.Windows.Forms.Label();
this.labelTimer = new System.Windows.Forms.Label();
this.labelSize = new System.Windows.Forms.Label();
this.panelMiscellaneous = new System.Windows.Forms.Panel();
this.durationUpdateTimer = new System.Windows.Forms.Timer(this.components);
this.checkMediaPreviews = new System.Windows.Forms.CheckBox();
((System.ComponentModel.ISupportInitialize)(this.trackBarEdgeDistance)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.trackBarEdgeDistance)).BeginInit();
this.groupNotificationDuration.SuspendLayout();
this.tableLayoutDurationButtons.SuspendLayout(); this.tableLayoutDurationButtons.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.trackBarDuration)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.trackBarDuration)).BeginInit();
this.groupUserInterface.SuspendLayout(); this.panelGeneral.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.trackBarScrollSpeed)).BeginInit();
this.panelLocation.SuspendLayout();
this.panelTimer.SuspendLayout();
this.panelMiscellaneous.SuspendLayout();
this.SuspendLayout(); this.SuspendLayout();
// //
// groupNotificationLocation
//
this.groupNotificationLocation.Controls.Add(this.labelEdgeDistanceValue);
this.groupNotificationLocation.Controls.Add(this.labelDisplay);
this.groupNotificationLocation.Controls.Add(this.comboBoxDisplay);
this.groupNotificationLocation.Controls.Add(this.labelEdgeDistance);
this.groupNotificationLocation.Controls.Add(this.radioLocCustom);
this.groupNotificationLocation.Controls.Add(this.radioLocBR);
this.groupNotificationLocation.Controls.Add(this.radioLocBL);
this.groupNotificationLocation.Controls.Add(this.radioLocTR);
this.groupNotificationLocation.Controls.Add(this.radioLocTL);
this.groupNotificationLocation.Controls.Add(this.trackBarEdgeDistance);
this.groupNotificationLocation.Location = new System.Drawing.Point(198, 9);
this.groupNotificationLocation.Name = "groupNotificationLocation";
this.groupNotificationLocation.Size = new System.Drawing.Size(183, 282);
this.groupNotificationLocation.TabIndex = 2;
this.groupNotificationLocation.TabStop = false;
this.groupNotificationLocation.Text = "Location";
//
// labelEdgeDistanceValue // labelEdgeDistanceValue
// //
this.labelEdgeDistanceValue.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.labelEdgeDistanceValue.Location = new System.Drawing.Point(147, 129);
this.labelEdgeDistanceValue.Location = new System.Drawing.Point(143, 217);
this.labelEdgeDistanceValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0); this.labelEdgeDistanceValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
this.labelEdgeDistanceValue.Name = "labelEdgeDistanceValue"; this.labelEdgeDistanceValue.Name = "labelEdgeDistanceValue";
this.labelEdgeDistanceValue.Size = new System.Drawing.Size(34, 13); this.labelEdgeDistanceValue.Size = new System.Drawing.Size(40, 13);
this.labelEdgeDistanceValue.TabIndex = 9; this.labelEdgeDistanceValue.TabIndex = 9;
this.labelEdgeDistanceValue.Text = "0 px"; this.labelEdgeDistanceValue.Text = "0 px";
this.labelEdgeDistanceValue.TextAlign = System.Drawing.ContentAlignment.TopRight; this.labelEdgeDistanceValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
@@ -92,7 +87,7 @@
// labelDisplay // labelDisplay
// //
this.labelDisplay.AutoSize = true; this.labelDisplay.AutoSize = true;
this.labelDisplay.Location = new System.Drawing.Point(5, 144); this.labelDisplay.Location = new System.Drawing.Point(3, 60);
this.labelDisplay.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); this.labelDisplay.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.labelDisplay.Name = "labelDisplay"; this.labelDisplay.Name = "labelDisplay";
this.labelDisplay.Size = new System.Drawing.Size(41, 13); this.labelDisplay.Size = new System.Drawing.Size(41, 13);
@@ -105,15 +100,16 @@
| System.Windows.Forms.AnchorStyles.Right))); | System.Windows.Forms.AnchorStyles.Right)));
this.comboBoxDisplay.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboBoxDisplay.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboBoxDisplay.FormattingEnabled = true; this.comboBoxDisplay.FormattingEnabled = true;
this.comboBoxDisplay.Location = new System.Drawing.Point(6, 160); this.comboBoxDisplay.Location = new System.Drawing.Point(5, 76);
this.comboBoxDisplay.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
this.comboBoxDisplay.Name = "comboBoxDisplay"; this.comboBoxDisplay.Name = "comboBoxDisplay";
this.comboBoxDisplay.Size = new System.Drawing.Size(171, 21); this.comboBoxDisplay.Size = new System.Drawing.Size(144, 21);
this.comboBoxDisplay.TabIndex = 6; this.comboBoxDisplay.TabIndex = 6;
// //
// labelEdgeDistance // labelEdgeDistance
// //
this.labelEdgeDistance.AutoSize = true; this.labelEdgeDistance.AutoSize = true;
this.labelEdgeDistance.Location = new System.Drawing.Point(5, 196); this.labelEdgeDistance.Location = new System.Drawing.Point(3, 112);
this.labelEdgeDistance.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); this.labelEdgeDistance.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.labelEdgeDistance.Name = "labelEdgeDistance"; this.labelEdgeDistance.Name = "labelEdgeDistance";
this.labelEdgeDistance.Size = new System.Drawing.Size(103, 13); this.labelEdgeDistance.Size = new System.Drawing.Size(103, 13);
@@ -122,24 +118,22 @@
// //
// radioLocCustom // radioLocCustom
// //
this.radioLocCustom.AutoSize = true; this.radioLocCustom.Location = new System.Drawing.Point(205, 4);
this.radioLocCustom.Location = new System.Drawing.Point(8, 112); this.radioLocCustom.Margin = new System.Windows.Forms.Padding(5, 4, 3, 3);
this.radioLocCustom.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
this.radioLocCustom.Name = "radioLocCustom"; this.radioLocCustom.Name = "radioLocCustom";
this.radioLocCustom.Size = new System.Drawing.Size(60, 17); this.radioLocCustom.Size = new System.Drawing.Size(65, 41);
this.radioLocCustom.TabIndex = 4; this.radioLocCustom.TabIndex = 4;
this.radioLocCustom.TabStop = true; this.radioLocCustom.TabStop = true;
this.radioLocCustom.Text = "Custom"; this.radioLocCustom.Text = "Custom";
this.toolTip.SetToolTip(this.radioLocCustom, "Drag the notification window to the desired location."); this.toolTip.SetToolTip(this.radioLocCustom, "Drag the example notification window to the desired location.");
this.radioLocCustom.UseVisualStyleBackColor = true; this.radioLocCustom.UseVisualStyleBackColor = true;
// //
// radioLocBR // radioLocBR
// //
this.radioLocBR.AutoSize = true; this.radioLocBR.Location = new System.Drawing.Point(105, 28);
this.radioLocBR.Location = new System.Drawing.Point(8, 89); this.radioLocBR.Margin = new System.Windows.Forms.Padding(5, 4, 3, 3);
this.radioLocBR.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
this.radioLocBR.Name = "radioLocBR"; this.radioLocBR.Name = "radioLocBR";
this.radioLocBR.Size = new System.Drawing.Size(86, 17); this.radioLocBR.Size = new System.Drawing.Size(92, 17);
this.radioLocBR.TabIndex = 3; this.radioLocBR.TabIndex = 3;
this.radioLocBR.TabStop = true; this.radioLocBR.TabStop = true;
this.radioLocBR.Text = "Bottom Right"; this.radioLocBR.Text = "Bottom Right";
@@ -147,11 +141,10 @@
// //
// radioLocBL // radioLocBL
// //
this.radioLocBL.AutoSize = true; this.radioLocBL.Location = new System.Drawing.Point(5, 28);
this.radioLocBL.Location = new System.Drawing.Point(8, 66); this.radioLocBL.Margin = new System.Windows.Forms.Padding(5, 4, 3, 3);
this.radioLocBL.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
this.radioLocBL.Name = "radioLocBL"; this.radioLocBL.Name = "radioLocBL";
this.radioLocBL.Size = new System.Drawing.Size(79, 17); this.radioLocBL.Size = new System.Drawing.Size(92, 17);
this.radioLocBL.TabIndex = 2; this.radioLocBL.TabIndex = 2;
this.radioLocBL.TabStop = true; this.radioLocBL.TabStop = true;
this.radioLocBL.Text = "Bottom Left"; this.radioLocBL.Text = "Bottom Left";
@@ -159,11 +152,10 @@
// //
// radioLocTR // radioLocTR
// //
this.radioLocTR.AutoSize = true; this.radioLocTR.Location = new System.Drawing.Point(105, 4);
this.radioLocTR.Location = new System.Drawing.Point(8, 43); this.radioLocTR.Margin = new System.Windows.Forms.Padding(5, 4, 3, 3);
this.radioLocTR.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
this.radioLocTR.Name = "radioLocTR"; this.radioLocTR.Name = "radioLocTR";
this.radioLocTR.Size = new System.Drawing.Size(72, 17); this.radioLocTR.Size = new System.Drawing.Size(92, 17);
this.radioLocTR.TabIndex = 1; this.radioLocTR.TabIndex = 1;
this.radioLocTR.TabStop = true; this.radioLocTR.TabStop = true;
this.radioLocTR.Text = "Top Right"; this.radioLocTR.Text = "Top Right";
@@ -171,11 +163,10 @@
// //
// radioLocTL // radioLocTL
// //
this.radioLocTL.AutoSize = true; this.radioLocTL.Location = new System.Drawing.Point(5, 4);
this.radioLocTL.Location = new System.Drawing.Point(8, 20);
this.radioLocTL.Margin = new System.Windows.Forms.Padding(5, 4, 3, 3); this.radioLocTL.Margin = new System.Windows.Forms.Padding(5, 4, 3, 3);
this.radioLocTL.Name = "radioLocTL"; this.radioLocTL.Name = "radioLocTL";
this.radioLocTL.Size = new System.Drawing.Size(65, 17); this.radioLocTL.Size = new System.Drawing.Size(92, 17);
this.radioLocTL.TabIndex = 0; this.radioLocTL.TabIndex = 0;
this.radioLocTL.TabStop = true; this.radioLocTL.TabStop = true;
this.radioLocTL.Text = "Top Left"; this.radioLocTL.Text = "Top Left";
@@ -183,36 +174,20 @@
// //
// trackBarEdgeDistance // trackBarEdgeDistance
// //
this.trackBarEdgeDistance.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.trackBarEdgeDistance.AutoSize = false; this.trackBarEdgeDistance.AutoSize = false;
this.trackBarEdgeDistance.LargeChange = 8; this.trackBarEdgeDistance.LargeChange = 8;
this.trackBarEdgeDistance.Location = new System.Drawing.Point(8, 212); this.trackBarEdgeDistance.Location = new System.Drawing.Point(5, 128);
this.trackBarEdgeDistance.Maximum = 40; this.trackBarEdgeDistance.Maximum = 40;
this.trackBarEdgeDistance.Minimum = 8; this.trackBarEdgeDistance.Minimum = 8;
this.trackBarEdgeDistance.Name = "trackBarEdgeDistance"; this.trackBarEdgeDistance.Name = "trackBarEdgeDistance";
this.trackBarEdgeDistance.Size = new System.Drawing.Size(141, 30); this.trackBarEdgeDistance.Size = new System.Drawing.Size(148, 30);
this.trackBarEdgeDistance.SmallChange = 2; this.trackBarEdgeDistance.SmallChange = 2;
this.trackBarEdgeDistance.TabIndex = 8; this.trackBarEdgeDistance.TabIndex = 8;
this.trackBarEdgeDistance.TickFrequency = 4; this.trackBarEdgeDistance.TickFrequency = 4;
this.trackBarEdgeDistance.Value = 8; this.trackBarEdgeDistance.Value = 8;
// //
// groupNotificationDuration
//
this.groupNotificationDuration.Controls.Add(this.tableLayoutDurationButtons);
this.groupNotificationDuration.Controls.Add(this.labelDurationValue);
this.groupNotificationDuration.Controls.Add(this.trackBarDuration);
this.groupNotificationDuration.Location = new System.Drawing.Point(9, 202);
this.groupNotificationDuration.Name = "groupNotificationDuration";
this.groupNotificationDuration.Size = new System.Drawing.Size(183, 89);
this.groupNotificationDuration.TabIndex = 1;
this.groupNotificationDuration.TabStop = false;
this.groupNotificationDuration.Text = "Duration";
//
// tableLayoutDurationButtons // tableLayoutDurationButtons
// //
this.tableLayoutDurationButtons.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.tableLayoutDurationButtons.ColumnCount = 3; this.tableLayoutDurationButtons.ColumnCount = 3;
this.tableLayoutDurationButtons.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 32F)); this.tableLayoutDurationButtons.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 32F));
this.tableLayoutDurationButtons.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 36F)); this.tableLayoutDurationButtons.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 36F));
@@ -220,12 +195,12 @@
this.tableLayoutDurationButtons.Controls.Add(this.btnDurationMedium, 0, 0); this.tableLayoutDurationButtons.Controls.Add(this.btnDurationMedium, 0, 0);
this.tableLayoutDurationButtons.Controls.Add(this.btnDurationLong, 1, 0); this.tableLayoutDurationButtons.Controls.Add(this.btnDurationLong, 1, 0);
this.tableLayoutDurationButtons.Controls.Add(this.btnDurationShort, 0, 0); this.tableLayoutDurationButtons.Controls.Add(this.btnDurationShort, 0, 0);
this.tableLayoutDurationButtons.Location = new System.Drawing.Point(6, 56); this.tableLayoutDurationButtons.Location = new System.Drawing.Point(3, 113);
this.tableLayoutDurationButtons.Name = "tableLayoutDurationButtons"; this.tableLayoutDurationButtons.Name = "tableLayoutDurationButtons";
this.tableLayoutDurationButtons.RowCount = 1; this.tableLayoutDurationButtons.RowCount = 1;
this.tableLayoutDurationButtons.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tableLayoutDurationButtons.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutDurationButtons.Size = new System.Drawing.Size(171, 27); this.tableLayoutDurationButtons.Size = new System.Drawing.Size(171, 27);
this.tableLayoutDurationButtons.TabIndex = 2; this.tableLayoutDurationButtons.TabIndex = 5;
// //
// btnDurationMedium // btnDurationMedium
// //
@@ -274,55 +249,36 @@
// //
// labelDurationValue // labelDurationValue
// //
this.labelDurationValue.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.labelDurationValue.BackColor = System.Drawing.Color.Transparent; this.labelDurationValue.BackColor = System.Drawing.Color.Transparent;
this.labelDurationValue.Location = new System.Drawing.Point(129, 20); this.labelDurationValue.Location = new System.Drawing.Point(147, 77);
this.labelDurationValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0); this.labelDurationValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
this.labelDurationValue.Name = "labelDurationValue"; this.labelDurationValue.Name = "labelDurationValue";
this.labelDurationValue.Size = new System.Drawing.Size(48, 13); this.labelDurationValue.Size = new System.Drawing.Size(52, 13);
this.labelDurationValue.TabIndex = 1; this.labelDurationValue.TabIndex = 4;
this.labelDurationValue.Text = "0 ms/c"; this.labelDurationValue.Text = "0 ms/c";
this.labelDurationValue.TextAlign = System.Drawing.ContentAlignment.TopRight; this.labelDurationValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
this.toolTip.SetToolTip(this.labelDurationValue, "Milliseconds per character."); this.toolTip.SetToolTip(this.labelDurationValue, "Milliseconds per character.");
// //
// trackBarDuration // trackBarDuration
// //
this.trackBarDuration.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.trackBarDuration.AutoSize = false; this.trackBarDuration.AutoSize = false;
this.trackBarDuration.Location = new System.Drawing.Point(6, 19); this.trackBarDuration.Location = new System.Drawing.Point(3, 76);
this.trackBarDuration.Maximum = 60; this.trackBarDuration.Maximum = 60;
this.trackBarDuration.Minimum = 10; this.trackBarDuration.Minimum = 10;
this.trackBarDuration.Name = "trackBarDuration"; this.trackBarDuration.Name = "trackBarDuration";
this.trackBarDuration.Size = new System.Drawing.Size(128, 30); this.trackBarDuration.Size = new System.Drawing.Size(148, 30);
this.trackBarDuration.TabIndex = 0; this.trackBarDuration.TabIndex = 3;
this.trackBarDuration.TickFrequency = 5; this.trackBarDuration.TickFrequency = 5;
this.trackBarDuration.Value = 25; this.trackBarDuration.Value = 25;
// //
// groupUserInterface
//
this.groupUserInterface.Controls.Add(this.checkSkipOnLinkClick);
this.groupUserInterface.Controls.Add(this.checkColumnName);
this.groupUserInterface.Controls.Add(this.labelIdlePause);
this.groupUserInterface.Controls.Add(this.comboBoxIdlePause);
this.groupUserInterface.Controls.Add(this.checkNonIntrusive);
this.groupUserInterface.Controls.Add(this.checkTimerCountDown);
this.groupUserInterface.Controls.Add(this.checkNotificationTimer);
this.groupUserInterface.Location = new System.Drawing.Point(9, 9);
this.groupUserInterface.Name = "groupUserInterface";
this.groupUserInterface.Size = new System.Drawing.Size(183, 187);
this.groupUserInterface.TabIndex = 0;
this.groupUserInterface.TabStop = false;
this.groupUserInterface.Text = "General";
//
// checkSkipOnLinkClick // checkSkipOnLinkClick
// //
this.checkSkipOnLinkClick.AutoSize = true; this.checkSkipOnLinkClick.AutoSize = true;
this.checkSkipOnLinkClick.Location = new System.Drawing.Point(9, 90); this.checkSkipOnLinkClick.Location = new System.Drawing.Point(6, 51);
this.checkSkipOnLinkClick.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3); this.checkSkipOnLinkClick.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
this.checkSkipOnLinkClick.Name = "checkSkipOnLinkClick"; this.checkSkipOnLinkClick.Name = "checkSkipOnLinkClick";
this.checkSkipOnLinkClick.Size = new System.Drawing.Size(113, 17); this.checkSkipOnLinkClick.Size = new System.Drawing.Size(113, 17);
this.checkSkipOnLinkClick.TabIndex = 3; this.checkSkipOnLinkClick.TabIndex = 2;
this.checkSkipOnLinkClick.Text = "Skip On Link Click"; this.checkSkipOnLinkClick.Text = "Skip On Link Click";
this.toolTip.SetToolTip(this.checkSkipOnLinkClick, "Skips current notification when a link\r\ninside the notification is clicked."); this.toolTip.SetToolTip(this.checkSkipOnLinkClick, "Skips current notification when a link\r\ninside the notification is clicked.");
this.checkSkipOnLinkClick.UseVisualStyleBackColor = true; this.checkSkipOnLinkClick.UseVisualStyleBackColor = true;
@@ -330,60 +286,56 @@
// checkColumnName // checkColumnName
// //
this.checkColumnName.AutoSize = true; this.checkColumnName.AutoSize = true;
this.checkColumnName.Location = new System.Drawing.Point(9, 21); this.checkColumnName.Location = new System.Drawing.Point(6, 5);
this.checkColumnName.Margin = new System.Windows.Forms.Padding(6, 5, 3, 3); this.checkColumnName.Margin = new System.Windows.Forms.Padding(6, 5, 3, 3);
this.checkColumnName.Name = "checkColumnName"; this.checkColumnName.Name = "checkColumnName";
this.checkColumnName.Size = new System.Drawing.Size(129, 17); this.checkColumnName.Size = new System.Drawing.Size(129, 17);
this.checkColumnName.TabIndex = 0; this.checkColumnName.TabIndex = 0;
this.checkColumnName.Text = "Display Column Name"; this.checkColumnName.Text = "Display Column Name";
this.toolTip.SetToolTip(this.checkColumnName, "Shows column name each notification originated\r\nfrom in the notification window t" + this.toolTip.SetToolTip(this.checkColumnName, "Shows column name each notification originated\r\nfrom in the notification window title.");
"itle.");
this.checkColumnName.UseVisualStyleBackColor = true; this.checkColumnName.UseVisualStyleBackColor = true;
// //
// labelIdlePause // labelIdlePause
// //
this.labelIdlePause.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.labelIdlePause.AutoSize = true; this.labelIdlePause.AutoSize = true;
this.labelIdlePause.Location = new System.Drawing.Point(5, 141); this.labelIdlePause.Location = new System.Drawing.Point(3, 106);
this.labelIdlePause.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); this.labelIdlePause.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.labelIdlePause.Name = "labelIdlePause"; this.labelIdlePause.Name = "labelIdlePause";
this.labelIdlePause.Size = new System.Drawing.Size(89, 13); this.labelIdlePause.Size = new System.Drawing.Size(89, 13);
this.labelIdlePause.TabIndex = 3; this.labelIdlePause.TabIndex = 4;
this.labelIdlePause.Text = "Pause When Idle"; this.labelIdlePause.Text = "Pause When Idle";
// //
// comboBoxIdlePause // comboBoxIdlePause
// //
this.comboBoxIdlePause.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.comboBoxIdlePause.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboBoxIdlePause.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboBoxIdlePause.FormattingEnabled = true; this.comboBoxIdlePause.FormattingEnabled = true;
this.comboBoxIdlePause.Location = new System.Drawing.Point(6, 157); this.comboBoxIdlePause.Location = new System.Drawing.Point(5, 122);
this.comboBoxIdlePause.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
this.comboBoxIdlePause.Name = "comboBoxIdlePause"; this.comboBoxIdlePause.Name = "comboBoxIdlePause";
this.comboBoxIdlePause.Size = new System.Drawing.Size(171, 21); this.comboBoxIdlePause.Size = new System.Drawing.Size(144, 21);
this.comboBoxIdlePause.TabIndex = 4; this.comboBoxIdlePause.TabIndex = 5;
this.toolTip.SetToolTip(this.comboBoxIdlePause, "Pauses new notifications after going idle for a set amount of time."); this.toolTip.SetToolTip(this.comboBoxIdlePause, "Pauses new notifications after going idle for a set amount of time.");
// //
// checkNonIntrusive // checkNonIntrusive
// //
this.checkNonIntrusive.AutoSize = true; this.checkNonIntrusive.AutoSize = true;
this.checkNonIntrusive.Location = new System.Drawing.Point(9, 113); this.checkNonIntrusive.Location = new System.Drawing.Point(6, 74);
this.checkNonIntrusive.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3); this.checkNonIntrusive.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
this.checkNonIntrusive.Name = "checkNonIntrusive"; this.checkNonIntrusive.Name = "checkNonIntrusive";
this.checkNonIntrusive.Size = new System.Drawing.Size(128, 17); this.checkNonIntrusive.Size = new System.Drawing.Size(128, 17);
this.checkNonIntrusive.TabIndex = 4; this.checkNonIntrusive.TabIndex = 3;
this.checkNonIntrusive.Text = "Non-Intrusive Popups"; this.checkNonIntrusive.Text = "Non-Intrusive Popups";
this.toolTip.SetToolTip(this.checkNonIntrusive, "When not idle and the cursor is within the notification window area,\r\nit will be " + this.toolTip.SetToolTip(this.checkNonIntrusive, "When not idle and the cursor is within the notification window area,\r\nit will be delayed until the cursor moves away to prevent accidental clicks.");
"delayed until the cursor moves away to prevent accidental clicks.");
this.checkNonIntrusive.UseVisualStyleBackColor = true; this.checkNonIntrusive.UseVisualStyleBackColor = true;
// //
// checkTimerCountDown // checkTimerCountDown
// //
this.checkTimerCountDown.AutoSize = true; this.checkTimerCountDown.AutoSize = true;
this.checkTimerCountDown.Location = new System.Drawing.Point(9, 67); this.checkTimerCountDown.Location = new System.Drawing.Point(6, 28);
this.checkTimerCountDown.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3); this.checkTimerCountDown.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
this.checkTimerCountDown.Name = "checkTimerCountDown"; this.checkTimerCountDown.Name = "checkTimerCountDown";
this.checkTimerCountDown.Size = new System.Drawing.Size(119, 17); this.checkTimerCountDown.Size = new System.Drawing.Size(119, 17);
this.checkTimerCountDown.TabIndex = 2; this.checkTimerCountDown.TabIndex = 1;
this.checkTimerCountDown.Text = "Timer Counts Down"; this.checkTimerCountDown.Text = "Timer Counts Down";
this.toolTip.SetToolTip(this.checkTimerCountDown, "The notification timer counts down instead of up."); this.toolTip.SetToolTip(this.checkTimerCountDown, "The notification timer counts down instead of up.");
this.checkTimerCountDown.UseVisualStyleBackColor = true; this.checkTimerCountDown.UseVisualStyleBackColor = true;
@@ -391,39 +343,239 @@
// checkNotificationTimer // checkNotificationTimer
// //
this.checkNotificationTimer.AutoSize = true; this.checkNotificationTimer.AutoSize = true;
this.checkNotificationTimer.Location = new System.Drawing.Point(9, 44); this.checkNotificationTimer.Location = new System.Drawing.Point(6, 5);
this.checkNotificationTimer.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3); this.checkNotificationTimer.Margin = new System.Windows.Forms.Padding(6, 5, 3, 3);
this.checkNotificationTimer.Name = "checkNotificationTimer"; this.checkNotificationTimer.Name = "checkNotificationTimer";
this.checkNotificationTimer.Size = new System.Drawing.Size(145, 17); this.checkNotificationTimer.Size = new System.Drawing.Size(145, 17);
this.checkNotificationTimer.TabIndex = 1; this.checkNotificationTimer.TabIndex = 0;
this.checkNotificationTimer.Text = "Display Notification Timer"; this.checkNotificationTimer.Text = "Display Notification Timer";
this.checkNotificationTimer.UseVisualStyleBackColor = true; this.checkNotificationTimer.UseVisualStyleBackColor = true;
// //
// radioSizeAuto
//
this.radioSizeAuto.Location = new System.Drawing.Point(6, 4);
this.radioSizeAuto.Margin = new System.Windows.Forms.Padding(5, 4, 3, 3);
this.radioSizeAuto.Name = "radioSizeAuto";
this.radioSizeAuto.Size = new System.Drawing.Size(92, 17);
this.radioSizeAuto.TabIndex = 0;
this.radioSizeAuto.TabStop = true;
this.radioSizeAuto.Text = "Auto";
this.toolTip.SetToolTip(this.radioSizeAuto, "Notification size is based on the font size and browser zoom level.");
this.radioSizeAuto.UseVisualStyleBackColor = true;
//
// radioSizeCustom
//
this.radioSizeCustom.Location = new System.Drawing.Point(106, 4);
this.radioSizeCustom.Margin = new System.Windows.Forms.Padding(5, 4, 3, 3);
this.radioSizeCustom.Name = "radioSizeCustom";
this.radioSizeCustom.Size = new System.Drawing.Size(92, 17);
this.radioSizeCustom.TabIndex = 1;
this.radioSizeCustom.TabStop = true;
this.radioSizeCustom.Text = "Custom";
this.toolTip.SetToolTip(this.radioSizeCustom, "Resize the example notification window to the desired size.");
this.radioSizeCustom.UseVisualStyleBackColor = true;
//
// labelGeneral
//
this.labelGeneral.AutoSize = true;
this.labelGeneral.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelGeneral.Location = new System.Drawing.Point(6, 8);
this.labelGeneral.Margin = new System.Windows.Forms.Padding(0, 2, 0, 0);
this.labelGeneral.Name = "labelGeneral";
this.labelGeneral.Size = new System.Drawing.Size(66, 20);
this.labelGeneral.TabIndex = 0;
this.labelGeneral.Text = "General";
//
// panelGeneral
//
this.panelGeneral.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.panelGeneral.Controls.Add(this.checkMediaPreviews);
this.panelGeneral.Controls.Add(this.checkColumnName);
this.panelGeneral.Controls.Add(this.checkSkipOnLinkClick);
this.panelGeneral.Controls.Add(this.checkNonIntrusive);
this.panelGeneral.Controls.Add(this.labelIdlePause);
this.panelGeneral.Controls.Add(this.comboBoxIdlePause);
this.panelGeneral.Location = new System.Drawing.Point(9, 31);
this.panelGeneral.Name = "panelGeneral";
this.panelGeneral.Size = new System.Drawing.Size(322, 149);
this.panelGeneral.TabIndex = 1;
//
// labelScrollSpeedValue
//
this.labelScrollSpeedValue.Location = new System.Drawing.Point(147, 53);
this.labelScrollSpeedValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
this.labelScrollSpeedValue.Name = "labelScrollSpeedValue";
this.labelScrollSpeedValue.Size = new System.Drawing.Size(38, 13);
this.labelScrollSpeedValue.TabIndex = 4;
this.labelScrollSpeedValue.Text = "100%";
this.labelScrollSpeedValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
//
// trackBarScrollSpeed
//
this.trackBarScrollSpeed.AutoSize = false;
this.trackBarScrollSpeed.LargeChange = 25;
this.trackBarScrollSpeed.Location = new System.Drawing.Point(5, 52);
this.trackBarScrollSpeed.Maximum = 200;
this.trackBarScrollSpeed.Minimum = 25;
this.trackBarScrollSpeed.Name = "trackBarScrollSpeed";
this.trackBarScrollSpeed.Size = new System.Drawing.Size(148, 30);
this.trackBarScrollSpeed.SmallChange = 5;
this.trackBarScrollSpeed.TabIndex = 3;
this.trackBarScrollSpeed.TickFrequency = 25;
this.trackBarScrollSpeed.Value = 100;
//
// labelScrollSpeed
//
this.labelScrollSpeed.AutoSize = true;
this.labelScrollSpeed.Location = new System.Drawing.Point(3, 36);
this.labelScrollSpeed.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.labelScrollSpeed.Name = "labelScrollSpeed";
this.labelScrollSpeed.Size = new System.Drawing.Size(67, 13);
this.labelScrollSpeed.TabIndex = 2;
this.labelScrollSpeed.Text = "Scroll Speed";
//
// labelLocation
//
this.labelLocation.AutoSize = true;
this.labelLocation.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelLocation.Location = new System.Drawing.Point(6, 395);
this.labelLocation.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0);
this.labelLocation.Name = "labelLocation";
this.labelLocation.Size = new System.Drawing.Size(70, 20);
this.labelLocation.TabIndex = 4;
this.labelLocation.Text = "Location";
//
// panelLocation
//
this.panelLocation.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.panelLocation.Controls.Add(this.radioLocTL);
this.panelLocation.Controls.Add(this.labelDisplay);
this.panelLocation.Controls.Add(this.trackBarEdgeDistance);
this.panelLocation.Controls.Add(this.comboBoxDisplay);
this.panelLocation.Controls.Add(this.radioLocTR);
this.panelLocation.Controls.Add(this.labelEdgeDistance);
this.panelLocation.Controls.Add(this.radioLocBL);
this.panelLocation.Controls.Add(this.radioLocCustom);
this.panelLocation.Controls.Add(this.radioLocBR);
this.panelLocation.Controls.Add(this.labelEdgeDistanceValue);
this.panelLocation.Location = new System.Drawing.Point(9, 418);
this.panelLocation.Name = "panelLocation";
this.panelLocation.Size = new System.Drawing.Size(322, 165);
this.panelLocation.TabIndex = 5;
//
// panelTimer
//
this.panelTimer.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.panelTimer.Controls.Add(this.labelDuration);
this.panelTimer.Controls.Add(this.checkNotificationTimer);
this.panelTimer.Controls.Add(this.tableLayoutDurationButtons);
this.panelTimer.Controls.Add(this.checkTimerCountDown);
this.panelTimer.Controls.Add(this.labelDurationValue);
this.panelTimer.Controls.Add(this.trackBarDuration);
this.panelTimer.Location = new System.Drawing.Point(9, 227);
this.panelTimer.Name = "panelTimer";
this.panelTimer.Size = new System.Drawing.Size(322, 144);
this.panelTimer.TabIndex = 3;
//
// labelDuration
//
this.labelDuration.AutoSize = true;
this.labelDuration.Location = new System.Drawing.Point(3, 60);
this.labelDuration.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.labelDuration.Name = "labelDuration";
this.labelDuration.Size = new System.Drawing.Size(47, 13);
this.labelDuration.TabIndex = 2;
this.labelDuration.Text = "Duration";
//
// labelTimer
//
this.labelTimer.AutoSize = true;
this.labelTimer.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelTimer.Location = new System.Drawing.Point(6, 204);
this.labelTimer.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0);
this.labelTimer.Name = "labelTimer";
this.labelTimer.Size = new System.Drawing.Size(48, 20);
this.labelTimer.TabIndex = 2;
this.labelTimer.Text = "Timer";
//
// labelSize
//
this.labelSize.AutoSize = true;
this.labelSize.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelSize.Location = new System.Drawing.Point(6, 607);
this.labelSize.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0);
this.labelSize.Name = "labelSize";
this.labelSize.Size = new System.Drawing.Size(40, 20);
this.labelSize.TabIndex = 6;
this.labelSize.Text = "Size";
//
// panelMiscellaneous
//
this.panelMiscellaneous.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.panelMiscellaneous.Controls.Add(this.radioSizeCustom);
this.panelMiscellaneous.Controls.Add(this.radioSizeAuto);
this.panelMiscellaneous.Controls.Add(this.labelScrollSpeedValue);
this.panelMiscellaneous.Controls.Add(this.trackBarScrollSpeed);
this.panelMiscellaneous.Controls.Add(this.labelScrollSpeed);
this.panelMiscellaneous.Location = new System.Drawing.Point(9, 630);
this.panelMiscellaneous.Name = "panelMiscellaneous";
this.panelMiscellaneous.Size = new System.Drawing.Size(322, 92);
this.panelMiscellaneous.TabIndex = 7;
//
// durationUpdateTimer
//
this.durationUpdateTimer.Interval = 200;
this.durationUpdateTimer.Tick += new System.EventHandler(this.durationUpdateTimer_Tick);
//
// checkMediaPreviews
//
this.checkMediaPreviews.AutoSize = true;
this.checkMediaPreviews.Location = new System.Drawing.Point(6, 28);
this.checkMediaPreviews.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
this.checkMediaPreviews.Name = "checkMediaPreviews";
this.checkMediaPreviews.Size = new System.Drawing.Size(131, 17);
this.checkMediaPreviews.TabIndex = 1;
this.checkMediaPreviews.Text = "Show Media Previews";
this.toolTip.SetToolTip(this.checkMediaPreviews, "Shows image and video thumbnails in the notification window.");
this.checkMediaPreviews.UseVisualStyleBackColor = true;
//
// TabSettingsNotifications // TabSettingsNotifications
// //
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.groupUserInterface); this.Controls.Add(this.panelMiscellaneous);
this.Controls.Add(this.groupNotificationDuration); this.Controls.Add(this.labelSize);
this.Controls.Add(this.groupNotificationLocation); this.Controls.Add(this.labelTimer);
this.Controls.Add(this.panelLocation);
this.Controls.Add(this.labelLocation);
this.Controls.Add(this.panelGeneral);
this.Controls.Add(this.labelGeneral);
this.Controls.Add(this.panelTimer);
this.Name = "TabSettingsNotifications"; this.Name = "TabSettingsNotifications";
this.Size = new System.Drawing.Size(478, 300); this.Size = new System.Drawing.Size(340, 731);
this.ParentChanged += new System.EventHandler(this.TabSettingsNotifications_ParentChanged); this.ParentChanged += new System.EventHandler(this.TabSettingsNotifications_ParentChanged);
this.groupNotificationLocation.ResumeLayout(false);
this.groupNotificationLocation.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.trackBarEdgeDistance)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.trackBarEdgeDistance)).EndInit();
this.groupNotificationDuration.ResumeLayout(false);
this.tableLayoutDurationButtons.ResumeLayout(false); this.tableLayoutDurationButtons.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.trackBarDuration)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.trackBarDuration)).EndInit();
this.groupUserInterface.ResumeLayout(false); this.panelGeneral.ResumeLayout(false);
this.groupUserInterface.PerformLayout(); this.panelGeneral.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.trackBarScrollSpeed)).EndInit();
this.panelLocation.ResumeLayout(false);
this.panelLocation.PerformLayout();
this.panelTimer.ResumeLayout(false);
this.panelTimer.PerformLayout();
this.panelMiscellaneous.ResumeLayout(false);
this.panelMiscellaneous.PerformLayout();
this.ResumeLayout(false); this.ResumeLayout(false);
this.PerformLayout();
} }
#endregion #endregion
private System.Windows.Forms.GroupBox groupNotificationLocation;
private System.Windows.Forms.Label labelDisplay; private System.Windows.Forms.Label labelDisplay;
private System.Windows.Forms.ComboBox comboBoxDisplay; private System.Windows.Forms.ComboBox comboBoxDisplay;
private System.Windows.Forms.Label labelEdgeDistance; private System.Windows.Forms.Label labelEdgeDistance;
@@ -433,8 +585,6 @@
private System.Windows.Forms.RadioButton radioLocBL; private System.Windows.Forms.RadioButton radioLocBL;
private System.Windows.Forms.RadioButton radioLocTR; private System.Windows.Forms.RadioButton radioLocTR;
private System.Windows.Forms.RadioButton radioLocTL; private System.Windows.Forms.RadioButton radioLocTL;
private System.Windows.Forms.GroupBox groupNotificationDuration;
private System.Windows.Forms.GroupBox groupUserInterface;
private System.Windows.Forms.ToolTip toolTip; private System.Windows.Forms.ToolTip toolTip;
private System.Windows.Forms.Label labelEdgeDistanceValue; private System.Windows.Forms.Label labelEdgeDistanceValue;
private System.Windows.Forms.Label labelDurationValue; private System.Windows.Forms.Label labelDurationValue;
@@ -450,5 +600,21 @@
private System.Windows.Forms.CheckBox checkSkipOnLinkClick; private System.Windows.Forms.CheckBox checkSkipOnLinkClick;
private System.Windows.Forms.CheckBox checkTimerCountDown; private System.Windows.Forms.CheckBox checkTimerCountDown;
private System.Windows.Forms.CheckBox checkNotificationTimer; private System.Windows.Forms.CheckBox checkNotificationTimer;
private System.Windows.Forms.Label labelGeneral;
private System.Windows.Forms.Panel panelGeneral;
private System.Windows.Forms.Label labelLocation;
private System.Windows.Forms.Panel panelLocation;
private System.Windows.Forms.Panel panelTimer;
private System.Windows.Forms.Label labelTimer;
private System.Windows.Forms.Label labelScrollSpeedValue;
private System.Windows.Forms.TrackBar trackBarScrollSpeed;
private System.Windows.Forms.Label labelScrollSpeed;
private System.Windows.Forms.Label labelSize;
private System.Windows.Forms.Panel panelMiscellaneous;
private System.Windows.Forms.Label labelDuration;
private System.Windows.Forms.Timer durationUpdateTimer;
private System.Windows.Forms.RadioButton radioSizeCustom;
private System.Windows.Forms.RadioButton radioSizeAuto;
private System.Windows.Forms.CheckBox checkMediaPreviews;
} }
} }

View File

@@ -1,11 +1,10 @@
using System; using System;
using System.Globalization;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Notification; using TweetDuck.Core.Notification;
namespace TweetDuck.Core.Other.Settings{ namespace TweetDuck.Core.Other.Settings{
partial class TabSettingsNotifications : BaseTabSettings{ sealed partial class TabSettingsNotifications : BaseTabSettings{
private static readonly int[] IdlePauseSeconds = { 0, 30, 60, 120, 300 }; private static readonly int[] IdlePauseSeconds = { 0, 30, 60, 120, 300 };
private readonly FormNotificationMain notification; private readonly FormNotificationMain notification;
@@ -14,16 +13,13 @@ namespace TweetDuck.Core.Other.Settings{
InitializeComponent(); InitializeComponent();
this.notification = notification; this.notification = notification;
this.notification.CanMoveWindow = () => radioLocCustom.Checked;
this.notification.Move += (sender, args) => {
if (radioLocCustom.Checked && this.notification.Location != ControlExtensions.InvisibleLocation){
Config.CustomNotificationPosition = this.notification.Location;
}
};
this.notification.Initialized += (sender, args) => { this.notification.Initialized += (sender, args) => {
this.InvokeAsyncSafe(() => this.notification.ShowNotificationForSettings(true)); this.InvokeAsyncSafe(() => {
this.notification.ShowNotificationForSettings(true);
this.notification.Move += notification_Move;
this.notification.ResizeEnd += notification_ResizeEnd;
});
}; };
this.notification.Activated += notification_Activated; this.notification.Activated += notification_Activated;
@@ -39,6 +35,11 @@ namespace TweetDuck.Core.Other.Settings{
comboBoxDisplay.Enabled = trackBarEdgeDistance.Enabled = !radioLocCustom.Checked; comboBoxDisplay.Enabled = trackBarEdgeDistance.Enabled = !radioLocCustom.Checked;
switch(Config.NotificationSize){
case TweetNotification.Size.Auto: radioSizeAuto.Checked = true; break;
case TweetNotification.Size.Custom: radioSizeCustom.Checked = true; break;
}
toolTip.SetToolTip(trackBarDuration, toolTip.GetToolTip(labelDurationValue)); toolTip.SetToolTip(trackBarDuration, toolTip.GetToolTip(labelDurationValue));
trackBarDuration.SetValueSafe(Config.NotificationDurationValue); trackBarDuration.SetValueSafe(Config.NotificationDurationValue);
labelDurationValue.Text = Config.NotificationDurationValue+" ms/c"; labelDurationValue.Text = Config.NotificationDurationValue+" ms/c";
@@ -50,10 +51,10 @@ namespace TweetDuck.Core.Other.Settings{
comboBoxIdlePause.Items.Add("5 minutes"); comboBoxIdlePause.Items.Add("5 minutes");
comboBoxIdlePause.SelectedIndex = Math.Max(0, Array.FindIndex(IdlePauseSeconds, val => val == Config.NotificationIdlePauseSeconds)); comboBoxIdlePause.SelectedIndex = Math.Max(0, Array.FindIndex(IdlePauseSeconds, val => val == Config.NotificationIdlePauseSeconds));
comboBoxDisplay.Items.Add("(Same As "+Program.BrandName+")"); comboBoxDisplay.Items.Add("(Same as TweetDuck)");
foreach(Screen screen in Screen.AllScreens){ foreach(Screen screen in Screen.AllScreens){
comboBoxDisplay.Items.Add(screen.DeviceName+" ("+screen.Bounds.Width+"x"+screen.Bounds.Height+")"); comboBoxDisplay.Items.Add(screen.DeviceName.TrimStart('\\', '.')+" ("+screen.Bounds.Width+"x"+screen.Bounds.Height+")");
} }
comboBoxDisplay.SelectedIndex = Math.Min(comboBoxDisplay.Items.Count-1, Config.NotificationDisplay); comboBoxDisplay.SelectedIndex = Math.Min(comboBoxDisplay.Items.Count-1, Config.NotificationDisplay);
@@ -62,11 +63,18 @@ namespace TweetDuck.Core.Other.Settings{
checkNotificationTimer.Checked = Config.DisplayNotificationTimer; checkNotificationTimer.Checked = Config.DisplayNotificationTimer;
checkTimerCountDown.Enabled = checkNotificationTimer.Checked; checkTimerCountDown.Enabled = checkNotificationTimer.Checked;
checkTimerCountDown.Checked = Config.NotificationTimerCountDown; checkTimerCountDown.Checked = Config.NotificationTimerCountDown;
checkMediaPreviews.Checked = Config.NotificationMediaPreviews;
checkSkipOnLinkClick.Checked = Config.NotificationSkipOnLinkClick; checkSkipOnLinkClick.Checked = Config.NotificationSkipOnLinkClick;
checkNonIntrusive.Checked = Config.NotificationNonIntrusiveMode; checkNonIntrusive.Checked = Config.NotificationNonIntrusiveMode;
trackBarScrollSpeed.SetValueSafe(Config.NotificationScrollSpeed);
labelScrollSpeedValue.Text = trackBarScrollSpeed.Value+"%";
trackBarEdgeDistance.SetValueSafe(Config.NotificationEdgeDistance); trackBarEdgeDistance.SetValueSafe(Config.NotificationEdgeDistance);
labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value.ToString(CultureInfo.InvariantCulture)+" px"; labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value+" px";
this.notification.CanMoveWindow = () => radioLocCustom.Checked;
this.notification.CanResizeWindow = radioSizeCustom.Checked;
Disposed += (sender, args) => this.notification.Dispose(); Disposed += (sender, args) => this.notification.Dispose();
} }
@@ -78,6 +86,9 @@ namespace TweetDuck.Core.Other.Settings{
radioLocBR.CheckedChanged += radioLoc_CheckedChanged; radioLocBR.CheckedChanged += radioLoc_CheckedChanged;
radioLocCustom.Click += radioLocCustom_Click; radioLocCustom.Click += radioLocCustom_Click;
radioSizeAuto.CheckedChanged += radioSize_CheckedChanged;
radioSizeCustom.Click += radioSizeCustom_Click;
trackBarDuration.ValueChanged += trackBarDuration_ValueChanged; trackBarDuration.ValueChanged += trackBarDuration_ValueChanged;
btnDurationShort.Click += btnDurationShort_Click; btnDurationShort.Click += btnDurationShort_Click;
btnDurationMedium.Click += btnDurationMedium_Click; btnDurationMedium.Click += btnDurationMedium_Click;
@@ -86,10 +97,12 @@ namespace TweetDuck.Core.Other.Settings{
checkColumnName.CheckedChanged += checkColumnName_CheckedChanged; checkColumnName.CheckedChanged += checkColumnName_CheckedChanged;
checkNotificationTimer.CheckedChanged += checkNotificationTimer_CheckedChanged; checkNotificationTimer.CheckedChanged += checkNotificationTimer_CheckedChanged;
checkTimerCountDown.CheckedChanged += checkTimerCountDown_CheckedChanged; checkTimerCountDown.CheckedChanged += checkTimerCountDown_CheckedChanged;
checkMediaPreviews.CheckedChanged += checkMediaPreviews_CheckedChanged;
checkSkipOnLinkClick.CheckedChanged += checkSkipOnLinkClick_CheckedChanged; checkSkipOnLinkClick.CheckedChanged += checkSkipOnLinkClick_CheckedChanged;
checkNonIntrusive.CheckedChanged += checkNonIntrusive_CheckedChanged; checkNonIntrusive.CheckedChanged += checkNonIntrusive_CheckedChanged;
comboBoxIdlePause.SelectedValueChanged += comboBoxIdlePause_SelectedValueChanged; comboBoxIdlePause.SelectedValueChanged += comboBoxIdlePause_SelectedValueChanged;
trackBarScrollSpeed.ValueChanged += trackBarScrollSpeed_ValueChanged;
comboBoxDisplay.SelectedValueChanged += comboBoxDisplay_SelectedValueChanged; comboBoxDisplay.SelectedValueChanged += comboBoxDisplay_SelectedValueChanged;
trackBarEdgeDistance.ValueChanged += trackBarEdgeDistance_ValueChanged; trackBarEdgeDistance.ValueChanged += trackBarEdgeDistance_ValueChanged;
@@ -109,6 +122,19 @@ namespace TweetDuck.Core.Other.Settings{
notification.Activated -= notification_Activated; notification.Activated -= notification_Activated;
} }
private void notification_Move(object sender, EventArgs e){
if (radioLocCustom.Checked && notification.Location != ControlExtensions.InvisibleLocation){
Config.CustomNotificationPosition = notification.Location;
}
}
private void notification_ResizeEnd(object sender, EventArgs e){
if (radioSizeCustom.Checked){
Config.CustomNotificationSize = notification.BrowserSize;
notification.ShowNotificationForSettings(false);
}
}
private void radioLoc_CheckedChanged(object sender, EventArgs e){ private void radioLoc_CheckedChanged(object sender, EventArgs e){
if (radioLocTL.Checked)Config.NotificationPosition = TweetNotification.Position.TopLeft; if (radioLocTL.Checked)Config.NotificationPosition = TweetNotification.Position.TopLeft;
else if (radioLocTR.Checked)Config.NotificationPosition = TweetNotification.Position.TopRight; else if (radioLocTR.Checked)Config.NotificationPosition = TweetNotification.Position.TopRight;
@@ -129,7 +155,7 @@ namespace TweetDuck.Core.Other.Settings{
comboBoxDisplay.Enabled = trackBarEdgeDistance.Enabled = false; comboBoxDisplay.Enabled = trackBarEdgeDistance.Enabled = false;
notification.ShowNotificationForSettings(false); notification.ShowNotificationForSettings(false);
if (notification.IsFullyOutsideView() && MessageBox.Show("The notification seems to be outside of view, would you like to reset its position?", "Notification is outside view", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == DialogResult.Yes){ if (notification.IsFullyOutsideView() && FormMessage.Question("Notification is outside view", "The notification seems to be outside of view, would you like to reset its position?", FormMessage.Yes, FormMessage.No)){
Config.NotificationPosition = TweetNotification.Position.TopRight; Config.NotificationPosition = TweetNotification.Position.TopRight;
notification.MoveToVisibleLocation(); notification.MoveToVisibleLocation();
@@ -140,11 +166,30 @@ namespace TweetDuck.Core.Other.Settings{
} }
} }
private void radioSize_CheckedChanged(object sender, EventArgs e){
if (radioSizeAuto.Checked)Config.NotificationSize = TweetNotification.Size.Auto;
notification.ShowNotificationForSettings(false);
notification.CanResizeWindow = false; // must be after ShowNotificationForSettings
}
private void radioSizeCustom_Click(object sender, EventArgs e){
if (!Config.IsCustomNotificationSizeSet){
Config.CustomNotificationSize = notification.BrowserSize;
}
Config.NotificationSize = TweetNotification.Size.Custom;
notification.CanResizeWindow = true;
notification.ShowNotificationForSettings(false);
}
private void trackBarDuration_ValueChanged(object sender, EventArgs e){ private void trackBarDuration_ValueChanged(object sender, EventArgs e){
durationUpdateTimer.Stop();
durationUpdateTimer.Start();
Config.NotificationDurationValue = trackBarDuration.Value; Config.NotificationDurationValue = trackBarDuration.Value;
labelDurationValue.Text = Config.NotificationDurationValue+" ms/c"; labelDurationValue.Text = Config.NotificationDurationValue+" ms/c";
notification.ShowNotificationForSettings(true);
} }
private void btnDurationShort_Click(object sender, EventArgs e){ private void btnDurationShort_Click(object sender, EventArgs e){
@@ -175,6 +220,10 @@ namespace TweetDuck.Core.Other.Settings{
notification.ShowNotificationForSettings(true); notification.ShowNotificationForSettings(true);
} }
private void checkMediaPreviews_CheckedChanged(object sender, EventArgs e){
Config.NotificationMediaPreviews = checkMediaPreviews.Checked;
}
private void checkSkipOnLinkClick_CheckedChanged(object sender, EventArgs e){ private void checkSkipOnLinkClick_CheckedChanged(object sender, EventArgs e){
Config.NotificationSkipOnLinkClick = checkSkipOnLinkClick.Checked; Config.NotificationSkipOnLinkClick = checkSkipOnLinkClick.Checked;
} }
@@ -187,15 +236,27 @@ namespace TweetDuck.Core.Other.Settings{
Config.NotificationIdlePauseSeconds = IdlePauseSeconds[comboBoxIdlePause.SelectedIndex]; Config.NotificationIdlePauseSeconds = IdlePauseSeconds[comboBoxIdlePause.SelectedIndex];
} }
private void trackBarScrollSpeed_ValueChanged(object sender, EventArgs e){
if (trackBarScrollSpeed.AlignValueToTick()){
labelScrollSpeedValue.Text = trackBarScrollSpeed.Value+"%";
Config.NotificationScrollSpeed = trackBarScrollSpeed.Value;
}
}
private void comboBoxDisplay_SelectedValueChanged(object sender, EventArgs e){ private void comboBoxDisplay_SelectedValueChanged(object sender, EventArgs e){
Config.NotificationDisplay = comboBoxDisplay.SelectedIndex; Config.NotificationDisplay = comboBoxDisplay.SelectedIndex;
notification.ShowNotificationForSettings(false); notification.ShowNotificationForSettings(false);
} }
private void trackBarEdgeDistance_ValueChanged(object sender, EventArgs e){ private void trackBarEdgeDistance_ValueChanged(object sender, EventArgs e){
labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value.ToString(CultureInfo.InvariantCulture)+" px"; labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value+" px";
Config.NotificationEdgeDistance = trackBarEdgeDistance.Value; Config.NotificationEdgeDistance = trackBarEdgeDistance.Value;
notification.ShowNotificationForSettings(false); notification.ShowNotificationForSettings(false);
} }
private void durationUpdateTimer_Tick(object sender, EventArgs e){
notification.ShowNotificationForSettings(true);
durationUpdateTimer.Stop();
}
} }
} }

View File

@@ -25,31 +25,45 @@
private void InitializeComponent() { private void InitializeComponent() {
this.components = new System.ComponentModel.Container(); this.components = new System.ComponentModel.Container();
this.toolTip = new System.Windows.Forms.ToolTip(this.components); this.toolTip = new System.Windows.Forms.ToolTip(this.components);
this.groupCustomSound = new System.Windows.Forms.GroupBox(); this.tbCustomSound = new System.Windows.Forms.TextBox();
this.labelVolumeValue = new System.Windows.Forms.Label();
this.btnPlaySound = new System.Windows.Forms.Button(); this.btnPlaySound = new System.Windows.Forms.Button();
this.btnResetSound = new System.Windows.Forms.Button(); this.btnResetSound = new System.Windows.Forms.Button();
this.btnBrowseSound = new System.Windows.Forms.Button(); this.btnBrowseSound = new System.Windows.Forms.Button();
this.tbCustomSound = new System.Windows.Forms.TextBox(); this.labelSoundNotification = new System.Windows.Forms.Label();
this.groupCustomSound.SuspendLayout(); this.panelSoundNotification = new System.Windows.Forms.Panel();
this.labelVolume = new System.Windows.Forms.Label();
this.trackBarVolume = new System.Windows.Forms.TrackBar();
this.panelSoundNotification.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.trackBarVolume)).BeginInit();
this.SuspendLayout(); this.SuspendLayout();
// //
// groupCustomSound // tbCustomSound
// //
this.groupCustomSound.Controls.Add(this.btnPlaySound); this.tbCustomSound.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
this.groupCustomSound.Controls.Add(this.btnResetSound); | System.Windows.Forms.AnchorStyles.Right)));
this.groupCustomSound.Controls.Add(this.btnBrowseSound); this.tbCustomSound.Location = new System.Drawing.Point(3, 3);
this.groupCustomSound.Controls.Add(this.tbCustomSound); this.tbCustomSound.Name = "tbCustomSound";
this.groupCustomSound.Location = new System.Drawing.Point(9, 9); this.tbCustomSound.Size = new System.Drawing.Size(316, 20);
this.groupCustomSound.Name = "groupCustomSound"; this.tbCustomSound.TabIndex = 0;
this.groupCustomSound.Size = new System.Drawing.Size(372, 75); this.toolTip.SetToolTip(this.tbCustomSound, "When empty, the default TweetDeck sound notification is used.");
this.groupCustomSound.TabIndex = 0; //
this.groupCustomSound.TabStop = false; // labelVolumeValue
this.groupCustomSound.Text = "Custom Sound Notification"; //
this.labelVolumeValue.BackColor = System.Drawing.Color.Transparent;
this.labelVolumeValue.Location = new System.Drawing.Point(147, 84);
this.labelVolumeValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
this.labelVolumeValue.Name = "labelVolumeValue";
this.labelVolumeValue.Size = new System.Drawing.Size(38, 13);
this.labelVolumeValue.TabIndex = 6;
this.labelVolumeValue.Text = "100%";
this.labelVolumeValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
// //
// btnPlaySound // btnPlaySound
// //
this.btnPlaySound.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.btnPlaySound.AutoSize = true; this.btnPlaySound.AutoSize = true;
this.btnPlaySound.Location = new System.Drawing.Point(250, 45); this.btnPlaySound.Location = new System.Drawing.Point(203, 29);
this.btnPlaySound.Name = "btnPlaySound"; this.btnPlaySound.Name = "btnPlaySound";
this.btnPlaySound.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0); this.btnPlaySound.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
this.btnPlaySound.Size = new System.Drawing.Size(43, 23); this.btnPlaySound.Size = new System.Drawing.Size(43, 23);
@@ -60,7 +74,7 @@
// btnResetSound // btnResetSound
// //
this.btnResetSound.AutoSize = true; this.btnResetSound.AutoSize = true;
this.btnResetSound.Location = new System.Drawing.Point(6, 45); this.btnResetSound.Location = new System.Drawing.Point(3, 29);
this.btnResetSound.Name = "btnResetSound"; this.btnResetSound.Name = "btnResetSound";
this.btnResetSound.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0); this.btnResetSound.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
this.btnResetSound.Size = new System.Drawing.Size(51, 23); this.btnResetSound.Size = new System.Drawing.Size(51, 23);
@@ -70,8 +84,9 @@
// //
// btnBrowseSound // btnBrowseSound
// //
this.btnBrowseSound.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.btnBrowseSound.AutoSize = true; this.btnBrowseSound.AutoSize = true;
this.btnBrowseSound.Location = new System.Drawing.Point(299, 45); this.btnBrowseSound.Location = new System.Drawing.Point(252, 29);
this.btnBrowseSound.Name = "btnBrowseSound"; this.btnBrowseSound.Name = "btnBrowseSound";
this.btnBrowseSound.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0); this.btnBrowseSound.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
this.btnBrowseSound.Size = new System.Drawing.Size(67, 23); this.btnBrowseSound.Size = new System.Drawing.Size(67, 23);
@@ -79,33 +94,84 @@
this.btnBrowseSound.Text = "Browse..."; this.btnBrowseSound.Text = "Browse...";
this.btnBrowseSound.UseVisualStyleBackColor = true; this.btnBrowseSound.UseVisualStyleBackColor = true;
// //
// tbCustomSound // labelSoundNotification
// //
this.tbCustomSound.Location = new System.Drawing.Point(6, 19); this.labelSoundNotification.AutoSize = true;
this.tbCustomSound.Name = "tbCustomSound"; this.labelSoundNotification.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.tbCustomSound.Size = new System.Drawing.Size(360, 20); this.labelSoundNotification.Location = new System.Drawing.Point(6, 8);
this.tbCustomSound.TabIndex = 0; this.labelSoundNotification.Margin = new System.Windows.Forms.Padding(0, 2, 0, 0);
this.labelSoundNotification.Name = "labelSoundNotification";
this.labelSoundNotification.Size = new System.Drawing.Size(198, 20);
this.labelSoundNotification.TabIndex = 1;
this.labelSoundNotification.Text = "Custom Sound Notification";
//
// panelSoundNotification
//
this.panelSoundNotification.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.panelSoundNotification.Controls.Add(this.labelVolume);
this.panelSoundNotification.Controls.Add(this.trackBarVolume);
this.panelSoundNotification.Controls.Add(this.btnPlaySound);
this.panelSoundNotification.Controls.Add(this.tbCustomSound);
this.panelSoundNotification.Controls.Add(this.btnResetSound);
this.panelSoundNotification.Controls.Add(this.btnBrowseSound);
this.panelSoundNotification.Controls.Add(this.labelVolumeValue);
this.panelSoundNotification.Location = new System.Drawing.Point(9, 31);
this.panelSoundNotification.Name = "panelSoundNotification";
this.panelSoundNotification.Size = new System.Drawing.Size(322, 119);
this.panelSoundNotification.TabIndex = 2;
//
// labelVolume
//
this.labelVolume.AutoSize = true;
this.labelVolume.Location = new System.Drawing.Point(3, 67);
this.labelVolume.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.labelVolume.Name = "labelVolume";
this.labelVolume.Size = new System.Drawing.Size(42, 13);
this.labelVolume.TabIndex = 4;
this.labelVolume.Text = "Volume";
//
// trackBarVolume
//
this.trackBarVolume.AutoSize = false;
this.trackBarVolume.BackColor = System.Drawing.SystemColors.Control;
this.trackBarVolume.Location = new System.Drawing.Point(3, 83);
this.trackBarVolume.Maximum = 100;
this.trackBarVolume.Name = "trackBarVolume";
this.trackBarVolume.Size = new System.Drawing.Size(148, 30);
this.trackBarVolume.SmallChange = 1;
this.trackBarVolume.TabIndex = 5;
this.trackBarVolume.TickFrequency = 10;
this.trackBarVolume.Value = 100;
this.trackBarVolume.ValueChanged += new System.EventHandler(this.trackBarVolume_ValueChanged);
// //
// TabSettingsSounds // TabSettingsSounds
// //
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.groupCustomSound); this.Controls.Add(this.panelSoundNotification);
this.Controls.Add(this.labelSoundNotification);
this.Name = "TabSettingsSounds"; this.Name = "TabSettingsSounds";
this.Size = new System.Drawing.Size(478, 300); this.Size = new System.Drawing.Size(340, 160);
this.groupCustomSound.ResumeLayout(false); this.panelSoundNotification.ResumeLayout(false);
this.groupCustomSound.PerformLayout(); this.panelSoundNotification.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.trackBarVolume)).EndInit();
this.ResumeLayout(false); this.ResumeLayout(false);
this.PerformLayout();
} }
#endregion #endregion
private System.Windows.Forms.ToolTip toolTip; private System.Windows.Forms.ToolTip toolTip;
private System.Windows.Forms.GroupBox groupCustomSound;
private System.Windows.Forms.Button btnResetSound; private System.Windows.Forms.Button btnResetSound;
private System.Windows.Forms.Button btnBrowseSound; private System.Windows.Forms.Button btnBrowseSound;
private System.Windows.Forms.TextBox tbCustomSound; private System.Windows.Forms.TextBox tbCustomSound;
private System.Windows.Forms.Button btnPlaySound; private System.Windows.Forms.Button btnPlaySound;
private System.Windows.Forms.Label labelSoundNotification;
private System.Windows.Forms.Panel panelSoundNotification;
private System.Windows.Forms.Label labelVolume;
private System.Windows.Forms.Label labelVolumeValue;
private System.Windows.Forms.TrackBar trackBarVolume;
} }
} }

View File

@@ -2,21 +2,29 @@
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Controls;
using TweetDuck.Core.Notification; using TweetDuck.Core.Notification;
using TweetDuck.Core.Notification.Sound; using TweetLib.Audio;
namespace TweetDuck.Core.Other.Settings{ namespace TweetDuck.Core.Other.Settings{
partial class TabSettingsSounds : BaseTabSettings{ sealed partial class TabSettingsSounds : BaseTabSettings{
private readonly ISoundNotificationPlayer soundNotification; private readonly SoundNotification soundNotification;
private readonly bool supportsChangingVolume;
public TabSettingsSounds(){ public TabSettingsSounds(){
InitializeComponent(); InitializeComponent();
soundNotification = SoundNotification.New(); soundNotification = new SoundNotification();
soundNotification.PlaybackError += sound_PlaybackError; soundNotification.PlaybackError += sound_PlaybackError;
supportsChangingVolume = soundNotification.SetVolume(Config.NotificationSoundVolume);
trackBarVolume.Enabled = supportsChangingVolume && !string.IsNullOrEmpty(Config.NotificationSoundPath);
trackBarVolume.SetValueSafe(Config.NotificationSoundVolume);
labelVolumeValue.Text = trackBarVolume.Value+"%";
tbCustomSound.Text = Config.NotificationSoundPath; tbCustomSound.Text = Config.NotificationSoundPath;
tbCustomSound_TextChanged(tbCustomSound, new EventArgs()); tbCustomSound_TextChanged(tbCustomSound, EventArgs.Empty);
Disposed += (sender, args) => soundNotification.Dispose(); Disposed += (sender, args) => soundNotification.Dispose();
} }
@@ -34,9 +42,10 @@ namespace TweetDuck.Core.Other.Settings{
private void tbCustomSound_TextChanged(object sender, EventArgs e){ private void tbCustomSound_TextChanged(object sender, EventArgs e){
bool isEmpty = string.IsNullOrEmpty(tbCustomSound.Text); bool isEmpty = string.IsNullOrEmpty(tbCustomSound.Text);
tbCustomSound.ForeColor = isEmpty || File.Exists(tbCustomSound.Text) ? SystemColors.WindowText : Color.Maroon; tbCustomSound.ForeColor = isEmpty || File.Exists(tbCustomSound.Text) ? SystemColors.WindowText : Color.Red;
btnPlaySound.Enabled = !isEmpty; btnPlaySound.Enabled = !isEmpty;
btnResetSound.Enabled = !isEmpty; btnResetSound.Enabled = !isEmpty;
trackBarVolume.Enabled = supportsChangingVolume && !isEmpty;
} }
private void btnPlaySound_Click(object sender, EventArgs e){ private void btnPlaySound_Click(object sender, EventArgs e){
@@ -44,7 +53,7 @@ namespace TweetDuck.Core.Other.Settings{
} }
private void sound_PlaybackError(object sender, PlaybackErrorEventArgs e){ private void sound_PlaybackError(object sender, PlaybackErrorEventArgs e){
MessageBox.Show("Could not play custom notification sound."+Environment.NewLine+e.Message, "Notification Sound Error", MessageBoxButtons.OK, MessageBoxIcon.Error); FormMessage.Error("Notification Sound Error", "Could not play custom notification sound.\n"+e.Message, FormMessage.OK);
} }
private void btnBrowseSound_Click(object sender, EventArgs e){ private void btnBrowseSound_Click(object sender, EventArgs e){
@@ -63,5 +72,11 @@ namespace TweetDuck.Core.Other.Settings{
private void btnResetSound_Click(object sender, EventArgs e){ private void btnResetSound_Click(object sender, EventArgs e){
tbCustomSound.Text = string.Empty; tbCustomSound.Text = string.Empty;
} }
private void trackBarVolume_ValueChanged(object sender, EventArgs e){
Config.NotificationSoundVolume = trackBarVolume.Value;
soundNotification.SetVolume(Config.NotificationSoundVolume);
labelVolumeValue.Text = Config.NotificationSoundVolume+"%";
}
} }
} }

View File

@@ -0,0 +1,116 @@
namespace TweetDuck.Core.Other.Settings {
partial class TabSettingsTray {
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.components = new System.ComponentModel.Container();
this.panelTray = new System.Windows.Forms.Panel();
this.checkTrayHighlight = new System.Windows.Forms.CheckBox();
this.comboBoxTrayType = new System.Windows.Forms.ComboBox();
this.labelTrayIcon = new System.Windows.Forms.Label();
this.labelTray = new System.Windows.Forms.Label();
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
this.panelTray.SuspendLayout();
this.SuspendLayout();
//
// panelTray
//
this.panelTray.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.panelTray.Controls.Add(this.checkTrayHighlight);
this.panelTray.Controls.Add(this.comboBoxTrayType);
this.panelTray.Controls.Add(this.labelTrayIcon);
this.panelTray.Location = new System.Drawing.Point(9, 31);
this.panelTray.Name = "panelTray";
this.panelTray.Size = new System.Drawing.Size(322, 76);
this.panelTray.TabIndex = 1;
//
// checkTrayHighlight
//
this.checkTrayHighlight.AutoSize = true;
this.checkTrayHighlight.Location = new System.Drawing.Point(6, 56);
this.checkTrayHighlight.Margin = new System.Windows.Forms.Padding(6, 5, 3, 3);
this.checkTrayHighlight.Name = "checkTrayHighlight";
this.checkTrayHighlight.Size = new System.Drawing.Size(103, 17);
this.checkTrayHighlight.TabIndex = 2;
this.checkTrayHighlight.Text = "Enable Highlight";
this.toolTip.SetToolTip(this.checkTrayHighlight, "Highlights the tray icon if there are new tweets.\r\nOnly works for columns with popup or audio notifications.\r\nThe icon resets when the main window is restored.");
this.checkTrayHighlight.UseVisualStyleBackColor = true;
//
// comboBoxTrayType
//
this.comboBoxTrayType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboBoxTrayType.FormattingEnabled = true;
this.comboBoxTrayType.Location = new System.Drawing.Point(5, 5);
this.comboBoxTrayType.Margin = new System.Windows.Forms.Padding(5, 5, 3, 3);
this.comboBoxTrayType.Name = "comboBoxTrayType";
this.comboBoxTrayType.Size = new System.Drawing.Size(144, 21);
this.comboBoxTrayType.TabIndex = 0;
this.toolTip.SetToolTip(this.comboBoxTrayType, "Changes behavior of the Tray icon.\r\nRight-click the icon for an action menu.");
//
// labelTrayIcon
//
this.labelTrayIcon.AutoSize = true;
this.labelTrayIcon.Location = new System.Drawing.Point(3, 38);
this.labelTrayIcon.Margin = new System.Windows.Forms.Padding(3, 9, 3, 0);
this.labelTrayIcon.Name = "labelTrayIcon";
this.labelTrayIcon.Size = new System.Drawing.Size(52, 13);
this.labelTrayIcon.TabIndex = 1;
this.labelTrayIcon.Text = "Tray Icon";
//
// labelTray
//
this.labelTray.AutoSize = true;
this.labelTray.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelTray.Location = new System.Drawing.Point(6, 8);
this.labelTray.Margin = new System.Windows.Forms.Padding(0, 2, 0, 0);
this.labelTray.Name = "labelTray";
this.labelTray.Size = new System.Drawing.Size(96, 20);
this.labelTray.TabIndex = 0;
this.labelTray.Text = "System Tray";
//
// TabSettingsTray
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.panelTray);
this.Controls.Add(this.labelTray);
this.Name = "TabSettingsTray";
this.Size = new System.Drawing.Size(340, 119);
this.panelTray.ResumeLayout(false);
this.panelTray.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Panel panelTray;
private System.Windows.Forms.CheckBox checkTrayHighlight;
private System.Windows.Forms.ComboBox comboBoxTrayType;
private System.Windows.Forms.Label labelTrayIcon;
private System.Windows.Forms.Label labelTray;
private System.Windows.Forms.ToolTip toolTip;
}
}

View File

@@ -0,0 +1,33 @@
using System;
namespace TweetDuck.Core.Other.Settings{
sealed partial class TabSettingsTray : BaseTabSettings{
public TabSettingsTray(){
InitializeComponent();
comboBoxTrayType.Items.Add("Disabled");
comboBoxTrayType.Items.Add("Display Icon Only");
comboBoxTrayType.Items.Add("Minimize to Tray");
comboBoxTrayType.Items.Add("Close to Tray");
comboBoxTrayType.Items.Add("Combined");
comboBoxTrayType.SelectedIndex = Math.Min(Math.Max((int)Config.TrayBehavior, 0), comboBoxTrayType.Items.Count-1);
checkTrayHighlight.Enabled = Config.TrayBehavior.ShouldDisplayIcon();
checkTrayHighlight.Checked = Config.EnableTrayHighlight;
}
public override void OnReady(){
comboBoxTrayType.SelectedIndexChanged += comboBoxTrayType_SelectedIndexChanged;
checkTrayHighlight.CheckedChanged += checkTrayHighlight_CheckedChanged;
}
private void comboBoxTrayType_SelectedIndexChanged(object sender, EventArgs e){
Config.TrayBehavior = (TrayIcon.Behavior)comboBoxTrayType.SelectedIndex;
checkTrayHighlight.Enabled = Config.TrayBehavior.ShouldDisplayIcon();
}
private void checkTrayHighlight_CheckedChanged(object sender, EventArgs e){
Config.EnableTrayHighlight = checkTrayHighlight.Checked;
}
}
}

View File

@@ -3,7 +3,7 @@ using System.ComponentModel;
using System.Windows.Forms; using System.Windows.Forms;
namespace TweetDuck.Core{ namespace TweetDuck.Core{
partial class TrayIcon : Component{ sealed partial class TrayIcon : Component{
public enum Behavior{ // keep order public enum Behavior{ // keep order
Disabled, DisplayOnly, MinimizeToTray, CloseToTray, Combined Disabled, DisplayOnly, MinimizeToTray, CloseToTray, Combined
} }

View File

@@ -1,54 +1,88 @@
using CefSharp; using CefSharp;
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Text.RegularExpressions;
using System.Windows.Forms; using System.Windows.Forms;
using CefSharp.WinForms;
using TweetDuck.Core.Other;
namespace TweetDuck.Core.Utils{ namespace TweetDuck.Core.Utils{
static class BrowserUtils{ static class BrowserUtils{
public static string HeaderAcceptLanguage{ public static string HeaderAcceptLanguage{
get{ get{
string culture = CultureInfo.CurrentCulture.Name; string culture = Program.Culture.Name;
if (culture == "en"){ if (culture == "en"){
return "en-us,en"; return "en-us,en";
} }
else{ else{
return culture.ToLowerInvariant()+",en;q=0.9"; return culture.ToLower()+",en;q=0.9";
} }
} }
} }
public static string HeaderUserAgent => Program.BrandName+" "+Application.ProductVersion; public static string HeaderUserAgent => Program.BrandName+" "+Application.ProductVersion;
public static readonly Color BackgroundColor = Color.FromArgb(28, 99, 153); public static void SetupCefArgs(IDictionary<string, string> args){
public const string BackgroundColorFix = "let e=document.createElement('style');document.head.appendChild(e);e.innerHTML='body::before{background:#1c6399!important}'"; if (!Program.SystemConfig.HardwareAcceleration){
args["disable-gpu"] = "1";
public static readonly string[] DictionaryWords = { args["disable-gpu-vsync"] = "1";
"tweetdeck", "TweetDeck", "tweetduck", "TweetDuck", "TD"
};
public static bool IsValidUrl(string url){
if (Uri.TryCreate(url, UriKind.Absolute, out Uri uri)){
string scheme = uri.Scheme;
return scheme == Uri.UriSchemeHttp || scheme == Uri.UriSchemeHttps || scheme == Uri.UriSchemeFtp || scheme == Uri.UriSchemeMailto;
} }
return false; args["disable-extensions"] = "1";
args["disable-plugins-discovery"] = "1";
args["enable-system-flash"] = "0";
if (args.TryGetValue("js-flags", out string jsFlags)){
args["js-flags"] = "--expose-gc "+jsFlags;
}
else{
args["js-flags"] = "--expose-gc";
}
}
public static ChromiumWebBrowser AsControl(this IWebBrowser browserControl){
return (ChromiumWebBrowser)browserControl;
}
private const string TwitterTrackingUrl = "t.co";
public enum UrlCheckResult{
Invalid, Tracking, Fine
}
public static UrlCheckResult CheckUrl(string url){
if (Uri.TryCreate(url, UriKind.Absolute, out Uri uri)){
string scheme = uri.Scheme;
if (scheme == Uri.UriSchemeHttps || scheme == Uri.UriSchemeHttp || scheme == Uri.UriSchemeFtp || scheme == Uri.UriSchemeMailto){
return uri.Host == TwitterTrackingUrl ? UrlCheckResult.Tracking : UrlCheckResult.Fine;
}
}
return UrlCheckResult.Invalid;
} }
public static void OpenExternalBrowser(string url){ public static void OpenExternalBrowser(string url){
if (string.IsNullOrWhiteSpace(url))return; if (string.IsNullOrWhiteSpace(url))return;
if (IsValidUrl(url)){ switch(CheckUrl(url)){
case UrlCheckResult.Fine:
OpenExternalBrowserUnsafe(url);
break;
case UrlCheckResult.Tracking:
if (FormMessage.Warning("Blocked URL", "TweetDuck has blocked a tracking url due to privacy concerns. Do you want to visit it anyway?\n"+url, FormMessage.Yes, FormMessage.No)){
OpenExternalBrowserUnsafe(url); OpenExternalBrowserUnsafe(url);
} }
else{
MessageBox.Show("A potentially malicious URL was blocked from opening:"+Environment.NewLine+url, "Blocked URL", MessageBoxButtons.OK, MessageBoxIcon.Warning); break;
case UrlCheckResult.Invalid:
FormMessage.Warning("Blocked URL", "A potentially malicious URL was blocked from opening:\n"+url, FormMessage.OK);
break;
} }
} }
@@ -61,12 +95,8 @@ namespace TweetDuck.Core.Utils{
return string.IsNullOrEmpty(file) ? null : file; return string.IsNullOrEmpty(file) ? null : file;
} }
public static string ConvertPascalCaseToScreamingSnakeCase(string str){
return Regex.Replace(str, @"(\p{Ll})(\P{Ll})|(\P{Ll})(\P{Ll}\p{Ll})", "$1$3_$2$4").ToUpperInvariant();
}
public static string GetErrorName(CefErrorCode code){ public static string GetErrorName(CefErrorCode code){
return ConvertPascalCaseToScreamingSnakeCase(Enum.GetName(typeof(CefErrorCode), code) ?? string.Empty); return StringUtils.ConvertPascalCaseToScreamingSnakeCase(Enum.GetName(typeof(CefErrorCode), code) ?? string.Empty);
} }
public static WebClient DownloadFileAsync(string url, string target, Action onSuccess, Action<Exception> onFailure){ public static WebClient DownloadFileAsync(string url, string target, Action onSuccess, Action<Exception> onFailure){
@@ -93,18 +123,14 @@ namespace TweetDuck.Core.Utils{
return client; return client;
} }
public static int Scale(int baseValue, double scaleFactor){
return (int)Math.Round(baseValue*scaleFactor);
}
public static void SetZoomLevel(IBrowser browser, int percentage){ public static void SetZoomLevel(IBrowser browser, int percentage){
browser.GetHost().SetZoomLevel(Math.Log(percentage/100.0, 1.2)); browser.GetHost().SetZoomLevel(Math.Log(percentage/100.0, 1.2));
} }
public static bool IsTweetDeckWebsite(IFrame frame){
return frame.Url.Contains("//tweetdeck.twitter.com/");
}
public static bool IsTwitterWebsite(IFrame frame){
return frame.Url.Contains("//twitter.com/");
}
#if DEBUG #if DEBUG
public static void HandleConsoleMessage(object sender, ConsoleMessageEventArgs e){ public static void HandleConsoleMessage(object sender, ConsoleMessageEventArgs e){
Debug.WriteLine("[Console] {0} ({1}:{2})", e.Message, e.Source, e.Line); Debug.WriteLine("[Console] {0} ({1}:{2})", e.Message, e.Source, e.Line);

View File

@@ -1,38 +0,0 @@
using System;
using System.Text.RegularExpressions;
namespace TweetDuck.Core.Utils{
static class CommandLineArgsParser{
private static readonly Lazy<Regex> SplitRegex = new Lazy<Regex>(() => new Regex(@"([^=\s]+(?:=(?:[^ ]*""[^""]*?""[^ ]*|[^ ]*))?)", RegexOptions.Compiled), false);
public static CommandLineArgs ReadCefArguments(string argumentString){
CommandLineArgs args = new CommandLineArgs();
if (string.IsNullOrWhiteSpace(argumentString)){
return args;
}
foreach(Match match in SplitRegex.Value.Matches(argumentString)){
string matchValue = match.Value;
int indexEquals = matchValue.IndexOf('=');
string key, value;
if (indexEquals == -1){
key = matchValue.TrimStart('-');
value = "1";
}
else{
key = matchValue.Substring(0, indexEquals).TrimStart('-');
value = matchValue.Substring(indexEquals+1).Trim('"');
}
if (key.Length != 0){
args.SetValue(key, value);
}
}
return args;
}
}
}

View File

@@ -1,129 +0,0 @@
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Runtime.InteropServices;
namespace TweetDuck.Core.Utils{
static class NativeCoreAudio{
private const int EDATAFLOW_RENDER = 0;
private const int EROLE_MULTIMEDIA = 1;
[ComImport]
[Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")]
private class MMDeviceEnumerator{}
[Guid("A95664D2-9614-4F35-A746-DE8DB63617E6")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IMMDeviceEnumerator{
int Unimpl_EnumAudioEndpoints();
IMMDevice GetDefaultAudioEndpoint(int dataFlow, int role);
}
[Guid("D666063F-1587-4E43-81F1-B948E807363F")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IMMDevice{
[return:MarshalAs(UnmanagedType.IUnknown)]
object Activate(ref Guid id, int clsCtx, IntPtr activationParams);
}
[Guid("77AA99A0-1BD6-484F-8BC7-2C654C9A9B6F")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IAudioSessionManager2{
int Unimpl_FindWillToLive();
int Unimpl_HelloDarknessMyOldFriend();
IAudioSessionEnumerator GetSessionEnumerator();
}
[Guid("E2F5BB11-0570-40CA-ACDD-3AA01277DEE8")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IAudioSessionEnumerator{
int GetCount();
IAudioSessionControl GetSession(int sessionIndex);
}
[Guid("F4B1A599-7266-4319-A8CA-E70ACB11E8CD")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IAudioSessionControl{}
[Guid("BFB7FF88-7239-4FC9-8FA2-07C950BE9C6D")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IAudioSessionControl2{
int Unimpl_GetState();
int Unimpl_GetDisplayName();
int Unimpl_SetDisplayName();
int Unimpl_GetIconPath();
int Unimpl_SetIconPath();
int Unimpl_GetGroupingParam();
int Unimpl_SetGroupingParam();
int Unimpl_RegisterAudioSessionNotification();
int Unimpl_UnregisterAudioSessionNotification();
[return:MarshalAs(UnmanagedType.LPWStr)]
string GetSessionIdentifier();
}
[Guid("87CE5498-68D6-44E5-9215-6DA47EF883D8")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface ISimpleAudioVolume{
void SetMasterVolume(float level, ref Guid eventContext);
float GetMasterVolume();
}
[SuppressMessage("ReSharper", "SuspiciousTypeConversion.Global")]
private static ISimpleAudioVolume GetVolumeObject(string name){
IMMDeviceEnumerator devices = (IMMDeviceEnumerator)new MMDeviceEnumerator();
IMMDevice device = devices.GetDefaultAudioEndpoint(EDATAFLOW_RENDER, EROLE_MULTIMEDIA);
Guid sessionManagerGUID = typeof(IAudioSessionManager2).GUID;
IAudioSessionManager2 manager = (IAudioSessionManager2)device.Activate(ref sessionManagerGUID, 0, IntPtr.Zero);
IAudioSessionEnumerator sessions = manager.GetSessionEnumerator();
ISimpleAudioVolume volumeObj = null;
for(int index = sessions.GetCount()-1; index >= 0; index--){
IAudioSessionControl2 ctl = sessions.GetSession(index) as IAudioSessionControl2;
if (ctl != null){
string identifier = ctl.GetSessionIdentifier();
if (identifier != null && identifier.Contains(name)){
volumeObj = ctl as ISimpleAudioVolume;
break;
}
Marshal.ReleaseComObject(ctl);
}
}
Marshal.ReleaseComObject(devices);
Marshal.ReleaseComObject(device);
Marshal.ReleaseComObject(manager);
Marshal.ReleaseComObject(sessions);
return volumeObj;
}
public static double GetMixerVolume(string appPath){
ISimpleAudioVolume obj = GetVolumeObject(appPath);
float level = 1F;
if (obj != null){
level = obj.GetMasterVolume();
Marshal.ReleaseComObject(obj);
}
return Math.Round(level, 2);
}
public static double GetMixerVolumeForCurrentProcess(){
string path;
using(Process process = Process.GetCurrentProcess()){
path = process.MainModule.FileName;
path = path.Substring(Path.GetPathRoot(path).Length);
}
return GetMixerVolume(path);
}
}
}

View File

@@ -8,7 +8,6 @@ namespace TweetDuck.Core.Utils{
[SuppressMessage("ReSharper", "FieldCanBeMadeReadOnly.Local")] [SuppressMessage("ReSharper", "FieldCanBeMadeReadOnly.Local")]
[SuppressMessage("ReSharper", "MemberCanBePrivate.Local")] [SuppressMessage("ReSharper", "MemberCanBePrivate.Local")]
static class NativeMethods{ static class NativeMethods{
public static readonly IntPtr HWND_BROADCAST = new IntPtr(0xFFFF);
public static readonly IntPtr HOOK_HANDLED = new IntPtr(-1); public static readonly IntPtr HOOK_HANDLED = new IntPtr(-1);
public const int HWND_TOPMOST = -1; public const int HWND_TOPMOST = -1;
@@ -17,7 +16,6 @@ namespace TweetDuck.Core.Utils{
public const int GWL_STYLE = -16; public const int GWL_STYLE = -16;
public const int SB_HORZ = 0; public const int SB_HORZ = 0;
public const int BCM_SETSHIELD = 0x160C;
public const int WM_MOUSE_LL = 14; public const int WM_MOUSE_LL = 14;
public const int WM_MOUSEWHEEL = 0x020A; public const int WM_MOUSEWHEEL = 0x020A;
@@ -66,12 +64,6 @@ namespace TweetDuck.Core.Utils{
[return: MarshalAs(UnmanagedType.Bool)] [return: MarshalAs(UnmanagedType.Bool)]
private static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, uint dwRop); private static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, uint dwRop);
[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, int wParam, IntPtr lParam);
[DllImport("user32.dll")]
public static extern uint RegisterWindowMessage(string messageName);
[DllImport("user32.dll")] [DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)] [return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ShowScrollBar(IntPtr hWnd, int wBar, bool bShow); public static extern bool ShowScrollBar(IntPtr hWnd, int wBar, bool bShow);

22
Core/Utils/StringUtils.cs Normal file
View File

@@ -0,0 +1,22 @@
using System;
using System.Linq;
using System.Text.RegularExpressions;
namespace TweetDuck.Core.Utils{
static class StringUtils{
public static readonly string[] EmptyArray = new string[0];
public static string ExtractBefore(string str, char search, int startIndex = 0){
int index = str.IndexOf(search, startIndex);
return index == -1 ? str : str.Substring(0, index);
}
public static int[] ParseInts(string str, char separator){
return str.Split(new char[]{ separator }, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse).ToArray();
}
public static string ConvertPascalCaseToScreamingSnakeCase(string str){
return Regex.Replace(str, @"(\p{Ll})(\P{Ll})|(\P{Ll})(\P{Ll}\p{Ll})", "$1$3_$2$4").ToUpper();
}
}
}

125
Core/Utils/TwitterUtils.cs Normal file
View File

@@ -0,0 +1,125 @@
using System;
using CefSharp;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using TweetDuck.Core.Other;
namespace TweetDuck.Core.Utils{
static class TwitterUtils{
public const string TweetDeckURL = "https://tweetdeck.twitter.com";
public static readonly Color BackgroundColor = Color.FromArgb(28, 99, 153);
public const string BackgroundColorFix = "let e=document.createElement('style');document.head.appendChild(e);e.innerHTML='body::before{background:#1c6399!important}'";
private static readonly Lazy<Regex> RegexAccountLazy = new Lazy<Regex>(() => new Regex(@"^https?://twitter\.com/(?!signup$|tos$|privacy$)([^/]+)/?$", RegexOptions.Compiled), false);
public static Regex RegexAccount => RegexAccountLazy.Value;
public static readonly string[] DictionaryWords = {
"tweetdeck", "TweetDeck", "tweetduck", "TweetDuck", "TD"
};
public enum ImageQuality{
Default, Orig
}
public static bool IsTweetDeckWebsite(IFrame frame){
return frame.Url.Contains("//tweetdeck.twitter.com/");
}
public static bool IsTwitterWebsite(IFrame frame){
return frame.Url.Contains("//twitter.com/");
}
private static string ExtractMediaBaseLink(string url){
int dot = url.LastIndexOf('/');
return dot == -1 ? url : StringUtils.ExtractBefore(url, ':', dot);
}
public static string GetMediaLink(string url, ImageQuality quality){
if (quality == ImageQuality.Orig){
string result = ExtractMediaBaseLink(url);
if (result != url || url.Contains("//pbs.twimg.com/media/")){
result += ":orig";
}
return result;
}
else{
return url;
}
}
public static void DownloadImage(string url, string username, ImageQuality quality){
DownloadImages(new string[]{ url }, username, quality);
}
public static void DownloadImages(string[] urls, string username, ImageQuality quality){
if (urls.Length == 0){
return;
}
string firstImageLink = GetMediaLink(urls[0], quality);
int qualityIndex = firstImageLink.IndexOf(':', firstImageLink.LastIndexOf('/'));
string file = BrowserUtils.GetFileNameFromUrl(ExtractMediaBaseLink(firstImageLink));
string ext = Path.GetExtension(file); // includes dot
string[] fileNameParts = qualityIndex == -1 ? new string[]{
Path.ChangeExtension(file, null)
} : new string[]{
username,
Path.ChangeExtension(file, null),
firstImageLink.Substring(qualityIndex+1)
};
using(SaveFileDialog dialog = new SaveFileDialog{
AutoUpgradeEnabled = true,
OverwritePrompt = urls.Length == 1,
Title = "Save image",
FileName = $"{string.Join(" ", fileNameParts.Where(part => part.Length > 0))}{ext}",
Filter = (urls.Length == 1 ? "Image" : "Images")+(string.IsNullOrEmpty(ext) ? " (unknown)|*.*" : $" (*{ext})|*{ext}")
}){
if (dialog.ShowDialog() == DialogResult.OK){
void OnFailure(Exception ex){
FormMessage.Error("Image Download", "An error occurred while downloading the image: "+ex.Message, FormMessage.OK);
}
if (urls.Length == 1){
BrowserUtils.DownloadFileAsync(firstImageLink, dialog.FileName, null, OnFailure);
}
else{
string pathBase = Path.ChangeExtension(dialog.FileName, null);
string pathExt = Path.GetExtension(dialog.FileName);
for(int index = 0; index < urls.Length; index++){
BrowserUtils.DownloadFileAsync(GetMediaLink(urls[index], quality), $"{pathBase} {index+1}{pathExt}", null, OnFailure);
}
}
}
}
}
public static void DownloadVideo(string url){
string filename = BrowserUtils.GetFileNameFromUrl(url);
string ext = Path.GetExtension(filename);
using(SaveFileDialog dialog = new SaveFileDialog{
AutoUpgradeEnabled = true,
OverwritePrompt = true,
Title = "Save video",
FileName = filename,
Filter = "Video"+(string.IsNullOrEmpty(ext) ? " (unknown)|*.*" : $" (*{ext})|*{ext}")
}){
if (dialog.ShowDialog() == DialogResult.OK){
BrowserUtils.DownloadFileAsync(url, dialog.FileName, null, ex => {
FormMessage.Error("Image Download", "An error occurred while downloading the image: "+ex.Message, FormMessage.OK);
});
}
}
}
}
}

View File

@@ -1,6 +1,7 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Management;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
@@ -11,13 +12,29 @@ namespace TweetDuck.Core.Utils{
private static readonly Lazy<Regex> RegexStripHtmlStyles = new Lazy<Regex>(() => new Regex(@"\s?(?:style|class)="".*?"""), false); private static readonly Lazy<Regex> RegexStripHtmlStyles = new Lazy<Regex>(() => new Regex(@"\s?(?:style|class)="".*?"""), false);
private static readonly Lazy<Regex> RegexOffsetClipboardHtml = new Lazy<Regex>(() => new Regex(@"(?<=EndHTML:|EndFragment:)(\d+)"), false); private static readonly Lazy<Regex> RegexOffsetClipboardHtml = new Lazy<Regex>(() => new Regex(@"(?<=EndHTML:|EndFragment:)(\d+)"), false);
public static int CurrentProcessID { get; }
public static bool ShouldAvoidToolWindow { get; } public static bool ShouldAvoidToolWindow { get; }
static WindowsUtils(){ static WindowsUtils(){
using(Process me = Process.GetCurrentProcess()){
CurrentProcessID = me.Id;
}
Version ver = Environment.OSVersion.Version; Version ver = Environment.OSVersion.Version;
ShouldAvoidToolWindow = ver.Major == 6 && ver.Minor == 2; // windows 8/10 ShouldAvoidToolWindow = ver.Major == 6 && ver.Minor == 2; // windows 8/10
} }
public static void CreateDirectoryForFile(string file){
string dir = Path.GetDirectoryName(file);
if (dir == null){
throw new ArgumentException("Invalid file path: "+file);
}
else if (dir.Length > 0){
Directory.CreateDirectory(dir);
}
}
public static bool CheckFolderWritePermission(string path){ public static bool CheckFolderWritePermission(string path){
string testFile = Path.Combine(path, ".test"); string testFile = Path.Combine(path, ".test");
@@ -72,6 +89,20 @@ namespace TweetDuck.Core.Utils{
}).Start(); }).Start();
} }
public static bool IsChildProcess(int pid){
try{
using(ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT ParentProcessId FROM Win32_Process WHERE ProcessId = "+pid)){
foreach(ManagementBaseObject obj in searcher.Get()){
return (uint)obj["ParentProcessId"] == CurrentProcessID;
}
}
return false;
}catch{
return false;
}
}
public static void ClipboardStripHtmlStyles(){ public static void ClipboardStripHtmlStyles(){
if (!Clipboard.ContainsText(TextDataFormat.Html)){ if (!Clipboard.ContainsText(TextDataFormat.Html)){
return; return;
@@ -105,7 +136,7 @@ namespace TweetDuck.Core.Utils{
try{ try{
Clipboard.SetDataObject(obj); Clipboard.SetDataObject(obj);
}catch(ExternalException e){ }catch(ExternalException e){
Program.Reporter.HandleException("Clipboard Error", Program.BrandName+" could not access the clipboard as it is currently used by another process.", true, e); Program.Reporter.HandleException("Clipboard Error", "TweetDuck could not access the clipboard as it is currently used by another process.", true, e);
} }
} }
} }

View File

@@ -1,9 +1,10 @@
using System; using System;
using System.IO; using System.IO;
using System.Text; using System.Text;
using TweetDuck.Core.Utils;
namespace TweetDuck.Core.Other.Settings.Export{ namespace TweetDuck.Data{
class CombinedFileStream : IDisposable{ sealed class CombinedFileStream : IDisposable{
public const char KeySeparator = '|'; public const char KeySeparator = '|';
private readonly Stream stream; private readonly Stream stream;
@@ -79,8 +80,7 @@ namespace TweetDuck.Core.Other.Settings.Export{
stream.Position += BitConverter.ToInt32(contentLength, 0); stream.Position += BitConverter.ToInt32(contentLength, 0);
string keyName = Encoding.UTF8.GetString(name); string keyName = Encoding.UTF8.GetString(name);
int separatorIndex = keyName.IndexOf(KeySeparator); return StringUtils.ExtractBefore(keyName, KeySeparator);
return separatorIndex == -1 ? keyName : keyName.Substring(0, separatorIndex);
} }
public void Flush(){ public void Flush(){
@@ -91,20 +91,19 @@ namespace TweetDuck.Core.Other.Settings.Export{
stream.Dispose(); stream.Dispose();
} }
public class Entry{ public sealed class Entry{
public string Identifier { get; } public string Identifier { get; }
public string KeyName{ public string KeyName{
get{ get{
int index = Identifier.IndexOf(KeySeparator); return StringUtils.ExtractBefore(Identifier, KeySeparator);
return index == -1 ? Identifier : Identifier.Substring(0, index);
} }
} }
public string[] KeyValue{ public string[] KeyValue{
get{ get{
int index = Identifier.IndexOf(KeySeparator); int index = Identifier.IndexOf(KeySeparator);
return index == -1 ? new string[0] : Identifier.Substring(index+1).Split(KeySeparator); return index == -1 ? StringUtils.EmptyArray : Identifier.Substring(index+1).Split(KeySeparator);
} }
} }
@@ -121,11 +120,7 @@ namespace TweetDuck.Core.Other.Settings.Export{
public void WriteToFile(string path, bool createDirectory){ public void WriteToFile(string path, bool createDirectory){
if (createDirectory){ if (createDirectory){
string dir = Path.GetDirectoryName(path); WindowsUtils.CreateDirectoryForFile(path);
if (!string.IsNullOrEmpty(dir)){
Directory.CreateDirectory(dir);
}
} }
File.WriteAllBytes(path, contents); File.WriteAllBytes(path, contents);

View File

@@ -1,8 +1,9 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using System.Text.RegularExpressions;
namespace TweetDuck.Core.Utils{ namespace TweetDuck.Data{
class CommandLineArgs{ sealed class CommandLineArgs{
public static CommandLineArgs FromStringArray(char entryChar, string[] array){ public static CommandLineArgs FromStringArray(char entryChar, string[] array){
CommandLineArgs args = new CommandLineArgs(); CommandLineArgs args = new CommandLineArgs();
ReadStringArray(entryChar, array, args); ReadStringArray(entryChar, array, args);
@@ -32,37 +33,67 @@ namespace TweetDuck.Core.Utils{
} }
} }
public static CommandLineArgs ReadCefArguments(string argumentString){
CommandLineArgs args = new CommandLineArgs();
if (string.IsNullOrWhiteSpace(argumentString)){
return args;
}
foreach(Match match in Regex.Matches(argumentString, @"([^=\s]+(?:=(?:\S*""[^""]*?""\S*|\S*))?)")){
string matchValue = match.Value;
int indexEquals = matchValue.IndexOf('=');
string key, value;
if (indexEquals == -1){
key = matchValue.TrimStart('-');
value = "1";
}
else{
key = matchValue.Substring(0, indexEquals).TrimStart('-');
value = matchValue.Substring(indexEquals+1).Trim('"');
}
if (key.Length != 0){
args.SetValue(key, value);
}
}
return args;
}
private readonly HashSet<string> flags = new HashSet<string>(); private readonly HashSet<string> flags = new HashSet<string>();
private readonly Dictionary<string, string> values = new Dictionary<string, string>(); private readonly Dictionary<string, string> values = new Dictionary<string, string>();
public int Count => flags.Count+values.Count; public int Count => flags.Count+values.Count;
public void AddFlag(string flag){ public void AddFlag(string flag){
flags.Add(flag.ToLowerInvariant()); flags.Add(flag.ToLower());
} }
public bool HasFlag(string flag){ public bool HasFlag(string flag){
return flags.Contains(flag.ToLowerInvariant()); return flags.Contains(flag.ToLower());
} }
public void RemoveFlag(string flag){ public void RemoveFlag(string flag){
flags.Remove(flag.ToLowerInvariant()); flags.Remove(flag.ToLower());
} }
public void SetValue(string key, string value){ public void SetValue(string key, string value){
values[key.ToLowerInvariant()] = value; values[key.ToLower()] = value;
} }
public bool HasValue(string key){ public bool HasValue(string key){
return values.ContainsKey(key.ToLowerInvariant()); return values.ContainsKey(key.ToLower());
} }
public string GetValue(string key, string defaultValue){ public string GetValue(string key, string defaultValue){
return values.TryGetValue(key.ToLowerInvariant(), out string val) ? val : defaultValue; return values.TryGetValue(key.ToLower(), out string val) ? val : defaultValue;
} }
public void RemoveValue(string key){ public void RemoveValue(string key){
values.Remove(key.ToLowerInvariant()); values.Remove(key.ToLower());
} }
public CommandLineArgs Clone(){ public CommandLineArgs Clone(){

View File

@@ -1,7 +1,7 @@
using System; using System;
namespace TweetDuck.Core.Utils{ namespace TweetDuck.Data{
class InjectedHTML{ sealed class InjectedHTML{
public enum Position{ public enum Position{
Before, After Before, After
} }

View File

@@ -0,0 +1,146 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
namespace TweetDuck.Data.Serialization{
sealed class FileSerializer<T>{
private const string NewLineReal = "\r\n";
private const string NewLineCustom = "\r~\n";
private static readonly ITypeConverter BasicSerializerObj = new BasicTypeConverter();
public delegate void HandleUnknownPropertiesHandler(T obj, Dictionary<string, string> data);
public HandleUnknownPropertiesHandler HandleUnknownProperties { get; set; }
private readonly Dictionary<string, PropertyInfo> props;
private readonly Dictionary<Type, ITypeConverter> converters;
public FileSerializer(){
this.props = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(prop => prop.CanWrite).ToDictionary(prop => prop.Name);
this.converters = new Dictionary<Type, ITypeConverter>();
}
public void RegisterTypeConverter(Type type, ITypeConverter converter){
converters[type] = converter;
}
public void Write(string file, T obj){
using(StreamWriter writer = new StreamWriter(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None))){
foreach(KeyValuePair<string, PropertyInfo> prop in props){
Type type = prop.Value.PropertyType;
object value = prop.Value.GetValue(obj);
if (!converters.TryGetValue(type, out ITypeConverter serializer)) {
serializer = BasicSerializerObj;
}
if (serializer.TryWriteType(type, value, out string converted)){
if (converted != null){
writer.Write($"{prop.Key} {converted.Replace(Environment.NewLine, NewLineCustom)}");
writer.Write(NewLineReal);
}
}
else{
throw new SerializationException($"Invalid serialization type, conversion failed for: {type}");
}
}
}
}
public void Read(string file, T obj){
Dictionary<string, string> unknownProperties = new Dictionary<string, string>(4);
using(StreamReader reader = new StreamReader(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))){
if (reader.Peek() <= 1){
throw new FormatException("Input appears to be a binary file.");
}
foreach(string line in reader.ReadToEnd().Split(new string[]{ NewLineReal }, StringSplitOptions.RemoveEmptyEntries)){
int space = line.IndexOf(' ');
if (space == -1){
throw new SerializationException($"Invalid file format, missing separator: {line}");
}
string property = line.Substring(0, space);
string value = line.Substring(space+1).Replace(NewLineCustom, Environment.NewLine);
if (props.TryGetValue(property, out PropertyInfo info)){
if (!converters.TryGetValue(info.PropertyType, out ITypeConverter serializer)) {
serializer = BasicSerializerObj;
}
if (serializer.TryReadType(info.PropertyType, value, out object converted)){
info.SetValue(obj, converted);
}
else{
throw new SerializationException($"Invalid file format, cannot convert value: {value} (property: {property})");
}
}
else{
unknownProperties[property] = value;
}
}
}
if (unknownProperties.Count > 0){
HandleUnknownProperties?.Invoke(obj, unknownProperties);
if (unknownProperties.Count > 0){
throw new SerializationException($"Invalid file format, unknown properties: {string.Join(", ", unknownProperties.Keys)}");
}
}
}
private sealed class BasicTypeConverter : ITypeConverter{
bool ITypeConverter.TryWriteType(Type type, object value, out string converted){
switch(Type.GetTypeCode(type)){
case TypeCode.Boolean:
converted = value.ToString();
return true;
case TypeCode.Int32:
converted = ((int)value).ToString(); // cast required for enums
return true;
case TypeCode.String:
converted = value?.ToString();
return true;
default:
converted = null;
return false;
}
}
bool ITypeConverter.TryReadType(Type type, string value, out object converted){
switch(Type.GetTypeCode(type)){
case TypeCode.Boolean:
if (bool.TryParse(value, out bool b)){
converted = b;
return true;
}
else goto default;
case TypeCode.Int32:
if (int.TryParse(value, out int i)){
converted = i;
return true;
}
else goto default;
case TypeCode.String:
converted = value;
return true;
default:
converted = null;
return false;
}
}
}
}
}

View File

@@ -0,0 +1,8 @@
using System;
namespace TweetDuck.Data.Serialization{
interface ITypeConverter{
bool TryWriteType(Type type, object value, out string converted);
bool TryReadType(Type type, string value, out object converted);
}
}

View File

@@ -0,0 +1,28 @@
using System;
namespace TweetDuck.Data.Serialization{
sealed class SingleTypeConverter<T> : ITypeConverter{
public Func<T, string> ConvertToString { get; set; }
public Func<string, T> ConvertToObject { get; set; }
bool ITypeConverter.TryWriteType(Type type, object value, out string converted){
try{
converted = ConvertToString((T)value);
return true;
}catch{
converted = null;
return false;
}
}
bool ITypeConverter.TryReadType(Type type, string value, out object converted){
try{
converted = ConvertToObject(value);
return true;
}catch{
converted = null;
return false;
}
}
}
}

View File

@@ -1,8 +1,8 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
namespace TweetDuck.Core.Utils{ namespace TweetDuck.Data{
class TwoKeyDictionary<K1, K2, V>{ sealed class TwoKeyDictionary<K1, K2, V>{
private readonly Dictionary<K1, Dictionary<K2, V>> dict; private readonly Dictionary<K1, Dictionary<K2, V>> dict;
private readonly int innerCapacity; private readonly int innerCapacity;

View File

@@ -1,11 +1,11 @@
using System; using System.Drawing;
using System.Drawing;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Utils;
using TweetDuck.Data.Serialization;
namespace TweetDuck.Core.Utils{ namespace TweetDuck.Data{
[Serializable] sealed class WindowState{
class WindowState{
private Rectangle rect; private Rectangle rect;
private bool isMaximized; private bool isMaximized;
@@ -26,5 +26,17 @@ namespace TweetDuck.Core.Utils{
Save(form); Save(form);
} }
} }
public static readonly SingleTypeConverter<WindowState> Converter = new SingleTypeConverter<WindowState>{
ConvertToString = value => $"{(value.isMaximized ? 'M' : '_')}{value.rect.X} {value.rect.Y} {value.rect.Width} {value.rect.Height}",
ConvertToObject = value => {
int[] elements = StringUtils.ParseInts(value.Substring(1), ' ');
return new WindowState{
rect = new Rectangle(elements[0], elements[1], elements[2], elements[3]),
isMaximized = value[0] == 'M'
};
}
};
} }
} }

View File

@@ -32,6 +32,7 @@
this.labelWebsite = new System.Windows.Forms.Label(); this.labelWebsite = new System.Windows.Forms.Label();
this.labelVersion = new System.Windows.Forms.Label(); this.labelVersion = new System.Windows.Forms.Label();
this.btnOpenConfig = new System.Windows.Forms.Button(); this.btnOpenConfig = new System.Windows.Forms.Button();
this.labelType = new TweetDuck.Core.Controls.LabelVertical();
this.panelDescription.SuspendLayout(); this.panelDescription.SuspendLayout();
this.flowLayoutInfo.SuspendLayout(); this.flowLayoutInfo.SuspendLayout();
this.SuspendLayout(); this.SuspendLayout();
@@ -39,7 +40,7 @@
// btnToggleState // btnToggleState
// //
this.btnToggleState.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.btnToggleState.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnToggleState.Location = new System.Drawing.Point(459, 80); this.btnToggleState.Location = new System.Drawing.Point(456, 80);
this.btnToggleState.Name = "btnToggleState"; this.btnToggleState.Name = "btnToggleState";
this.btnToggleState.Size = new System.Drawing.Size(65, 23); this.btnToggleState.Size = new System.Drawing.Size(65, 23);
this.btnToggleState.TabIndex = 5; this.btnToggleState.TabIndex = 5;
@@ -51,7 +52,7 @@
// //
this.labelName.AutoSize = true; this.labelName.AutoSize = true;
this.labelName.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); this.labelName.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelName.Location = new System.Drawing.Point(7, 7); this.labelName.Location = new System.Drawing.Point(24, 7);
this.labelName.Name = "labelName"; this.labelName.Name = "labelName";
this.labelName.Size = new System.Drawing.Size(61, 24); this.labelName.Size = new System.Drawing.Size(61, 24);
this.labelName.TabIndex = 0; this.labelName.TabIndex = 0;
@@ -65,9 +66,9 @@
| System.Windows.Forms.AnchorStyles.Right))); | System.Windows.Forms.AnchorStyles.Right)));
this.panelDescription.AutoScroll = true; this.panelDescription.AutoScroll = true;
this.panelDescription.Controls.Add(this.labelDescription); this.panelDescription.Controls.Add(this.labelDescription);
this.panelDescription.Location = new System.Drawing.Point(11, 35); this.panelDescription.Location = new System.Drawing.Point(28, 35);
this.panelDescription.Name = "panelDescription"; this.panelDescription.Name = "panelDescription";
this.panelDescription.Size = new System.Drawing.Size(513, 39); this.panelDescription.Size = new System.Drawing.Size(493, 39);
this.panelDescription.TabIndex = 2; this.panelDescription.TabIndex = 2;
this.panelDescription.Resize += new System.EventHandler(this.panelDescription_Resize); this.panelDescription.Resize += new System.EventHandler(this.panelDescription_Resize);
// //
@@ -101,9 +102,9 @@
| System.Windows.Forms.AnchorStyles.Right))); | System.Windows.Forms.AnchorStyles.Right)));
this.flowLayoutInfo.Controls.Add(this.labelAuthor); this.flowLayoutInfo.Controls.Add(this.labelAuthor);
this.flowLayoutInfo.Controls.Add(this.labelWebsite); this.flowLayoutInfo.Controls.Add(this.labelWebsite);
this.flowLayoutInfo.Location = new System.Drawing.Point(11, 85); this.flowLayoutInfo.Location = new System.Drawing.Point(28, 85);
this.flowLayoutInfo.Name = "flowLayoutInfo"; this.flowLayoutInfo.Name = "flowLayoutInfo";
this.flowLayoutInfo.Size = new System.Drawing.Size(368, 18); this.flowLayoutInfo.Size = new System.Drawing.Size(348, 18);
this.flowLayoutInfo.TabIndex = 3; this.flowLayoutInfo.TabIndex = 3;
this.flowLayoutInfo.WrapContents = false; this.flowLayoutInfo.WrapContents = false;
// //
@@ -128,7 +129,7 @@
this.labelVersion.Location = new System.Drawing.Point(14, 12); this.labelVersion.Location = new System.Drawing.Point(14, 12);
this.labelVersion.Margin = new System.Windows.Forms.Padding(3, 9, 3, 0); this.labelVersion.Margin = new System.Windows.Forms.Padding(3, 9, 3, 0);
this.labelVersion.Name = "labelVersion"; this.labelVersion.Name = "labelVersion";
this.labelVersion.Size = new System.Drawing.Size(513, 13); this.labelVersion.Size = new System.Drawing.Size(510, 13);
this.labelVersion.TabIndex = 1; this.labelVersion.TabIndex = 1;
this.labelVersion.Text = "Version"; this.labelVersion.Text = "Version";
this.labelVersion.TextAlign = System.Drawing.ContentAlignment.TopRight; this.labelVersion.TextAlign = System.Drawing.ContentAlignment.TopRight;
@@ -137,7 +138,7 @@
// btnOpenConfig // btnOpenConfig
// //
this.btnOpenConfig.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.btnOpenConfig.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnOpenConfig.Location = new System.Drawing.Point(385, 80); this.btnOpenConfig.Location = new System.Drawing.Point(382, 80);
this.btnOpenConfig.Name = "btnOpenConfig"; this.btnOpenConfig.Name = "btnOpenConfig";
this.btnOpenConfig.Size = new System.Drawing.Size(68, 23); this.btnOpenConfig.Size = new System.Drawing.Size(68, 23);
this.btnOpenConfig.TabIndex = 4; this.btnOpenConfig.TabIndex = 4;
@@ -145,20 +146,34 @@
this.btnOpenConfig.UseVisualStyleBackColor = true; this.btnOpenConfig.UseVisualStyleBackColor = true;
this.btnOpenConfig.Click += new System.EventHandler(this.btnOpenConfig_Click); this.btnOpenConfig.Click += new System.EventHandler(this.btnOpenConfig_Click);
// //
// labelType
//
this.labelType.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)));
this.labelType.BackColor = System.Drawing.Color.DarkGray;
this.labelType.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelType.Location = new System.Drawing.Point(0, 0);
this.labelType.Name = "labelType";
this.labelType.Size = new System.Drawing.Size(18, 109);
this.labelType.TabIndex = 6;
this.labelType.Text = "TYPE";
//
// PluginControl // PluginControl
// //
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.labelType);
this.Controls.Add(this.btnOpenConfig); this.Controls.Add(this.btnOpenConfig);
this.Controls.Add(this.flowLayoutInfo); this.Controls.Add(this.flowLayoutInfo);
this.Controls.Add(this.panelDescription); this.Controls.Add(this.panelDescription);
this.Controls.Add(this.labelName); this.Controls.Add(this.labelName);
this.Controls.Add(this.btnToggleState); this.Controls.Add(this.btnToggleState);
this.Controls.Add(this.labelVersion); this.Controls.Add(this.labelVersion);
this.Margin = new System.Windows.Forms.Padding(0);
this.MaximumSize = new System.Drawing.Size(65535, 109); this.MaximumSize = new System.Drawing.Size(65535, 109);
this.MinimumSize = new System.Drawing.Size(0, 61); this.MinimumSize = new System.Drawing.Size(0, 61);
this.Name = "PluginControl"; this.Name = "PluginControl";
this.Padding = new System.Windows.Forms.Padding(3); this.Padding = new System.Windows.Forms.Padding(3, 3, 6, 3);
this.Size = new System.Drawing.Size(530, 109); this.Size = new System.Drawing.Size(530, 109);
this.panelDescription.ResumeLayout(false); this.panelDescription.ResumeLayout(false);
this.panelDescription.PerformLayout(); this.panelDescription.PerformLayout();
@@ -180,5 +195,6 @@
private System.Windows.Forms.Label labelWebsite; private System.Windows.Forms.Label labelWebsite;
private System.Windows.Forms.Label labelVersion; private System.Windows.Forms.Label labelVersion;
private System.Windows.Forms.Button btnOpenConfig; private System.Windows.Forms.Button btnOpenConfig;
private Core.Controls.LabelVertical labelType;
} }
} }

View File

@@ -3,13 +3,17 @@ using System.Diagnostics;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Controls;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetDuck.Plugins.Enums;
namespace TweetDuck.Plugins.Controls{ namespace TweetDuck.Plugins.Controls{
partial class PluginControl : UserControl{ sealed partial class PluginControl : UserControl{
private readonly PluginManager pluginManager; private readonly PluginManager pluginManager;
private readonly Plugin plugin; private readonly Plugin plugin;
private readonly float dpiScale;
public PluginControl(){ public PluginControl(){
InitializeComponent(); InitializeComponent();
} }
@@ -18,19 +22,23 @@ namespace TweetDuck.Plugins.Controls{
this.pluginManager = pluginManager; this.pluginManager = pluginManager;
this.plugin = plugin; this.plugin = plugin;
this.dpiScale = this.GetDPIScale();
this.labelName.Text = plugin.Name; this.labelName.Text = plugin.Name;
this.labelDescription.Text = plugin.CanRun ? plugin.Description : "This plugin requires "+Program.BrandName+" "+plugin.RequiredVersion+" or newer."; this.labelDescription.Text = plugin.CanRun ? plugin.Description : "This plugin requires TweetDuck "+plugin.RequiredVersion+" or newer.";
this.labelVersion.Text = plugin.Version; this.labelVersion.Text = plugin.Version;
this.labelAuthor.Text = plugin.Author; this.labelAuthor.Text = plugin.Author;
this.labelWebsite.Text = plugin.Website; this.labelWebsite.Text = plugin.Website;
this.labelType.LineHeight = BrowserUtils.Scale(9, dpiScale);
UpdatePluginState(); UpdatePluginState();
if (labelDescription.Text.Length == 0){ if (labelDescription.Text.Length == 0){
labelDescription.Visible = false; labelDescription.Visible = false;
} }
panelDescription_Resize(panelDescription, new EventArgs()); panelDescription_Resize(panelDescription, null);
} }
private void panelDescription_Resize(object sender, EventArgs e){ private void panelDescription_Resize(object sender, EventArgs e){
@@ -39,7 +47,7 @@ namespace TweetDuck.Plugins.Controls{
} }
else{ else{
labelDescription.MaximumSize = new Size(panelDescription.Width-SystemInformation.VerticalScrollBarWidth, 0); labelDescription.MaximumSize = new Size(panelDescription.Width-SystemInformation.VerticalScrollBarWidth, 0);
Height = Math.Min(MinimumSize.Height+9+labelDescription.Height, MaximumSize.Height); Height = Math.Min(MinimumSize.Height+BrowserUtils.Scale(9, dpiScale)+labelDescription.Height, MaximumSize.Height);
} }
} }
@@ -68,6 +76,15 @@ namespace TweetDuck.Plugins.Controls{
labelAuthor.ForeColor = textColor; labelAuthor.ForeColor = textColor;
labelWebsite.ForeColor = isEnabled ? Color.Blue : Color.FromArgb(90, 90, 249); labelWebsite.ForeColor = isEnabled ? Color.Blue : Color.FromArgb(90, 90, 249);
if (plugin.Group == PluginGroup.Official){
labelType.Text = "OFFICIAL";
labelType.BackColor = isEnabled ? Color.FromArgb(154, 195, 217) : Color.FromArgb(185, 185, 185);
}
else{
labelType.Text = "CUSTOM";
labelType.BackColor = isEnabled ? Color.FromArgb(208, 154, 217) : Color.FromArgb(185, 185, 185);
}
if (plugin.CanRun){ if (plugin.CanRun){
labelName.ForeColor = textColor; labelName.ForeColor = textColor;
labelDescription.ForeColor = textColor; labelDescription.ForeColor = textColor;

View File

@@ -17,7 +17,20 @@ namespace TweetDuck.Plugins.Enums{
} }
} }
public static string GetScriptFile(this PluginEnvironment environment){ public static bool IncludesDisabledPlugins(this PluginEnvironment environment){
return environment == PluginEnvironment.Browser;
}
public static string GetScriptIdentifier(this PluginEnvironment environment){
switch(environment){
case PluginEnvironment.None: return "root:plugins";
case PluginEnvironment.Browser: return "root:plugins.browser";
case PluginEnvironment.Notification: return "root:plugins.notification";
default: return null;
}
}
public static string GetPluginScriptFile(this PluginEnvironment environment){
switch(environment){ switch(environment){
case PluginEnvironment.Browser: return "browser.js"; case PluginEnvironment.Browser: return "browser.js";
case PluginEnvironment.Notification: return "notification.js"; case PluginEnvironment.Notification: return "notification.js";
@@ -25,7 +38,7 @@ namespace TweetDuck.Plugins.Enums{
} }
} }
public static string GetScriptVariables(this PluginEnvironment environment){ public static string GetPluginScriptVariables(this PluginEnvironment environment){
switch(environment){ switch(environment){
case PluginEnvironment.Browser: return "$,$TD,$TDP,TD"; case PluginEnvironment.Browser: return "$,$TD,$TDP,TD";
case PluginEnvironment.Notification: return "$TD,$TDP"; case PluginEnvironment.Notification: return "$TD,$TDP";

View File

@@ -1,7 +1,7 @@
using System; using System;
namespace TweetDuck.Plugins.Events{ namespace TweetDuck.Plugins.Events{
class PluginChangedStateEventArgs : EventArgs{ sealed class PluginChangedStateEventArgs : EventArgs{
public Plugin Plugin { get; } public Plugin Plugin { get; }
public bool IsEnabled { get; } public bool IsEnabled { get; }

View File

@@ -2,10 +2,10 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace TweetDuck.Plugins.Events{ namespace TweetDuck.Plugins.Events{
class PluginErrorEventArgs : EventArgs{ sealed class PluginErrorEventArgs : EventArgs{
public bool HasErrors => Errors.Count > 0; public bool HasErrors => Errors.Count > 0;
public IList<string> Errors; public IList<string> Errors { get; }
public PluginErrorEventArgs(IList<string> errors){ public PluginErrorEventArgs(IList<string> errors){
this.Errors = errors; this.Errors = errors;

View File

@@ -6,7 +6,10 @@ using System.Text;
using TweetDuck.Plugins.Enums; using TweetDuck.Plugins.Enums;
namespace TweetDuck.Plugins{ namespace TweetDuck.Plugins{
class Plugin{ sealed class Plugin{
private static readonly Version AppVersion = new Version(Program.VersionTag);
private const string VersionWildcard = "*";
public string Identifier { get; } public string Identifier { get; }
public PluginGroup Group { get; } public PluginGroup Group { get; }
public PluginEnvironment Environments { get; private set; } public PluginEnvironment Environments { get; private set; }
@@ -42,7 +45,7 @@ namespace TweetDuck.Plugins{
private readonly string pathRoot; private readonly string pathRoot;
private readonly string pathData; private readonly string pathData;
private readonly Dictionary<string, string> metadata = new Dictionary<string, string>(4){ private readonly Dictionary<string, string> metadata = new Dictionary<string, string>(8){
{ "NAME", "" }, { "NAME", "" },
{ "DESCRIPTION", "" }, { "DESCRIPTION", "" },
{ "AUTHOR", "(anonymous)" }, { "AUTHOR", "(anonymous)" },
@@ -50,7 +53,7 @@ namespace TweetDuck.Plugins{
{ "WEBSITE", "" }, { "WEBSITE", "" },
{ "CONFIGFILE", "" }, { "CONFIGFILE", "" },
{ "CONFIGDEFAULT", "" }, { "CONFIGDEFAULT", "" },
{ "REQUIRES", "*" } { "REQUIRES", VersionWildcard }
}; };
private bool? canRun; private bool? canRun;
@@ -84,7 +87,7 @@ namespace TweetDuck.Plugins{
public string GetScriptPath(PluginEnvironment environment){ public string GetScriptPath(PluginEnvironment environment){
if (Environments.HasFlag(environment)){ if (Environments.HasFlag(environment)){
string file = environment.GetScriptFile(); string file = environment.GetPluginScriptFile();
return file != null ? Path.Combine(pathRoot, file) : string.Empty; return file != null ? Path.Combine(pathRoot, file) : string.Empty;
} }
else{ else{
@@ -132,8 +135,7 @@ namespace TweetDuck.Plugins{
} }
public override bool Equals(object obj){ public override bool Equals(object obj){
Plugin plugin = obj as Plugin; return obj is Plugin plugin && plugin.Identifier.Equals(Identifier);
return plugin != null && plugin.Identifier.Equals(Identifier);
} }
public static Plugin CreateFromFolder(string path, PluginGroup group, out string error){ public static Plugin CreateFromFolder(string path, PluginGroup group, out string error){
@@ -153,7 +155,7 @@ namespace TweetDuck.Plugins{
private static bool LoadEnvironments(string path, Plugin plugin, out string error){ private static bool LoadEnvironments(string path, Plugin plugin, out string error){
foreach(string file in Directory.EnumerateFiles(path, "*.js", SearchOption.TopDirectoryOnly).Select(Path.GetFileName)){ foreach(string file in Directory.EnumerateFiles(path, "*.js", SearchOption.TopDirectoryOnly).Select(Path.GetFileName)){
plugin.Environments |= PluginEnvironmentExtensions.Values.FirstOrDefault(env => file.Equals(env.GetScriptFile(), StringComparison.Ordinal)); plugin.Environments |= PluginEnvironmentExtensions.Values.FirstOrDefault(env => file.Equals(env.GetPluginScriptFile(), StringComparison.Ordinal));
} }
if (plugin.Environments == PluginEnvironment.None){ if (plugin.Environments == PluginEnvironment.None){
@@ -184,7 +186,7 @@ namespace TweetDuck.Plugins{
plugin.metadata[currentTag] = currentContents; plugin.metadata[currentTag] = currentContents;
} }
currentTag = line.Substring(1, line.Length-2).ToUpperInvariant(); currentTag = line.Substring(1, line.Length-2).ToUpper();
currentContents = ""; currentContents = "";
if (line.Equals(endTag[0])){ if (line.Equals(endTag[0])){
@@ -210,7 +212,7 @@ namespace TweetDuck.Plugins{
return false; return false;
} }
if (plugin.RequiredVersion.Length == 0 || !(plugin.RequiredVersion.Equals("*") || System.Version.TryParse(plugin.RequiredVersion, out Version _))){ if (plugin.RequiredVersion.Length == 0 || !(plugin.RequiredVersion == VersionWildcard || System.Version.TryParse(plugin.RequiredVersion, out Version _))){
error = "Plugin contains invalid version: "+plugin.RequiredVersion; error = "Plugin contains invalid version: "+plugin.RequiredVersion;
return false; return false;
} }
@@ -222,7 +224,7 @@ namespace TweetDuck.Plugins{
} }
private static bool CheckRequiredVersion(string requires){ private static bool CheckRequiredVersion(string requires){
return requires.Equals("*", StringComparison.Ordinal) || Program.Version >= new Version(requires); return requires == VersionWildcard || AppVersion >= new Version(requires);
} }
} }
} }

View File

@@ -3,11 +3,12 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text; using System.Text;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetDuck.Data;
using TweetDuck.Plugins.Enums; using TweetDuck.Plugins.Enums;
using TweetDuck.Plugins.Events; using TweetDuck.Plugins.Events;
namespace TweetDuck.Plugins{ namespace TweetDuck.Plugins{
class PluginBridge{ sealed class PluginBridge{
private static string SanitizeCacheKey(string key){ private static string SanitizeCacheKey(string key){
return key.Replace('\\', '/').Trim(); return key.Replace('\\', '/').Trim();
} }
@@ -47,9 +48,9 @@ namespace TweetDuck.Plugins{
if (fullPath.Length == 0){ if (fullPath.Length == 0){
switch(folder){ switch(folder){
case PluginFolder.Data: throw new Exception("File path has to be relative to the plugin data folder."); case PluginFolder.Data: throw new ArgumentException("File path has to be relative to the plugin data folder.");
case PluginFolder.Root: throw new Exception("File path has to be relative to the plugin root folder."); case PluginFolder.Root: throw new ArgumentException("File path has to be relative to the plugin root folder.");
default: throw new Exception("Invalid folder type "+folder+", this is a "+Program.BrandName+" error."); default: throw new ArgumentException("Invalid folder type "+folder+", this is a TweetDuck error.");
} }
} }
else{ else{
@@ -67,9 +68,9 @@ namespace TweetDuck.Plugins{
try{ try{
return fileCache[token, cacheKey] = File.ReadAllText(fullPath, Encoding.UTF8); return fileCache[token, cacheKey] = File.ReadAllText(fullPath, Encoding.UTF8);
}catch(FileNotFoundException){ }catch(FileNotFoundException){
throw new Exception("File not found."); throw new FileNotFoundException("File not found.");
}catch(DirectoryNotFoundException){ }catch(DirectoryNotFoundException){
throw new Exception("Directory not found."); throw new DirectoryNotFoundException("Directory not found.");
} }
} }
@@ -78,9 +79,7 @@ namespace TweetDuck.Plugins{
public void WriteFile(int token, string path, string contents){ public void WriteFile(int token, string path, string contents){
string fullPath = GetFullPathOrThrow(token, PluginFolder.Data, path); string fullPath = GetFullPathOrThrow(token, PluginFolder.Data, path);
// ReSharper disable once AssignNullToNotNullAttribute WindowsUtils.CreateDirectoryForFile(fullPath);
Directory.CreateDirectory(Path.GetDirectoryName(fullPath));
File.WriteAllText(fullPath, contents, Encoding.UTF8); File.WriteAllText(fullPath, contents, Encoding.UTF8);
fileCache[token, SanitizeCacheKey(path)] = contents; fileCache[token, SanitizeCacheKey(path)] = contents;
} }

View File

@@ -5,52 +5,50 @@ using System.Text;
using TweetDuck.Plugins.Events; using TweetDuck.Plugins.Events;
namespace TweetDuck.Plugins{ namespace TweetDuck.Plugins{
[Serializable]
sealed class PluginConfig{ sealed class PluginConfig{
[field:NonSerialized]
public event EventHandler<PluginChangedStateEventArgs> InternalPluginChangedState; // should only be accessed from PluginManager public event EventHandler<PluginChangedStateEventArgs> InternalPluginChangedState; // should only be accessed from PluginManager
public IEnumerable<string> DisabledPlugins => Disabled; public IEnumerable<string> DisabledPlugins => disabled;
public bool AnyDisabled => Disabled.Count > 0; public bool AnyDisabled => disabled.Count > 0;
private readonly HashSet<string> Disabled = new HashSet<string>{ private static readonly string[] DefaultDisabled = {
"official/clear-columns", "official/clear-columns",
"official/reply-account" "official/reply-account"
}; };
public void ImportLegacy(PluginConfig config){ private readonly HashSet<string> disabled = new HashSet<string>();
Disabled.Clear();
foreach(string plugin in config.Disabled){
Disabled.Add(plugin);
}
}
public void SetEnabled(Plugin plugin, bool enabled){ public void SetEnabled(Plugin plugin, bool enabled){
if ((enabled && Disabled.Remove(plugin.Identifier)) || (!enabled && Disabled.Add(plugin.Identifier))){ if ((enabled && disabled.Remove(plugin.Identifier)) || (!enabled && disabled.Add(plugin.Identifier))){
InternalPluginChangedState?.Invoke(this, new PluginChangedStateEventArgs(plugin, enabled)); InternalPluginChangedState?.Invoke(this, new PluginChangedStateEventArgs(plugin, enabled));
} }
} }
public bool IsEnabled(Plugin plugin){ public bool IsEnabled(Plugin plugin){
return !Disabled.Contains(plugin.Identifier); return !disabled.Contains(plugin.Identifier);
} }
public void Load(string file){ public void Load(string file){
try{ try{
using(FileStream stream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read)) using(StreamReader reader = new StreamReader(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read), Encoding.UTF8)){
using(StreamReader reader = new StreamReader(stream, Encoding.UTF8)){
string line = reader.ReadLine(); string line = reader.ReadLine();
if (line == "#Disabled"){ if (line == "#Disabled"){
Disabled.Clear(); disabled.Clear();
while((line = reader.ReadLine()) != null){ while((line = reader.ReadLine()) != null){
Disabled.Add(line); disabled.Add(line);
} }
} }
} }
}catch(FileNotFoundException){ }catch(FileNotFoundException){
disabled.Clear();
foreach(string identifier in DefaultDisabled){
disabled.Add(identifier);
}
Save(file);
}catch(DirectoryNotFoundException){ }catch(DirectoryNotFoundException){
}catch(Exception e){ }catch(Exception e){
Program.Reporter.HandleException("Plugin Configuration Error", "Could not read the plugin configuration file. If you continue, the list of disabled plugins will be reset to default.", true, e); Program.Reporter.HandleException("Plugin Configuration Error", "Could not read the plugin configuration file. If you continue, the list of disabled plugins will be reset to default.", true, e);
@@ -59,12 +57,11 @@ namespace TweetDuck.Plugins{
public void Save(string file){ public void Save(string file){
try{ try{
using(FileStream stream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None)) using(StreamWriter writer = new StreamWriter(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None), Encoding.UTF8)){
using(StreamWriter writer = new StreamWriter(stream, Encoding.UTF8)){
writer.WriteLine("#Disabled"); writer.WriteLine("#Disabled");
foreach(string disabled in Disabled){ foreach(string identifier in disabled){
writer.WriteLine(disabled); writer.WriteLine(identifier);
} }
} }
}catch(Exception e){ }catch(Exception e){

View File

@@ -9,12 +9,14 @@ using TweetDuck.Resources;
namespace TweetDuck.Plugins{ namespace TweetDuck.Plugins{
sealed class PluginManager{ sealed class PluginManager{
public const string PluginBrowserScriptFile = "plugins.browser.js";
public const string PluginNotificationScriptFile = "plugins.notification.js";
public const string PluginGlobalScriptFile = "plugins.js";
private const int InvalidToken = 0; private const int InvalidToken = 0;
private static readonly Dictionary<PluginEnvironment, string> PluginSetupScripts = new Dictionary<PluginEnvironment, string>(4){
{ PluginEnvironment.None, ScriptLoader.LoadResource("plugins.js") },
{ PluginEnvironment.Browser, ScriptLoader.LoadResource("plugins.browser.js") },
{ PluginEnvironment.Notification, ScriptLoader.LoadResource("plugins.notification.js") }
};
public string PathOfficialPlugins => Path.Combine(rootPath, "official"); public string PathOfficialPlugins => Path.Combine(rootPath, "official");
public string PathCustomPlugins => Path.Combine(rootPath, "user"); public string PathCustomPlugins => Path.Combine(rootPath, "user");
@@ -42,30 +44,8 @@ namespace TweetDuck.Plugins{
this.Config = new PluginConfig(); this.Config = new PluginConfig();
this.Bridge = new PluginBridge(this); this.Bridge = new PluginBridge(this);
LoadConfig();
Config.InternalPluginChangedState += Config_InternalPluginChangedState;
Program.UserConfigReplaced += Program_UserConfigReplaced;
}
private void LoadConfig(){
#pragma warning disable 612
if (Program.UserConfig.Plugins != null){
Config.ImportLegacy(Program.UserConfig.Plugins);
Config.Save(configPath);
Program.UserConfig.Plugins = null;
Program.UserConfig.Save();
}
#pragma warning restore 612
else{
Config.Load(configPath); Config.Load(configPath);
} Config.InternalPluginChangedState += Config_InternalPluginChangedState;
}
private void Program_UserConfigReplaced(object sender, EventArgs e){
LoadConfig();
Reload();
} }
private void Config_InternalPluginChangedState(object sender, PluginChangedStateEventArgs e){ private void Config_InternalPluginChangedState(object sender, PluginChangedStateEventArgs e){
@@ -77,14 +57,6 @@ namespace TweetDuck.Plugins{
return plugins.Any(plugin => plugin.Identifier.Equals(identifier)); return plugins.Any(plugin => plugin.Identifier.Equals(identifier));
} }
public IEnumerable<Plugin> GetPluginsByGroup(PluginGroup group){
return plugins.Where(plugin => plugin.Group == group);
}
public int CountPluginByGroup(PluginGroup group){
return plugins.Count(plugin => plugin.Group == group);
}
public bool HasAnyPlugin(PluginEnvironment environment){ public bool HasAnyPlugin(PluginEnvironment environment){
return plugins.Any(plugin => plugin.Environments.HasFlag(environment)); return plugins.Any(plugin => plugin.Environments.HasFlag(environment));
} }
@@ -104,6 +76,8 @@ namespace TweetDuck.Plugins{
} }
public void Reload(){ public void Reload(){
Config.Load(configPath);
plugins.Clear(); plugins.Clear();
tokens.Clear(); tokens.Clear();
@@ -120,7 +94,17 @@ namespace TweetDuck.Plugins{
Reloaded?.Invoke(this, new PluginErrorEventArgs(loadErrors)); Reloaded?.Invoke(this, new PluginErrorEventArgs(loadErrors));
} }
public void ExecutePlugins(IFrame frame, PluginEnvironment environment, bool includeDisabled){ public void ExecutePlugins(IFrame frame, PluginEnvironment environment){
if (HasAnyPlugin(environment)){
ScriptLoader.ExecuteScript(frame, PluginSetupScripts[environment], environment.GetScriptIdentifier());
ScriptLoader.ExecuteScript(frame, PluginSetupScripts[PluginEnvironment.None], PluginEnvironment.None.GetScriptIdentifier());
ExecutePluginScripts(frame, environment);
}
}
private void ExecutePluginScripts(IFrame frame, PluginEnvironment environment){
bool includeDisabled = environment.IncludesDisabledPlugins();
if (includeDisabled){ if (includeDisabled){
ScriptLoader.ExecuteScript(frame, PluginScriptGenerator.GenerateConfig(Config), "gen:pluginconfig"); ScriptLoader.ExecuteScript(frame, PluginScriptGenerator.GenerateConfig(Config), "gen:pluginconfig");
} }

View File

@@ -1,5 +1,4 @@
using System.Text; using TweetDuck.Plugins.Enums;
using TweetDuck.Plugins.Enums;
namespace TweetDuck.Plugins{ namespace TweetDuck.Plugins{
static class PluginScriptGenerator{ static class PluginScriptGenerator{
@@ -8,22 +7,29 @@ namespace TweetDuck.Plugins{
} }
public static string GeneratePlugin(string pluginIdentifier, string pluginContents, int pluginToken, PluginEnvironment environment){ public static string GeneratePlugin(string pluginIdentifier, string pluginContents, int pluginToken, PluginEnvironment environment){
StringBuilder build = new StringBuilder(2*pluginIdentifier.Length+pluginContents.Length+165); return PluginGen
.Replace("%params", environment.GetPluginScriptVariables())
build.Append("(function(").Append(environment.GetScriptVariables()).Append("){"); .Replace("%id", pluginIdentifier)
.Replace("%token", pluginToken.ToString())
build.Append("let tmp={"); .Replace("%contents", pluginContents);
build.Append("id:\"").Append(pluginIdentifier).Append("\",");
build.Append("obj:new class extends PluginBase{").Append(pluginContents).Append("}");
build.Append("};");
build.Append("tmp.obj.$id=\"").Append(pluginIdentifier).Append("\";");
build.Append("tmp.obj.$token=").Append(pluginToken).Append(";");
build.Append("window.TD_PLUGINS.install(tmp);");
build.Append("})(").Append(environment.GetScriptVariables()).Append(");");
return build.ToString();
} }
private const string PluginGen = "(function(%params,$d){let tmp={id:'%id',obj:new class extends PluginBase{%contents}};$d(tmp.obj,'$id',{value:'%id'});$d(tmp.obj,'$token',{value:%token});window.TD_PLUGINS.install(tmp);})(%params,Object.defineProperty);";
/* PluginGen
(function(%params, $d){
let tmp = {
id: '%id',
obj: new class extends PluginBase{%contents}
};
$d(tmp.obj, '$id', { value: '%id' });
$d(tmp.obj, '$token', { value: %token });
window.TD_PLUGINS.install(tmp);
})(%params, Object.defineProperty);
*/
} }
} }

View File

@@ -1,29 +1,28 @@
using CefSharp; using CefSharp;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Configuration; using TweetDuck.Configuration;
using TweetDuck.Core; using TweetDuck.Core;
using TweetDuck.Core.Handling; using TweetDuck.Core.Handling.General;
using TweetDuck.Core.Other; using TweetDuck.Core.Other;
using TweetDuck.Core.Other.Settings.Export; using TweetDuck.Core.Other.Settings.Export;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetDuck.Plugins; using TweetDuck.Data;
using TweetDuck.Plugins.Events;
using TweetDuck.Updates; using TweetDuck.Updates;
using TweetLib.Communication;
namespace TweetDuck{ namespace TweetDuck{
static class Program{ static class Program{
public const string BrandName = "TweetDuck"; public const string BrandName = "TweetDuck";
public const string Website = "https://tweetduck.chylex.com"; public const string Website = "https://tweetduck.chylex.com";
public const string VersionTag = "1.7.7"; public const string VersionTag = "1.9.1";
public const string VersionFull = "1.7.7.0";
public static readonly Version Version = new Version(VersionTag);
public static readonly bool IsPortable = File.Exists("makeportable"); public static readonly bool IsPortable = File.Exists("makeportable");
public static readonly string ProgramPath = AppDomain.CurrentDomain.BaseDirectory; public static readonly string ProgramPath = AppDomain.CurrentDomain.BaseDirectory;
@@ -32,106 +31,98 @@ namespace TweetDuck{
public static readonly string ScriptPath = Path.Combine(ProgramPath, "scripts"); public static readonly string ScriptPath = Path.Combine(ProgramPath, "scripts");
public static readonly string PluginPath = Path.Combine(ProgramPath, "plugins"); public static readonly string PluginPath = Path.Combine(ProgramPath, "plugins");
public static readonly string UserConfigFilePath = Path.Combine(StoragePath, "TD_UserConfig.cfg");
public static readonly string SystemConfigFilePath = Path.Combine(StoragePath, "TD_SystemConfig.cfg");
public static readonly string PluginConfigFilePath = Path.Combine(StoragePath, "TD_PluginConfig.cfg");
public static readonly string PluginDataPath = Path.Combine(StoragePath, "TD_Plugins"); public static readonly string PluginDataPath = Path.Combine(StoragePath, "TD_Plugins");
private static readonly string InstallerPath = Path.Combine(StoragePath, "TD_Updates"); private static readonly string InstallerPath = Path.Combine(StoragePath, "TD_Updates");
public static string UserConfigFilePath => Path.Combine(StoragePath, "TD_UserConfig.cfg");
public static string SystemConfigFilePath => Path.Combine(StoragePath, "TD_SystemConfig.cfg");
public static string PluginConfigFilePath => Path.Combine(StoragePath, "TD_PluginConfig.cfg");
private static string ErrorLogFilePath => Path.Combine(StoragePath, "TD_Log.txt"); private static string ErrorLogFilePath => Path.Combine(StoragePath, "TD_Log.txt");
private static string ConsoleLogFilePath => Path.Combine(StoragePath, "TD_Console.txt"); private static string ConsoleLogFilePath => Path.Combine(StoragePath, "TD_Console.txt");
public static uint WindowRestoreMessage; public static uint WindowRestoreMessage;
public static uint SubProcessMessage;
private static readonly LockManager LockManager = new LockManager(Path.Combine(StoragePath, ".lock")); private static readonly LockManager LockManager = new LockManager(Path.Combine(StoragePath, ".lock"));
private static bool HasCleanedUp; private static bool HasCleanedUp;
public static UserConfig UserConfig { get; private set; } public static UserConfig UserConfig { get; private set; }
public static SystemConfig SystemConfig { get; private set; } public static SystemConfig SystemConfig { get; private set; }
public static Reporter Reporter { get; private set; } public static Reporter Reporter { get; }
public static CultureInfo Culture { get; }
public static event EventHandler UserConfigReplaced; static Program(){
Culture = CultureInfo.CurrentCulture;
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture;
#if DEBUG
CultureInfo.DefaultThreadCurrentUICulture = Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-us"); // force english exceptions
#endif
Reporter = new Reporter(ErrorLogFilePath);
Reporter.SetupUnhandledExceptionHandler("TweetDuck Has Failed :(");
}
[STAThread] [STAThread]
private static void Main(){ private static void Main(){
Application.EnableVisualStyles(); Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false); Application.SetCompatibleTextRenderingDefault(false);
WindowRestoreMessage = NativeMethods.RegisterWindowMessage("TweetDuckRestore"); WindowRestoreMessage = Comms.RegisterMessage("TweetDuckRestore");
SubProcessMessage = Comms.RegisterMessage("TweetDuckSubProcess");
if (!WindowsUtils.CheckFolderWritePermission(StoragePath)){ if (!WindowsUtils.CheckFolderWritePermission(StoragePath)){
MessageBox.Show(BrandName+" does not have write permissions to the storage folder: "+StoragePath, "Permission Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); FormMessage.Warning("Permission Error", "TweetDuck does not have write permissions to the storage folder: "+StoragePath, FormMessage.OK);
return; return;
} }
Reporter = new Reporter(ErrorLogFilePath);
Reporter.SetupUnhandledExceptionHandler(BrandName+" Has Failed :(");
if (Arguments.HasFlag(Arguments.ArgRestart)){ if (Arguments.HasFlag(Arguments.ArgRestart)){
for(int attempt = 0; attempt < 21; attempt++){ LockManager.Result lockResult = LockManager.LockWait(10000);
LockManager.Result lockResult = LockManager.Lock();
if (lockResult == LockManager.Result.Success){ while(lockResult != LockManager.Result.Success){
break; if (lockResult == LockManager.Result.Fail){
} FormMessage.Error("TweetDuck Has Failed :(", "An unknown error occurred accessing the data folder. Please, make sure TweetDuck is not already running. If the problem persists, try restarting your system.", FormMessage.OK);
else if (lockResult == LockManager.Result.Fail){
MessageBox.Show("An unknown error occurred accessing the data folder. Please, make sure "+BrandName+" is not already running. If the problem persists, try restarting your system.", BrandName+" Has Failed :(", MessageBoxButtons.OK, MessageBoxIcon.Error);
return; return;
} }
else if (attempt == 20){ else if (!FormMessage.Warning("TweetDuck Cannot Restart", "TweetDuck is taking too long to close.", FormMessage.Retry, FormMessage.Exit)){
using(FormMessage form = new FormMessage(BrandName+" Cannot Restart", BrandName+" is taking too long to close.", MessageBoxIcon.Warning)){
form.CancelButton = form.AddButton("Exit");
form.ActiveControl = form.AddButton("Retry", DialogResult.Retry);
if (form.ShowDialog() == DialogResult.Retry){
attempt /= 2;
continue;
}
return; return;
} }
}
else Thread.Sleep(500); lockResult = LockManager.LockWait(5000);
} }
} }
else{ else{
LockManager.Result lockResult = LockManager.Lock(); LockManager.Result lockResult = LockManager.Lock();
if (lockResult == LockManager.Result.HasProcess){ if (lockResult == LockManager.Result.HasProcess){
if (LockManager.LockingProcess.MainWindowHandle == IntPtr.Zero){ // restore if the original process is in tray if (!LockManager.RestoreLockingProcess(2000) && FormMessage.Error("TweetDuck is Already Running", "Another instance of TweetDuck is already running.\nDo you want to close it?", FormMessage.Yes, FormMessage.No)){
NativeMethods.SendMessage(NativeMethods.HWND_BROADCAST, WindowRestoreMessage, LockManager.LockingProcess.Id, IntPtr.Zero);
if (WindowsUtils.TrySleepUntil(() => {
LockManager.LockingProcess.Refresh();
return LockManager.LockingProcess.HasExited || (LockManager.LockingProcess.MainWindowHandle != IntPtr.Zero && LockManager.LockingProcess.Responding);
}, 2000, 250)){
return; // should trigger on first attempt if succeeded, but wait just in case
}
}
if (MessageBox.Show("Another instance of "+BrandName+" is already running.\r\nDo you want to close it?", BrandName+" is Already Running", MessageBoxButtons.YesNo, MessageBoxIcon.Error, MessageBoxDefaultButton.Button2) == DialogResult.Yes){
if (!LockManager.CloseLockingProcess(10000, 5000)){ if (!LockManager.CloseLockingProcess(10000, 5000)){
MessageBox.Show("Could not close the other process.", BrandName+" Has Failed :(", MessageBoxButtons.OK, MessageBoxIcon.Error); FormMessage.Error("TweetDuck Has Failed :(", "Could not close the other process.", FormMessage.OK);
return; return;
} }
LockManager.Lock(); lockResult = LockManager.Lock();
} }
else return; else return;
} }
else if (lockResult != LockManager.Result.Success){
MessageBox.Show("An unknown error occurred accessing the data folder. Please, make sure "+BrandName+" is not already running. If the problem persists, try restarting your system.", BrandName+" Has Failed :(", MessageBoxButtons.OK, MessageBoxIcon.Error); if (lockResult != LockManager.Result.Success){
FormMessage.Error("TweetDuck Has Failed :(", "An unknown error occurred accessing the data folder. Please, make sure TweetDuck is not already running. If the problem persists, try restarting your system.", FormMessage.OK);
return; return;
} }
} }
ReloadConfig(); UserConfig = UserConfig.Load(UserConfigFilePath);
SystemConfig = SystemConfig.Load(SystemConfigFilePath); SystemConfig = SystemConfig.Load(SystemConfigFilePath);
if (Arguments.HasFlag(Arguments.ArgImportCookies)){ if (Arguments.HasFlag(Arguments.ArgImportCookies)){
ExportManager.ImportCookies(); ExportManager.ImportCookies();
} }
else if (Arguments.HasFlag(Arguments.ArgDeleteCookies)){
ExportManager.DeleteCookies();
}
if (Arguments.HasFlag(Arguments.ArgUpdated)){ if (Arguments.HasFlag(Arguments.ArgUpdated)){
WindowsUtils.TryDeleteFolderWhenAble(InstallerPath, 8000); WindowsUtils.TryDeleteFolderWhenAble(InstallerPath, 8000);
@@ -151,32 +142,21 @@ namespace TweetDuck{
#endif #endif
}; };
CommandLineArgsParser.ReadCefArguments(UserConfig.CustomCefArgs).ToDictionary(settings.CefCommandLineArgs); CommandLineArgs.ReadCefArguments(UserConfig.CustomCefArgs).ToDictionary(settings.CefCommandLineArgs);
BrowserUtils.SetupCefArgs(settings.CefCommandLineArgs);
if (!SystemConfig.HardwareAcceleration){
settings.CefCommandLineArgs["disable-gpu"] = "1";
settings.CefCommandLineArgs["disable-gpu-vsync"] = "1";
}
settings.CefCommandLineArgs["disable-extensions"] = "1";
settings.CefCommandLineArgs["disable-plugins-discovery"] = "1";
settings.CefCommandLineArgs["enable-system-flash"] = "0";
Cef.EnableHighDPISupport();
Cef.Initialize(settings, false, new BrowserProcessHandler()); Cef.Initialize(settings, false, new BrowserProcessHandler());
Application.ApplicationExit += (sender, args) => ExitCleanup(); Application.ApplicationExit += (sender, args) => ExitCleanup();
PluginManager plugins = new PluginManager(PluginPath, PluginConfigFilePath); UpdaterSettings updaterSettings = new UpdaterSettings{
plugins.Reloaded += plugins_Reloaded;
plugins.Executed += plugins_Executed;
plugins.Reload();
FormBrowser mainForm = new FormBrowser(plugins, new UpdaterSettings{
AllowPreReleases = Arguments.HasFlag(Arguments.ArgDebugUpdates), AllowPreReleases = Arguments.HasFlag(Arguments.ArgDebugUpdates),
DismissedUpdate = UserConfig.DismissedUpdate, DismissedUpdate = UserConfig.DismissedUpdate,
InstallerDownloadFolder = InstallerPath InstallerDownloadFolder = InstallerPath
}); };
FormBrowser mainForm = new FormBrowser(updaterSettings);
Application.Run(mainForm); Application.Run(mainForm);
if (mainForm.UpdateInstallerPath != null){ if (mainForm.UpdateInstallerPath != null){
@@ -191,20 +171,6 @@ namespace TweetDuck{
} }
} }
private static void plugins_Reloaded(object sender, PluginErrorEventArgs e){
if (e.HasErrors){
string doubleNL = Environment.NewLine+Environment.NewLine;
MessageBox.Show("The following plugins will not be available until the issues are resolved:"+doubleNL+string.Join(doubleNL, e.Errors), "Error Loading Plugins", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private static void plugins_Executed(object sender, PluginErrorEventArgs e){
if (e.HasErrors){
string doubleNL = Environment.NewLine+Environment.NewLine;
MessageBox.Show("Failed to execute the following plugins:"+doubleNL+string.Join(doubleNL, e.Errors), "Error Executing Plugins", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private static string GetDataStoragePath(){ private static string GetDataStoragePath(){
string custom = Arguments.GetValue(Arguments.ArgDataFolder, null); string custom = Arguments.GetValue(Arguments.ArgDataFolder, null);
@@ -223,28 +189,19 @@ namespace TweetDuck{
} }
} }
public static void ReloadConfig(){
UserConfig = UserConfig.Load(UserConfigFilePath);
UserConfigReplaced?.Invoke(UserConfig, new EventArgs());
}
public static void ResetConfig(){ public static void ResetConfig(){
try{ try{
File.Delete(UserConfigFilePath); File.Delete(UserConfigFilePath);
File.Delete(UserConfig.GetBackupFile(UserConfigFilePath)); File.Delete(UserConfig.GetBackupFile(UserConfigFilePath));
}catch(Exception e){ }catch(Exception e){
Reporter.HandleException("Configuration Reset Error", "Could not delete configuration files to reset the settings.", true, e); Reporter.HandleException("Configuration Reset Error", "Could not delete configuration files to reset the options.", true, e);
return; return;
} }
ReloadConfig(); UserConfig.Reload();
} }
public static void Restart(){ public static void Restart(params string[] extraArgs){
Restart(new string[0]);
}
public static void Restart(string[] extraArgs){
CommandLineArgs args = Arguments.GetCurrentClean(); CommandLineArgs args = Arguments.GetCurrentClean();
CommandLineArgs.ReadStringArray('-', extraArgs, args); CommandLineArgs.ReadStringArray('-', extraArgs, args);
RestartWithArgs(args); RestartWithArgs(args);

View File

@@ -34,12 +34,12 @@ using TweetDuck;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion(Program.VersionFull)] [assembly: AssemblyVersion(Program.VersionTag)]
[assembly: AssemblyFileVersion(Program.VersionFull)] [assembly: AssemblyFileVersion(Program.VersionTag)]
[assembly: NeutralResourcesLanguage("en")] [assembly: NeutralResourcesLanguage("en")]
[assembly: CLSCompliant(false)] [assembly: CLSCompliant(true)]
#if DEBUG #if DEBUG
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("UnitTests")] [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("UnitTests")]

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