Gifcurry (empty) → 0.1.0.0
raw patch · 11 files changed
+867/−0 lines, 11 filesdep +basedep +gtk3dep +processsetup-changedbinary-added
Dependencies added: base, gtk3, process, temporary
Files
- Gifcurry.cabal +37/−0
- Gifcurry.hs +101/−0
- LICENSE +202/−0
- README.md +71/−0
- Setup.hs +2/−0
- cli_src/Main.hs +34/−0
- data/gui.glade +315/−0
- dev/Paths_Gifcurry.hs +6/−0
- gui_src/Main.hs +99/−0
- sample.gif too large to diff
- ui.gif binary
+ Gifcurry.cabal view
@@ -0,0 +1,37 @@+-- Initial Gifcurry.cabal generated by cabal init. For further+-- documentation, see http://haskell.org/cabal/users-guide/++name: Gifcurry+version: 0.1.0.0+synopsis: Create animated GIFs, overlaid with optional text, from movies.+description: GIF creation utility.+homepage: https://github.com/lettier/gifcurry+license: Apache-2.0+license-file: LICENSE+author: Lettier+maintainer: Lettier+copyright: (C) 2016 David Lettier 2016+category: Graphics+build-type: Simple+extra-source-files: README.md, sample.gif, ui.gif, ./dev/Paths_Gifcurry.hs+data-files: data/gui.glade+data-dir: ./+cabal-version: >=1.10++source-repository head+ type: git+ location: https://github.com/lettier/gifcurry++executable gifcurry_gui+ main-is: Main.hs+ build-depends: base >=4.7 && <4.9, gtk3 >=0.14 && <0.15, process >=1.2 && <1.3, temporary >=1.2 && <1.3+ other-modules: Paths_Gifcurry, Gifcurry+ hs-source-dirs: gui_src, ./+ default-language: Haskell2010++executable gifcurry_cli+ main-is: Main.hs+ build-depends: base >=4.7 && <4.9, process >=1.2 && <1.3, temporary >=1.2 && <1.3+ other-modules: Gifcurry+ hs-source-dirs: cli_src, ./+ default-language: Haskell2010
+ Gifcurry.hs view
@@ -0,0 +1,101 @@+-- David Lettier (C) 2016. http://www.lettier.com/++module Gifcurry (gif) where++import System.Environment+import System.Process+import System.IO.Temp+import System.Exit+import Data.List++gif [is, os, st, dr, wd, qa, ttx, btx] = do+ withTempDirectory "." "frames" $ \tmpDir -> do+ putStrLn $ "\nInput file: " ++ is+ putStrLn $ "Start second: " ++ st+ putStrLn $ "Duration: " ++ dr ++ " seconds"+ putStrLn $ "GIF width: " ++ wd ++ "px"+ putStrLn $ "Quality: " ++ (quality qa) ++ "%"+ putStrLn $ "Top text: " ++ (ttx)+ putStrLn $ "Bottom text: " ++ (btx)+ putStrLn $ "\nWriting temporary frames to... " ++ tmpDir+ readProcess "ffmpeg" [+ "-nostats",+ "-loglevel",+ "panic",+ "-an",+ "-ss",+ st,+ "-i",+ is,+ "-t",+ dr,+ "-r",+ "15",+ "-q:v",+ "2",+ "-vf",+ "scale=" ++ wd ++ ":-1",+ "-f",+ "image2",+ tmpDir ++ "/%010d.png" ] ""+ putStrLn $ "Writing your GIF to... " ++ os+ convert qa tmpDir os wd ttx btx+ putStrLn "Done."++convert qa dr os wd ttx btx = readProcess "convert" ([+ "-quiet",+ "-delay",+ "6",+ "-colors",+ ncolors qa,+ "-coalesce",+ "-layers",+ "OptimizeTransparency",+ "-layers",+ "RemoveDups",+ dr ++ "/*.png",+ "-dither",+ "FloydSteinberg",+ "-loop",+ "0" ] ++ annotate wd ttx "north" ++ annotate wd btx "south" ++ [os]) ""++quality qa+ | n > 100 = show 100+ | n < 0 = show 2+ | otherwise = show n+ where n = read qa :: Float++ncolors qa+ | n < 0.0 = show 1+ | n >= 100.0 = show 256+ | otherwise = show $ truncate (n / 100.0 * 256.0)+ where n = read (quality qa) :: Float++annotate wd tx dr = [+ "-gravity",+ dr,+ "-stroke",+ "#000C",+ "-strokewidth",+ "10",+ "-pointsize",+ pointsize wd tx,+ "-annotate",+ "+0+10",+ tx,+ "-stroke",+ "none",+ "-fill",+ "white",+ "-pointsize",+ pointsize wd tx,+ "-annotate",+ "+0+10",+ tx ]++pointsize wd tx+ | length tx == 0 = show 0+ | n <= 0 = show 0+ | otherwise = show $ truncate ((n * 0.4) / l * (72.0 / 34.0))+ where n = read wd :: Float+ l = fromIntegral (length tx)
+ LICENSE view
@@ -0,0 +1,202 @@++ Apache License+ Version 2.0, January 2004+ http://www.apache.org/licenses/++ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION++ 1. Definitions.++ "License" shall mean the terms and conditions for use, reproduction,+ and distribution as defined by Sections 1 through 9 of this document.++ "Licensor" shall mean the copyright owner or entity authorized by+ the copyright owner that is granting the License.++ "Legal Entity" shall mean the union of the acting entity and all+ other entities that control, are controlled by, or are under common+ control with that entity. For the purposes of this definition,+ "control" means (i) the power, direct or indirect, to cause the+ direction or management of such entity, whether by contract or+ otherwise, or (ii) ownership of fifty percent (50%) or more of the+ outstanding shares, or (iii) beneficial ownership of such entity.++ "You" (or "Your") shall mean an individual or Legal Entity+ exercising permissions granted by this License.++ "Source" form shall mean the preferred form for making modifications,+ including but not limited to software source code, documentation+ source, and configuration files.++ "Object" form shall mean any form resulting from mechanical+ transformation or translation of a Source form, including but+ not limited to compiled object code, generated documentation,+ and conversions to other media types.++ "Work" shall mean the work of authorship, whether in Source or+ Object form, made available under the License, as indicated by a+ copyright notice that is included in or attached to the work+ (an example is provided in the Appendix below).++ "Derivative Works" shall mean any work, whether in Source or Object+ form, that is based on (or derived from) the Work and for which the+ editorial revisions, annotations, elaborations, or other modifications+ represent, as a whole, an original work of authorship. For the purposes+ of this License, Derivative Works shall not include works that remain+ separable from, or merely link (or bind by name) to the interfaces of,+ the Work and Derivative Works thereof.++ "Contribution" shall mean any work of authorship, including+ the original version of the Work and any modifications or additions+ to that Work or Derivative Works thereof, that is intentionally+ submitted to Licensor for inclusion in the Work by the copyright owner+ or by an individual or Legal Entity authorized to submit on behalf of+ the copyright owner. For the purposes of this definition, "submitted"+ means any form of electronic, verbal, or written communication sent+ to the Licensor or its representatives, including but not limited to+ communication on electronic mailing lists, source code control systems,+ and issue tracking systems that are managed by, or on behalf of, the+ Licensor for the purpose of discussing and improving the Work, but+ excluding communication that is conspicuously marked or otherwise+ designated in writing by the copyright owner as "Not a Contribution."++ "Contributor" shall mean Licensor and any individual or Legal Entity+ on behalf of whom a Contribution has been received by Licensor and+ subsequently incorporated within the Work.++ 2. Grant of Copyright License. Subject to the terms and conditions of+ this License, each Contributor hereby grants to You a perpetual,+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable+ copyright license to reproduce, prepare Derivative Works of,+ publicly display, publicly perform, sublicense, and distribute the+ Work and such Derivative Works in Source or Object form.++ 3. Grant of Patent License. Subject to the terms and conditions of+ this License, each Contributor hereby grants to You a perpetual,+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable+ (except as stated in this section) patent license to make, have made,+ use, offer to sell, sell, import, and otherwise transfer the Work,+ where such license applies only to those patent claims licensable+ by such Contributor that are necessarily infringed by their+ Contribution(s) alone or by combination of their Contribution(s)+ with the Work to which such Contribution(s) was submitted. If You+ institute patent litigation against any entity (including a+ cross-claim or counterclaim in a lawsuit) alleging that the Work+ or a Contribution incorporated within the Work constitutes direct+ or contributory patent infringement, then any patent licenses+ granted to You under this License for that Work shall terminate+ as of the date such litigation is filed.++ 4. Redistribution. You may reproduce and distribute copies of the+ Work or Derivative Works thereof in any medium, with or without+ modifications, and in Source or Object form, provided that You+ meet the following conditions:++ (a) You must give any other recipients of the Work or+ Derivative Works a copy of this License; and++ (b) You must cause any modified files to carry prominent notices+ stating that You changed the files; and++ (c) You must retain, in the Source form of any Derivative Works+ that You distribute, all copyright, patent, trademark, and+ attribution notices from the Source form of the Work,+ excluding those notices that do not pertain to any part of+ the Derivative Works; and++ (d) If the Work includes a "NOTICE" text file as part of its+ distribution, then any Derivative Works that You distribute must+ include a readable copy of the attribution notices contained+ within such NOTICE file, excluding those notices that do not+ pertain to any part of the Derivative Works, in at least one+ of the following places: within a NOTICE text file distributed+ as part of the Derivative Works; within the Source form or+ documentation, if provided along with the Derivative Works; or,+ within a display generated by the Derivative Works, if and+ wherever such third-party notices normally appear. The contents+ of the NOTICE file are for informational purposes only and+ do not modify the License. You may add Your own attribution+ notices within Derivative Works that You distribute, alongside+ or as an addendum to the NOTICE text from the Work, provided+ that such additional attribution notices cannot be construed+ as modifying the License.++ You may add Your own copyright statement to Your modifications and+ may provide additional or different license terms and conditions+ for use, reproduction, or distribution of Your modifications, or+ for any such Derivative Works as a whole, provided Your use,+ reproduction, and distribution of the Work otherwise complies with+ the conditions stated in this License.++ 5. Submission of Contributions. Unless You explicitly state otherwise,+ any Contribution intentionally submitted for inclusion in the Work+ by You to the Licensor shall be under the terms and conditions of+ this License, without any additional terms or conditions.+ Notwithstanding the above, nothing herein shall supersede or modify+ the terms of any separate license agreement you may have executed+ with Licensor regarding such Contributions.++ 6. Trademarks. This License does not grant permission to use the trade+ names, trademarks, service marks, or product names of the Licensor,+ except as required for reasonable and customary use in describing the+ origin of the Work and reproducing the content of the NOTICE file.++ 7. Disclaimer of Warranty. Unless required by applicable law or+ agreed to in writing, Licensor provides the Work (and each+ Contributor provides its Contributions) on an "AS IS" BASIS,+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or+ implied, including, without limitation, any warranties or conditions+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A+ PARTICULAR PURPOSE. You are solely responsible for determining the+ appropriateness of using or redistributing the Work and assume any+ risks associated with Your exercise of permissions under this License.++ 8. Limitation of Liability. In no event and under no legal theory,+ whether in tort (including negligence), contract, or otherwise,+ unless required by applicable law (such as deliberate and grossly+ negligent acts) or agreed to in writing, shall any Contributor be+ liable to You for damages, including any direct, indirect, special,+ incidental, or consequential damages of any character arising as a+ result of this License or out of the use or inability to use the+ Work (including but not limited to damages for loss of goodwill,+ work stoppage, computer failure or malfunction, or any and all+ other commercial damages or losses), even if such Contributor+ has been advised of the possibility of such damages.++ 9. Accepting Warranty or Additional Liability. While redistributing+ the Work or Derivative Works thereof, You may choose to offer,+ and charge a fee for, acceptance of support, warranty, indemnity,+ or other liability obligations and/or rights consistent with this+ License. However, in accepting such obligations, You may act only+ on Your own behalf and on Your sole responsibility, not on behalf+ of any other Contributor, and only if You agree to indemnify,+ defend, and hold each Contributor harmless for any liability+ incurred by, or claims asserted against, such Contributor by reason+ of your accepting any such warranty or additional liability.++ END OF TERMS AND CONDITIONS++ APPENDIX: How to apply the Apache License to your work.++ To apply the Apache License to your work, attach the following+ boilerplate notice, with the fields enclosed by brackets "[]"+ replaced with your own identifying information. (Don't include+ the brackets!) The text should be enclosed in the appropriate+ comment syntax for the file format. We also recommend that a+ file or class name and description of purpose be included on the+ same "printed page" as the copyright notice for easier+ identification within third-party archives.++ Copyright 2016 David Lettier++ Licensed under the Apache License, Version 2.0 (the "License");+ you may not use this file except in compliance with the License.+ You may obtain a copy of the License at++ http://www.apache.org/licenses/LICENSE-2.0++ Unless required by applicable law or agreed to in writing, software+ distributed under the License is distributed on an "AS IS" BASIS,+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+ See the License for the specific language governing permissions and+ limitations under the License.
+ README.md view
@@ -0,0 +1,71 @@+++ +Credit: [Caminandes: Gran Dillama - Blender Foundation](http://www.caminandes.com/)++# Gifcurry++## Description++Create animated GIFs, overlaid with optional text, from movies.++## CLI Usage++```bash+gifcurry ./in.mp4 ./out.gif start_second duration quality 'Optional top text.' 'Optional bottom text.'+```++## CLI Example++```Bash+~/gifcurry ❯❯❯ gifcurry ./02_gran_dillama_1080p.mp4 ./out.gif 32 8 500 100 'What is' 'Gifcurry?'+ _____ _ __ +| __ (_)/ _| +| | \/_| |_ ___ _ _ _ __ _ __ _ _ +| | __| | _/ __| | | | '__| '__| | | |+| |_\ \ | || (__| |_| | | | | | |_| |+ \____/_|_| \___|\__,_|_| |_| \__, |+ __/ |+ |___/ ++Gifcurry (C) 2016 David Lettier. http://www.lettier.com/++Input file: ./02_gran_dillama_1080p.mp4+Start second: 32+Duration: 8 seconds+GIF width: 500px+Quality: 100.0%+Top text: What is+Bottom text: Gifcurry?++Writing temporary frames to... ./frames3617+Writing your GIF to... ./out.gif+Done.+```++## Dependencies++* [Haskell](https://www.haskell.org/platform/)+ * [System.IO.Temp (temporary)](https://hackage.haskell.org/package/temporary)+ * [Graphics.UI.Gtk (gtk)](https://hackage.haskell.org/package/gtk3-0.14.2)+* [FFmpeg](https://www.ffmpeg.org/download.html)+* [ImageMagick](http://www.imagemagick.org/script/download.php)+* [GTK+](http://www.gtk.org/download/index.php)++## Build & Run++```+git clone git@github.com:lettier/gifcurry.git+cd gifcurry/+cabal sandbox init+cabal install -j+./.cabal-sandbox/bin/gifcurry_gui+./.cabal-sandbox/bin/gifcurry_cli+```++## License++For license information, see [LICENSE](LICENSE).++_(C) 2016 David Lettier._ +http://www.lettier.com/
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ cli_src/Main.hs view
@@ -0,0 +1,34 @@+-- David Lettier (C) 2016. http://www.lettier.com/++import System.Environment+import System.Process+import System.IO.Temp+import System.Exit+import Data.List++import Gifcurry (gif)++main :: IO ()+main = do+ args <- getArgs+ if length args < 8+ then do+ prntheader+ putStrLn "\nUsage: $ gifcurry input_file output_file start_sec duration width quality top_text bottom_text"+ putStrLn "Example: $ gifcurry ./in.mp4 ./out.gif 3 10 600 80 'Top' 'Bottom'"+ exitWith (ExitFailure 1)+ else do+ prntheader+ result <- gif $ take 8 args+ return ()++prntheader = do+ putStrLn " _____ _ __ "+ putStrLn "| __ (_)/ _| "+ putStrLn "| | \\/_| |_ ___ _ _ _ __ _ __ _ _ "+ putStrLn "| | __| | _/ __| | | | '__| '__| | | |"+ putStrLn "| |_\\ \\ | || (__| |_| | | | | | |_| |"+ putStrLn " \\____/_|_| \\___|\\__,_|_| |_| \\__, |"+ putStrLn " __/ |"+ putStrLn " |___/ "+ putStrLn "\nGifcurry (C) 2016 David Lettier. http://www.lettier.com/"
+ data/gui.glade view
@@ -0,0 +1,315 @@+<?xml version="1.0" encoding="UTF-8"?>+<!-- Generated with glade 3.19.0 -->+<interface>+ <requires lib="gtk+" version="3.16"/>+ <object class="GtkWindow" id="gifcurry_window">+ <property name="can_focus">False</property>+ <property name="title" translatable="yes">Gifcurry - lettier.com</property>+ <property name="window_position">mouse</property>+ <property name="gravity">center</property>+ <child>+ <object class="GtkBox" id="app_box">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="orientation">vertical</property>+ <child>+ <object class="GtkFileChooserButton" id="input_file_button">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="tooltip_text" translatable="yes">Select an input file.</property>+ <property name="do_overwrite_confirmation">True</property>+ <property name="title" translatable="yes">Select an Input File</property>+ </object>+ <packing>+ <property name="expand">False</property>+ <property name="fill">True</property>+ <property name="position">0</property>+ </packing>+ </child>+ <child>+ <object class="GtkBox" id="start_duration_box">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="homogeneous">True</property>+ <child>+ <object class="GtkEntry" id="start_time_text_entry">+ <property name="visible">True</property>+ <property name="can_focus">True</property>+ <property name="tooltip_text" translatable="yes">Start time.</property>+ <property name="shadow_type">none</property>+ <property name="primary_icon_stock">gtk-media-play</property>+ <property name="placeholder_text" translatable="yes">Start Time (seconds)</property>+ <property name="input_purpose">number</property>+ </object>+ <packing>+ <property name="expand">False</property>+ <property name="fill">True</property>+ <property name="position">0</property>+ </packing>+ </child>+ <child>+ <object class="GtkEntry" id="duration_text_entry">+ <property name="visible">True</property>+ <property name="can_focus">True</property>+ <property name="tooltip_text" translatable="yes">Duration.</property>+ <property name="width_chars">0</property>+ <property name="primary_icon_stock">gtk-media-stop</property>+ <property name="placeholder_text" translatable="yes">Duration (seconds)</property>+ <property name="input_purpose">number</property>+ </object>+ <packing>+ <property name="expand">False</property>+ <property name="fill">True</property>+ <property name="position">1</property>+ </packing>+ </child>+ </object>+ <packing>+ <property name="expand">False</property>+ <property name="fill">True</property>+ <property name="position">1</property>+ </packing>+ </child>+ <child>+ <object class="GtkBox" id="optional_text_box">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="orientation">vertical</property>+ <child>+ <object class="GtkBox" id="width_quality_box">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="homogeneous">True</property>+ <child>+ <object class="GtkEntry" id="width_text_entry">+ <property name="visible">True</property>+ <property name="can_focus">True</property>+ <property name="tooltip_text" translatable="yes">GIF width in px.</property>+ <property name="shadow_type">none</property>+ <property name="primary_icon_stock">gtk-justify-fill</property>+ <property name="secondary_icon_tooltip_text" translatable="yes">The width of the GIF.</property>+ <property name="placeholder_text" translatable="yes">Width</property>+ <property name="input_purpose">number</property>+ </object>+ <packing>+ <property name="expand">False</property>+ <property name="fill">True</property>+ <property name="position">0</property>+ </packing>+ </child>+ <child>+ <object class="GtkEntry" id="quality_text_entry">+ <property name="visible">True</property>+ <property name="can_focus">True</property>+ <property name="tooltip_text" translatable="yes">GIF quality from 1 to 100.</property>+ <property name="max_length">3</property>+ <property name="width_chars">3</property>+ <property name="max_width_chars">3</property>+ <property name="primary_icon_stock">gtk-preferences</property>+ <property name="secondary_icon_tooltip_text" translatable="yes">The quality of the GIF between 1 and 100.</property>+ <property name="placeholder_text" translatable="yes">Quality [1-100]</property>+ <property name="input_purpose">number</property>+ </object>+ <packing>+ <property name="expand">False</property>+ <property name="fill">True</property>+ <property name="position">1</property>+ </packing>+ </child>+ </object>+ <packing>+ <property name="expand">False</property>+ <property name="fill">True</property>+ <property name="position">0</property>+ </packing>+ </child>+ <child>+ <object class="GtkEntry" id="top_text_text_entry">+ <property name="visible">True</property>+ <property name="can_focus">True</property>+ <property name="tooltip_text" translatable="yes">Optional top text.</property>+ <property name="primary_icon_stock">gtk-italic</property>+ <property name="secondary_icon_tooltip_text" translatable="yes">Text will show up at the top of the GIF.</property>+ <property name="placeholder_text" translatable="yes">Optional top text.</property>+ </object>+ <packing>+ <property name="expand">False</property>+ <property name="fill">True</property>+ <property name="position">1</property>+ </packing>+ </child>+ </object>+ <packing>+ <property name="expand">False</property>+ <property name="fill">True</property>+ <property name="position">2</property>+ </packing>+ </child>+ <child>+ <object class="GtkEntry" id="bottom_text_text_entry">+ <property name="visible">True</property>+ <property name="can_focus">True</property>+ <property name="tooltip_text" translatable="yes">Optional bottom text.</property>+ <property name="primary_icon_stock">gtk-italic</property>+ <property name="secondary_icon_tooltip_text" translatable="yes">Text will show up at the bottom of the GIF.</property>+ <property name="placeholder_text" translatable="yes">Optional bottom text.</property>+ </object>+ <packing>+ <property name="expand">False</property>+ <property name="fill">True</property>+ <property name="position">3</property>+ </packing>+ </child>+ <child>+ <object class="GtkFileChooserButton" id="output_file_path_button">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="tooltip_text" translatable="yes">Select an output folder.</property>+ <property name="action">select-folder</property>+ <property name="title" translatable="yes">Select an Output Folder</property>+ </object>+ <packing>+ <property name="expand">False</property>+ <property name="fill">True</property>+ <property name="position">4</property>+ </packing>+ </child>+ <child>+ <object class="GtkSeparator" id="separator1">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ </object>+ <packing>+ <property name="expand">False</property>+ <property name="fill">True</property>+ <property name="position">5</property>+ </packing>+ </child>+ <child>+ <object class="GtkEntry" id="output_file_name_text_entry">+ <property name="visible">True</property>+ <property name="can_focus">True</property>+ <property name="tooltip_text" translatable="yes">Output file name.</property>+ <property name="shadow_type">none</property>+ <property name="primary_icon_stock">gtk-file</property>+ <property name="placeholder_text" translatable="yes">Output file name.</property>+ </object>+ <packing>+ <property name="expand">False</property>+ <property name="fill">True</property>+ <property name="position">6</property>+ </packing>+ </child>+ <child>+ <object class="GtkButton" id="ok_button">+ <property name="label">gtk-ok</property>+ <property name="visible">True</property>+ <property name="can_focus">True</property>+ <property name="receives_default">True</property>+ <property name="tooltip_text" translatable="yes">Create the GIF.</property>+ <property name="relief">half</property>+ <property name="use_stock">True</property>+ <property name="always_show_image">True</property>+ </object>+ <packing>+ <property name="expand">False</property>+ <property name="fill">True</property>+ <property name="position">7</property>+ </packing>+ </child>+ <child>+ <object class="GtkBox" id="link_box">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <child>+ <object class="GtkLinkButton" id="giphy_link_button">+ <property name="label" translatable="yes">button</property>+ <property name="visible">True</property>+ <property name="can_focus">True</property>+ <property name="receives_default">True</property>+ <property name="relief">half</property>+ <property name="uri">https://giphy.com/create/upload</property>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">0</property>+ </packing>+ </child>+ <child>+ <object class="GtkLinkButton" id="imgur_link_button">+ <property name="label" translatable="yes">button</property>+ <property name="visible">True</property>+ <property name="can_focus">True</property>+ <property name="receives_default">True</property>+ <property name="relief">half</property>+ <property name="uri">https://www.imgur.com/</property>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">1</property>+ </packing>+ </child>+ </object>+ <packing>+ <property name="expand">False</property>+ <property name="fill">True</property>+ <property name="position">8</property>+ </packing>+ </child>+ <child>+ <object class="GtkBox" id="status_box">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <child>+ <object class="GtkLabel" id="status_label">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="xpad">5</property>+ <property name="ypad">5</property>+ <property name="label" translatable="yes">Status</property>+ </object>+ <packing>+ <property name="expand">False</property>+ <property name="fill">True</property>+ <property name="position">0</property>+ </packing>+ </child>+ <child>+ <object class="GtkSeparator" id="separator2">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="orientation">vertical</property>+ </object>+ <packing>+ <property name="expand">False</property>+ <property name="fill">True</property>+ <property name="position">1</property>+ </packing>+ </child>+ <child>+ <object class="GtkEntry" id="status_text_entry">+ <property name="visible">True</property>+ <property name="can_focus">True</property>+ <property name="editable">False</property>+ <property name="text" translatable="yes">Ready.</property>+ <property name="caps_lock_warning">False</property>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">2</property>+ </packing>+ </child>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">9</property>+ </packing>+ </child>+ </object>+ </child>+ </object>+</interface>
+ dev/Paths_Gifcurry.hs view
@@ -0,0 +1,6 @@+module Paths_Gifcurry where++getDataFileName :: FilePath -> IO FilePath+getDataFileName a = do+ putStrLn "You are using a fake Paths_Gifcurry."+ return a
+ gui_src/Main.hs view
@@ -0,0 +1,99 @@+-- David Lettier (C) 2016. http://www.lettier.com/++import System.Environment+import Control.Concurrent+import Graphics.UI.Gtk+import Graphics.UI.Gtk.Builder++import Paths_Gifcurry+import Gifcurry (gif)++main = do+ initGUI++ builder <- build_builder++ window <- load_window builder "gifcurry_window"+ start_time_entry <- load_entry builder "start_time_text_entry"+ duration_entry <- load_entry builder "duration_text_entry"+ width_entry <- load_entry builder "width_text_entry"+ quality_entry <- load_entry builder "quality_text_entry"+ top_text_entry <- load_entry builder "top_text_text_entry"+ bottom_text_entry <- load_entry builder "bottom_text_text_entry"+ output_file_name_entry <- load_entry builder "output_file_name_text_entry"+ status_entry <- load_entry builder "status_text_entry"+ input_file_button <- load_fc_button builder "input_file_button"+ output_file_path_button <- load_fc_button builder "output_file_path_button"+ ok_button <- load_button builder "ok_button"+ giphy_button <- load_button builder "giphy_link_button"+ imgur_button <- load_button builder "imgur_link_button"++ -- Bug in Glade does not allow setting the link button label.+ buttonSetLabel giphy_button "Giphy"+ buttonSetLabel imgur_button "Imgur"++ entrySetText start_time_entry "0"+ entrySetText duration_entry "10"+ entrySetText quality_entry "100"+ entrySetText width_entry "500"++ ok_button `on` buttonActivated $ do+ input_file_text <- fileChooserGetFilename input_file_button+ input_file_path_name <- case input_file_text of+ Nothing -> return ""+ Just file_path_name -> return file_path_name++ output_file_path_text <- fileChooserGetFilename output_file_path_button+ output_file_path <- case output_file_path_text of+ Nothing -> return ""+ Just dir -> return dir++ start_time <- entryGetText start_time_entry+ duration <- entryGetText duration_entry++ width <- entryGetText width_entry+ quality <- entryGetText quality_entry++ top_text <- entryGetText top_text_entry+ bottom_text <- entryGetText bottom_text_entry++ output_file_name <- entryGetText output_file_name_entry+ let output_gif_file_name = output_file_name ++ ".gif"+ let output_file_path_name = output_file_path ++ "/" ++ output_gif_file_name++ if ((length input_file_path_name) > 0 && (length output_file_path_name) > 5 && (length output_gif_file_name) > 4)+ then do+ forkIO $ do+ entrySetText status_entry "One GIF coming up!"+ gif [+ input_file_path_name,+ output_file_path_name,+ start_time,+ duration,+ width,+ quality,+ top_text,+ bottom_text ]+ entrySetText status_entry "Ready."+ return ()+ else do entrySetText status_entry "File paths are wrong."++ return ()++ on window objectDestroy mainQuit+ widgetShowAll window+ mainGUI++load_window b id = builderGetObject b castToWindow id++load_entry b id = builderGetObject b castToEntry id++load_fc_button b id = builderGetObject b castToFileChooserButton id++load_button b id = builderGetObject b castToButton id++build_builder = do+ builder <- builderNew+ glade_file <- getDataFileName "data/gui.glade"+ builderAddFromFile builder glade_file+ return builder
+ sample.gif view
file too large to diff
+ ui.gif view
binary file changed (absent → 1106130 bytes)