wuffl 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 46a0f50d3457bec156544cc167f04c2a712dfc8a4b4ea8cf80a17f8b5247d7f0
4
+ data.tar.gz: b5cdc53358c6cc5ab9e2b639c8ef4a4de365a680b3d5805cbd420ce6937f5e03
5
+ SHA512:
6
+ metadata.gz: 64c69f5ee91b1282466a323d358f669407dbc7d27a29df5c3801922ff2fe93bde350ebed881c760d87672eb660c1a0cc5e05c58aacedd36c4b676fd9df45ed71
7
+ data.tar.gz: 97a5dbcedb88dba60a458d8bcfc2a5dd4d5e8a2b328101257018c578eb870a9e5435bef8e207bac52dd8e13ad6ef1b2e26f76a9381c315209eda7d26affcdd77
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Anna Foerster
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README ADDED
@@ -0,0 +1,34 @@
1
+ # Wuffl
2
+ <strong> 1. Introduction </strong>
3
+ Wuffl is a simple image viewer which can move pictures either to a „Selected“ folder or to a „Deleted“ folder. "Selected" and "Deleted" folders are located in the same folder as the chosen picture. Wuffl is available for Linux Ubuntu >=14.04, Mac and Windows.
4
+
5
+ Imagine you review you vacation photographs and you need to select which pictures to show to your friends at the next party or you work your way through a bunch of old pictures and you need to select a view of them for a anniversary celebration. Wuffl allows you to move the picture to an extra folder ("Selected" folder) or to a "Deleted" folder while viewing it.
6
+ <p>
7
+ Source for the icons of the buttons: http://icons.mysitemyway.com
8
+ </p>
9
+ <strong> 2. Installation </strong>
10
+ To use the Wuffl image viewer you require:
11
+ <ul>
12
+ <li> <a href="https://www.ruby-lang.org/en/downloads/"><code>ruby</code></a> (v2.4)
13
+ </ul>
14
+ as well as the following Ruby gems:
15
+ <ul>
16
+ <li><code>fastimage</code>
17
+ <li><code>fileutils</code>
18
+ <li><code>gtk3</code>
19
+ <li><code>stringio</code></li>
20
+ </ul>
21
+
22
+ Get the Wullf image viewer by typing ```gem install wuffl``` in your command line.
23
+
24
+ <strong>3. Usage</strong>
25
+ Using your terminal or cmd start Wuffl by typing
26
+ <code> wuffl</code>
27
+
28
+ After starting Wuffl you'll see a start screen with a line of buttons at the bottom.
29
+ To select an image go to the top left corner, klick on <code>File</code> → <code>Open file</code> and browse to the location of you images. Then select an image and click <code>open</code> or just doubleclick on the selected image.
30
+
31
+ The buttons from left to right mean: <code>show previous image</code>, <code>rotate current image</code>, <code>move the current image to the folder 'Selected'</code>, <code>show next image</code>, <code>move the current image to the folder 'Deleted'</code>.
32
+
33
+ <strong>4. Remarks</strong>
34
+ If you have any remarks, bugs, questions etc. please tell me, I'd be happy to help.
data/bin/wuffl ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative '../lib/wuffl/wuffl_interface'
3
+
4
+ int = Wuffl::WufflInterface.new
5
+
6
+ int.run
@@ -0,0 +1,290 @@
1
+ require_relative 'image_actions'
2
+
3
+ module Wuffl
4
+ module Actions
5
+
6
+ # get the operating system of the machine
7
+ def self.get_op_sys
8
+ gempath = File.expand_path(File.join(File.dirname(__FILE__), '..'))
9
+ gempath.sub!("/lib", "")
10
+ if (/darwin/ =~ RUBY_PLATFORM) != nil
11
+ return 'mac'
12
+ elsif (/linux/ =~ RUBY_PLATFORM) != nil
13
+ return 'linux'
14
+ elsif (/cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM) != nil
15
+ return 'windows'
16
+ end
17
+ end
18
+
19
+ # get the resolution of the display
20
+ def self.get_resolution
21
+
22
+ operating_system = get_op_sys
23
+
24
+ case operating_system
25
+
26
+ when 'windows'
27
+ output_dimensions = `wmic path Win32_VideoController get VideoModeDescription,CurrentVerticalResolution,CurrentHorizontalResolution /format:value`
28
+ begin_hor_res = output_dimensions.index("HorizontalResolution")
29
+ begin_ver_res = output_dimensions.index("VerticalResolution")
30
+ begin_video_mode = output_dimensions.index("VideoMode")
31
+ length_hor = "HorizontalResolution=".length
32
+ length_ver = "VerticalResolution=".length
33
+ length_video = "VideoMode=".length
34
+ char_hor = output_dimensions[begin_hor_res + length_hor]
35
+ char_ver = output_dimensions[begin_ver_res + length_ver]
36
+ char_video = output_dimensions[begin_video_mode + length_video]
37
+ if char_hor != char_hor.to_i.to_s
38
+ output_dimensions.sub!("HorizontalResolution", "")
39
+ end
40
+ if char_ver != char_ver.to_i.to_s
41
+ output_dimensions.sub!("VerticalResolution", "")
42
+ end
43
+ if char_video != char_video.to_i.to_s
44
+ output_dimensions.sub!("VideoMode", "")
45
+ end
46
+
47
+ # Horizontal Resolution of the screen
48
+ hor_res_begin_index = output_dimensions.index("HorizontalResolution") + "HorizontalResolution=".length
49
+ hor_res_end_index = output_dimensions.index("CurrentVertical") - 3
50
+ win_width = output_dimensions.slice(hor_res_begin_index..hor_res_end_index).to_i
51
+
52
+ # Vertical Resolution of the screen
53
+ ver_res_begin_index = output_dimensions.index("VerticalResolution") + "VerticalResolution=".length
54
+ ver_res_end_index = output_dimensions.index("VideoMode") - 3
55
+ win_height = output_dimensions.slice(ver_res_begin_index..ver_res_end_index).to_i
56
+
57
+ else
58
+ if operating_system == "mac"
59
+ output_dimensions = `system_profiler SPDisplaysDataType |grep Resolution`.chomp.tr(" ", "")
60
+ end_index = output_dimensions.index("Retina") - 1
61
+ elsif operating_system == "linux"
62
+ output_dimensions = `xdpyinfo | grep dimensions`.chomp.tr(" ", "")
63
+ end_index = output_dimensions.index("p") - 1
64
+ end
65
+ start_index = output_dimensions.index(":") + 1
66
+ resolution = output_dimensions.slice(start_index..end_index)
67
+ win_width = resolution.slice(0..resolution.index("x")-1).to_i
68
+ win_height = resolution.slice(resolution.index("x")+1.. resolution.length-1).to_i
69
+ end
70
+
71
+ return [win_width, win_height]
72
+ end
73
+
74
+ # pack the boxes for the GUI
75
+ def self.pack_boxes(window, img_current, box_set, button_set)
76
+ box_set[:hbox].add button_set[:prev_btn]
77
+ box_set[:hbox].add button_set[:select_btn]
78
+ box_set[:hbox].add button_set[:rotate_btn]
79
+ box_set[:hbox].add button_set[:delete_btn]
80
+ box_set[:hbox].add button_set[:next_btn]
81
+
82
+ box_set[:halign].add box_set[:hbox]
83
+
84
+ box_set[:vbox].pack_start box_set[:mb], :expand => false, :fill => false, :padding => 5
85
+ box_set[:vbox].pack_start img_current, :expand => true, :fill => true, :padding => 5
86
+ box_set[:vbox].pack_start box_set[:halign], :expand => false, :fill => false, :padding => 5
87
+
88
+
89
+ window.add box_set[:vbox]
90
+
91
+ return box_set
92
+ end
93
+
94
+ # set the index for the next image
95
+ def self.set_next_index(index, img_array)
96
+ if (index + 1) <= (img_array.length - 1)
97
+ index += 1
98
+ else
99
+ index = 0
100
+ end
101
+ return index
102
+ end
103
+
104
+ # set the index for the previous image
105
+ def self.set_prev_index(index, img_array)
106
+ if (index - 1) < 0
107
+ index = img_array.length - 1
108
+ else
109
+ index -= 1
110
+ end
111
+ return index
112
+ end
113
+
114
+ # deactivate all buttons
115
+ def self.deactivate_buttons (button_set)
116
+ button_set.each do |button, sens_value|
117
+ button_set[button].sensitive = false
118
+ end
119
+ end
120
+
121
+ # action when the "Open file" menu option is clicked
122
+ def self.open_file_action(window, img_parameters, button_set)
123
+ dialog = Gtk::FileChooserDialog.new(:title => "Open file", :parent => window, :action => Gtk::FileChooserAction::OPEN,
124
+ :buttons => [[Gtk::Stock::OPEN, Gtk::ResponseType::ACCEPT], [Gtk::Stock::CANCEL, Gtk::ResponseType::CANCEL]])
125
+
126
+ if dialog.run == Gtk::ResponseType::ACCEPT
127
+ filename = dialog.filename
128
+ img_parameters[:dir_path] = File.dirname(filename)
129
+ img_parameters[:selected_path] = img_parameters[:dir_path] + "/Selected"
130
+ img_parameters[:deleted_path] = img_parameters[:dir_path] + "/Deleted"
131
+ all_files = Dir.entries(img_parameters[:dir_path])
132
+ accepted_formats = [".jpg", ".JPG", ".png", ".PNG", ".gif", ".GIF"]
133
+ img_parameters[:is_landscape] = true
134
+
135
+ all_files.each do |name|
136
+ if accepted_formats.include? File.extname(name)
137
+ img_parameters[:all_orig_img] << name
138
+ end
139
+ end
140
+ img_parameters[:all_orig_img] = img_parameters[:all_orig_img].sort
141
+ just_the_name = File.basename filename
142
+
143
+ img_parameters[:all_orig_img].each_with_index do |name, index|
144
+ if just_the_name == name
145
+ img_parameters[:ind] = index
146
+ end
147
+ end
148
+
149
+ if File.directory?(img_parameters[:selected_path]) == false
150
+ Dir.mkdir(img_parameters[:selected_path])
151
+ FileUtils.chmod 0777, img_parameters[:selected_path]
152
+ end
153
+
154
+ if File.directory?(img_parameters[:deleted_path]) == false
155
+ Dir.mkdir(img_parameters[:deleted_path])
156
+ FileUtils.chmod 0777, img_parameters[:deleted_path]
157
+ end
158
+
159
+ # Prebuffer of all files
160
+ img_parameters[:all_orig_img].each do |file|
161
+ current_filename = img_parameters[:dir_path] + "/" + file
162
+ if current_filename.include?("\\")
163
+ current_filename.gsub!("\\", "/")
164
+ end
165
+ img_parameters[:all_orig_pb], img_parameters[:is_landscape] = ImageActions.prepare_pixbuf(current_filename, img_parameters[:all_orig_pb], img_parameters[:img_max_w], img_parameters[:img_max_h], img_parameters[:reduction_factor])
166
+ end
167
+ img_parameters[:pb_current] = img_parameters[:all_orig_pb][img_parameters[:ind]]
168
+
169
+ ImageActions.show_img(img_parameters[:img_current], img_parameters[:pb_current])
170
+ window.set_title File.basename filename
171
+
172
+ # Activate all buttons
173
+ button_set.each do |button, sens_value|
174
+ button_set[button].sensitive = true
175
+ end
176
+
177
+ end
178
+ dialog.destroy
179
+
180
+ return img_parameters
181
+ end
182
+
183
+ # action when the "previous image" button or the "next image" button is clicked
184
+ def self.prev_next_btn_action(window, img_parameters, previous_or_next_button)
185
+ if previous_or_next_button == "prev" # previous image
186
+ img_parameters[:ind] = set_prev_index(img_parameters[:ind], img_parameters[:all_orig_img])
187
+ elsif previous_or_next_button == "next" # next button
188
+ img_parameters[:ind] = set_next_index(img_parameters[:ind], img_parameters[:all_orig_img])
189
+ end
190
+ filename = img_parameters[:dir_path] + "/" + img_parameters[:all_orig_img][img_parameters[:ind]]
191
+ img_parameters[:is_landscape] = ImageActions.img_dimensions_fi(filename)[0]
192
+ img_parameters[:pb_current] = img_parameters[:all_orig_pb][img_parameters[:ind]]
193
+ ImageActions.show_img(img_parameters[:img_current], img_parameters[:pb_current])
194
+ window.set_title File.basename filename
195
+
196
+ return img_parameters
197
+ end
198
+
199
+ # action when the rotate button is clicked
200
+ def self.rotate_btn_action(img_parameters)
201
+ if img_parameters[:is_landscape] == false # Original image in portrait format
202
+ img_parameters[:pb_current] = img_parameters[:pb_current].rotate(:clockwise)
203
+
204
+ else # Original image in landscape format
205
+ if img_parameters[:rotation_case] == 'A'
206
+ img_parameters[:pb_current] = img_parameters[:pb_current].rotate(:clockwise)
207
+ width_pb = img_parameters[:pb_current].width
208
+ height_pb = img_parameters[:pb_current].height
209
+ if height_pb > img_parameters[:img_max_h]
210
+ while height_pb > img_parameters[:img_max_h] do
211
+ height_pb *= img_parameters[:reduction_factor]
212
+ width_pb *= img_parameters[:reduction_factor]
213
+ end
214
+ height_pb = height_pb.to_i
215
+ width_pb = width_pb.to_i
216
+ img_parameters[:pb_portrait] = img_parameters[:pb_current].scale(width_pb, height_pb, :bilinear)
217
+ img_parameters[:pb_current] = img_parameters[:pb_portrait]
218
+ else
219
+ img_parameters[:pb_portrait] = img_parameters[:pb_current]
220
+ end
221
+ img_parameters[:rotation_case] = 'B'
222
+
223
+ elsif img_parameters[:rotation_case] == 'B'
224
+ img_parameters[:pb_current] = img_parameters[:all_orig_pb][img_parameters[:ind]].rotate(:upsidedown)
225
+ img_parameters[:rotation_case] = 'C'
226
+
227
+ elsif img_parameters[:rotation_case] == 'C'
228
+ img_parameters[:pb_current] = img_parameters[:pb_portrait].rotate(:upsidedown)
229
+ img_parameters[:rotation_case] = 'D'
230
+
231
+ elsif img_parameters[:rotation_case] == 'D'
232
+ img_parameters[:pb_current] = img_parameters[:all_orig_pb][img_parameters[:ind]]
233
+ img_parameters[:rotation_case] = 'A'
234
+ end
235
+ end
236
+ img_parameters[:img_current].set_pixbuf(img_parameters[:pb_current])
237
+
238
+ return img_parameters
239
+ end
240
+
241
+ # action for the select and delete buttons
242
+ def self.select_delete_btn_action(window, img_parameters, button_set, select_delete_par)
243
+ if img_parameters[:all_orig_img].length > 0
244
+
245
+ just_the_name = img_parameters[:all_orig_img][img_parameters[:ind]]
246
+ current_location = img_parameters[:dir_path] + "/" + just_the_name
247
+ if select_delete_par == "select"
248
+ new_location = img_parameters[:selected_path] + "/" + just_the_name
249
+ elsif select_delete_par == "delete"
250
+ new_location = img_parameters[:deleted_path] + "/" + just_the_name
251
+ end
252
+ FileUtils.mv(current_location, new_location)
253
+
254
+ img_parameters[:all_orig_img].delete_at(img_parameters[:ind])
255
+ img_parameters[:all_orig_pb].delete_at(img_parameters[:ind])
256
+
257
+ if img_parameters[:all_orig_img].length > 0
258
+ if img_parameters[:ind] == img_parameters[:all_orig_img].length
259
+ img_parameters[:ind] -= 1
260
+ end
261
+
262
+ just_the_name = img_parameters[:all_orig_img][img_parameters[:ind]]
263
+ filename = img_parameters[:dir_path] + "/" + just_the_name
264
+ img_parameters[:is_landscape] = ImageActions.img_dimensions_fi(filename)[0]
265
+ img_parameters[:pb_current] = img_parameters[:all_orig_pb][img_parameters[:ind]]
266
+ ImageActions.show_img(img_parameters[:img_current], img_parameters[:pb_current])
267
+ window.set_title just_the_name
268
+
269
+ if select_delete_par == "select"
270
+ # Buffer the next image if not all loaded yet
271
+ if img_parameters[:all_orig_pb].length != img_parameters[:all_orig_img].length
272
+ current_filename = img_parameters[:dir_path] + "/" + img_parameters[:all_orig_img][img_parameters[:ind]+1]
273
+ img_parameters[:all_orig_pb], img_parameters[:is_landscape] = ImageActions.prepare_pixbuf(current_filename, img_parameters[:all_orig_pb], img_parameters[:img_max_w], img_parameters[:img_max_h], img_parameters[:reduction_factor])
274
+ end
275
+ end
276
+ else
277
+ img_parameters[:pb_current] = GdkPixbuf::Pixbuf.new
278
+ img_parameters[:img_current].set_pixbuf(img_parameters[:pb_current])
279
+ deactivate_buttons(button_set)
280
+ end
281
+ else
282
+ img_parameters[:pb_current] = GdkPixbuf::Pixbuf.new
283
+ img_parameters[:img_current].set_pixbuf(img_parameters[:pb_current])
284
+ deactivate_buttons(button_set)
285
+ end
286
+ return img_parameters
287
+ end
288
+
289
+ end
290
+ end
@@ -0,0 +1,109 @@
1
+ module Wuffl
2
+ module BasicElements
3
+
4
+ # define the interface window
5
+ def self.define_window(win_width, win_height)
6
+ window = Gtk::Window.new
7
+ window.set_title "Wuffl"
8
+ window.set_window_position :center
9
+ window.set_default_size win_width, win_height
10
+ window.signal_connect("destroy") {Gtk.main_quit}
11
+
12
+ return window
13
+ end
14
+
15
+ # define the boxes which are parts of the window
16
+ def self.define_boxes
17
+ vbox = Gtk::Box.new :vertical, 5
18
+ hbox = Gtk::Box.new :horizontal, 10
19
+
20
+ return [vbox, hbox]
21
+ end
22
+
23
+ # define the pixel buffers
24
+ def self.define_pixbuf
25
+ pb_current = GdkPixbuf::Pixbuf.new #current pixbuf
26
+ pb_portrait = GdkPixbuf::Pixbuf.new #pixbuf in portrait format (relevant only for rotations)
27
+
28
+ return [pb_current, pb_portrait]
29
+ end
30
+
31
+ # define the necessary buttons
32
+ def self.define_buttons
33
+ #-----------Definition of Buttons ----------
34
+
35
+ #-----------Previous-Button----------------------
36
+ prev_btn = Gtk::Button.new
37
+ prev_btn.label = "Previous"
38
+ prev_btn.set_tooltip_text "Previous image"
39
+ prev_btn.sensitive = false
40
+
41
+ #-----------Next-Button-----------------------
42
+ next_btn = Gtk::Button.new
43
+ next_btn.label = "Next"
44
+ next_btn.set_tooltip_text "Next image"
45
+ next_btn.sensitive = false
46
+
47
+ #-----------Rotate-Button----------------------
48
+ rotate_btn = Gtk::Button.new
49
+ rotate_btn.label = "Rotate"
50
+ rotate_btn.set_tooltip_text "Rotate image"
51
+ rotate_btn.sensitive = false
52
+
53
+ #-----------Select-Button-----------------
54
+ select_btn = Gtk::Button.new
55
+ select_btn.label = "Move to \"Selected\""
56
+ select_btn.set_tooltip_text "Move the image to the \"Selected\" folder"
57
+ select_btn.sensitive = false
58
+
59
+ #-----------Delete-Button---------------------
60
+ delete_btn = Gtk::Button.new
61
+ delete_btn.label = "Move to \"Deleted\""
62
+ delete_btn.set_tooltip_text "Move the image to the \"Deleted\" folder"
63
+ delete_btn.sensitive = false
64
+
65
+ return {prev_btn: prev_btn, next_btn: next_btn, rotate_btn: rotate_btn, select_btn: select_btn, delete_btn: delete_btn}
66
+ end
67
+
68
+ # define the menu
69
+ def self.define_menu
70
+ mb = Gtk::MenuBar.new
71
+ file = Gtk::MenuItem.new :label => "File"
72
+ filemenu = Gtk::Menu.new
73
+ file.set_submenu filemenu
74
+
75
+ open_file = Gtk::MenuItem.new :label => "Open file"
76
+ quit = Gtk::MenuItem.new :label => "Exit"
77
+
78
+ filemenu.append open_file
79
+ filemenu.append quit
80
+ mb.append file
81
+
82
+ return [mb, open_file, quit]
83
+ end
84
+
85
+ # define the image parameters
86
+ def self.define_img_parameters(win_width, win_height, img_current)
87
+ pb_current, pb_portrait = define_pixbuf
88
+
89
+ img_parameters = {
90
+ dir_path: "", # path of the image folder
91
+ selected_path: "",
92
+ deleted_path: "",
93
+ name: "",
94
+ rotation_case: 'A',
95
+ ind: 0,
96
+ is_landscape: true,
97
+ all_orig_img: [],
98
+ all_orig_pb: [],
99
+ pb_current: pb_current,
100
+ pb_portrait: pb_portrait,
101
+ img_current: img_current, # current image
102
+ img_max_w: (win_width * 0.37).to_i, # max width for each image
103
+ img_max_h: (win_height * 0.37).to_i , # max height for each image
104
+ reduction_factor: 0.95
105
+ }
106
+ return img_parameters
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,90 @@
1
+ module Wuffl
2
+ module ImageActions
3
+
4
+ # determine the dimensions of the given image with FastImage-gem
5
+ def self.img_dimensions_fi (filename)
6
+ img_dim = FastImage.size("#{filename}")
7
+ img_orig_width_fi = img_dim[0]
8
+ img_orig_height_fi = img_dim[1]
9
+
10
+ if img_orig_width_fi > img_orig_height_fi
11
+ is_orig_landscape = true
12
+ else
13
+ is_orig_landscape = false
14
+ end
15
+
16
+ fastimage_infos = [is_orig_landscape, img_orig_width_fi.to_i, img_orig_height_fi.to_i]
17
+ end
18
+
19
+ # resize the image if too big - for landscape mode
20
+ def self.landscape_pic(pb_orig, img_width, img_height, img_max_w, img_max_h, reduction_factor)
21
+ if img_width > img_max_w || img_height > img_max_h
22
+ while img_width > img_max_w || img_height > img_max_h do
23
+ img_width *= reduction_factor
24
+ img_height *= reduction_factor
25
+ end
26
+ end
27
+
28
+ img_width = img_width.to_i
29
+ img_height = img_height.to_i
30
+ pb_orig = pb_orig.scale(img_width, img_height, :bilinear)
31
+ return pb_orig
32
+ end
33
+
34
+ # resize the image if too big - for portrait mode
35
+ def self.portrait_pic(pb_orig, rotate_pic, img_width, img_height, img_max_h, reduction_factor)
36
+ if rotate_pic == true
37
+ pb_orig = pb_orig.rotate(:clockwise)
38
+ z = img_width
39
+ img_width = img_height
40
+ img_height = z
41
+ end
42
+
43
+ if img_height > img_max_h
44
+ while img_height > img_max_h do
45
+ img_width *= reduction_factor
46
+ img_height *= reduction_factor
47
+ end
48
+ end
49
+
50
+ img_height = img_height.to_i
51
+ img_width = img_width.to_i
52
+
53
+ pb_orig = pb_orig.scale(img_width, img_height, :bilinear)
54
+ return pb_orig
55
+ end
56
+
57
+ # prepare image for loading
58
+ def self.prepare_pixbuf(filename, array_of_orig_pixbufs, img_max_w, img_max_h, reduction_factor)
59
+ fi_infos = img_dimensions_fi(filename)
60
+ pixbuf_infos = GdkPixbuf::Pixbuf.get_file_info(filename)
61
+ is_landscape = fi_infos[0]
62
+ pixbuf_width = pixbuf_infos[1]
63
+ pixbuf_height = pixbuf_infos[2]
64
+
65
+ img_width = pixbuf_width
66
+ img_height = pixbuf_height
67
+
68
+ pb_orig = GdkPixbuf::Pixbuf.new :file => filename, :width => img_width, :height => img_height
69
+ if fi_infos[1] > fi_infos[2] #landscape format
70
+ pb_orig = landscape_pic(pb_orig, img_width, img_height, img_max_w, img_max_h, reduction_factor)
71
+ else #portrait format
72
+ if (fi_infos[1] == pixbuf_infos[1]) && (fi_infos[2] == pixbuf_infos[2])
73
+ rotate_pic = false
74
+ else
75
+ rotate_pic = true
76
+ end
77
+ pb_orig = portrait_pic(pb_orig, rotate_pic, img_width, img_height, img_max_h, reduction_factor)
78
+ end
79
+
80
+ array_of_orig_pixbufs << pb_orig
81
+ return [array_of_orig_pixbufs, is_landscape]
82
+ end
83
+
84
+ # display the current image
85
+ def self.show_img(img_curr, pb_curr)
86
+ img_curr.set_pixbuf(pb_curr)
87
+
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,88 @@
1
+ require 'gtk3'
2
+ require 'fileutils'
3
+ require 'fastimage'
4
+ require 'stringio'
5
+ require_relative 'basic_elements'
6
+ require_relative 'actions'
7
+
8
+ module Wuffl
9
+
10
+ class WufflInterface
11
+
12
+ def initialize
13
+
14
+ # get the screen resolution
15
+ @win_width, @win_height = Actions.get_resolution
16
+
17
+ # define the window
18
+ @window = BasicElements.define_window(@win_width*0.45, @win_height*0.45)
19
+
20
+ # define the boxes
21
+ @vbox, @hbox = BasicElements.define_boxes
22
+
23
+ # prepare the current image
24
+ @img_current = Gtk::Image.new
25
+
26
+ @halign = Gtk::Alignment.new 0.5, 0, 0, 0
27
+
28
+ # define image parameters
29
+ @img_parameters = BasicElements.define_img_parameters(@win_width, @win_height, @img_current)
30
+
31
+ # define buttons
32
+ @button_set = BasicElements.define_buttons
33
+
34
+ # define menu
35
+ @mb, @open_file, @quit = BasicElements.define_menu
36
+
37
+ # define the set for the packing of boxes
38
+ @box_set = {hbox: @hbox, vbox: @vbox, halign: @halign, mb: @mb}
39
+
40
+
41
+ end
42
+
43
+
44
+ def run
45
+
46
+ # open file action
47
+ @open_file.signal_connect("activate") do |w|
48
+ @img_parameters = BasicElements.define_img_parameters(@win_width, @win_height, @img_current)
49
+ @img_parameters = Actions.open_file_action(@window, @img_parameters, @button_set)
50
+ end
51
+
52
+ # previous button action
53
+ @button_set[:prev_btn].signal_connect("clicked") do
54
+ @img_parameters = Actions.prev_next_btn_action(@window, @img_parameters, "prev")
55
+ end
56
+
57
+ # next button action
58
+ @button_set[:next_btn].signal_connect("clicked") do
59
+ @img_parameters = Actions.prev_next_btn_action(@window, @img_parameters, "next")
60
+ end
61
+
62
+ # rotate button action
63
+ #----------------Rotate-Button------------------
64
+ @button_set[:rotate_btn].signal_connect("clicked") do
65
+ @img_parameters = Actions.rotate_btn_action(@img_parameters)
66
+ end
67
+
68
+ # select button action
69
+ @button_set[:select_btn].signal_connect("clicked") do
70
+ @img_parameters = Actions.select_delete_btn_action(@window, @img_parameters, @button_set, "select")
71
+
72
+ end
73
+
74
+ # delete button action
75
+ @button_set[:delete_btn].signal_connect("clicked") do
76
+ @img_parameters = Actions.select_delete_btn_action(@window, @img_parameters, @button_set, "delete")
77
+ end
78
+
79
+ @quit.signal_connect("activate") {Gtk.main_quit}
80
+
81
+ # pack the boxes
82
+ @box_set = Actions.pack_boxes(@window, @img_parameters[:img_current], @box_set, @button_set)
83
+
84
+ @window.show_all
85
+ Gtk.main
86
+ end
87
+ end
88
+ end
metadata ADDED
@@ -0,0 +1,130 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: wuffl
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - BluePeony
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-05-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: gtk3
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: fileutils
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: fastimage
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: stringio
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: "# Wuffl\n<strong> 1. Introduction </strong> \nWuffl is a simple image
70
+ viewer which can move pictures either to a „Selected“ folder or to a „Deleted“ folder.
71
+ \"Selected\" and \"Deleted\" folders are located in the same folder as the chosen
72
+ picture. Wuffl is available for Linux Ubuntu >=14.04, Mac and Windows. \n\nImagine
73
+ you review you vacation photographs and you need to select which pictures to show
74
+ to your friends at the next party or you work your way through a bunch of old pictures
75
+ and you need to select a view of them for a anniversary celebration. Wuffl allows
76
+ you to move the picture to an extra folder (\"Selected\" folder) or to a \"Deleted\"
77
+ folder while viewing it. \n<p>\nSource for the icons of the buttons: http://icons.mysitemyway.com
78
+ \ \n</p>\n<strong> 2. Installation </strong> \nTo use the Wuffl image viewer you
79
+ require:\n<ul>\n <li> <a href=\"https://www.ruby-lang.org/en/downloads/\"><code>ruby</code></a>
80
+ (v2.4)\n</ul>\nas well as the following Ruby gems: \n<ul>\n <li><code>fastimage</code>\n
81
+ \ <li><code>fileutils</code>\n <li><code>gtk3</code>\n <li><code>stringio</code></li>\n</ul>
82
+ \ \n\nGet the Wullf image viewer by typing ```gem install wuffl``` in your command
83
+ line.\n\n<strong>3. Usage</strong>\nUsing your terminal or cmd start Wuffl by typing
84
+ \ \n<code> wuffl</code>\n\nAfter starting Wuffl you'll see a start screen with a
85
+ line of buttons at the bottom. \nTo select an image go to the top left corner,
86
+ klick on <code>File</code> → <code>Open file</code> and browse to the location of
87
+ you images. Then select an image and click <code>open</code> or just doubleclick
88
+ on the selected image. \n\nThe buttons from left to right mean: <code>show previous
89
+ image</code>, <code>rotate current image</code>, <code>move the current image to
90
+ the folder 'Selected'</code>, <code>show next image</code>, <code>move the current
91
+ image to the folder 'Deleted'</code>.\n\n<strong>4. Remarks</strong> \nIf you have
92
+ any remarks, bugs, questions etc. please tell me, I'd be happy to help. \n"
93
+ email: blue.peony2314@gmail.com
94
+ executables:
95
+ - wuffl
96
+ extensions: []
97
+ extra_rdoc_files: []
98
+ files:
99
+ - LICENSE
100
+ - README
101
+ - bin/wuffl
102
+ - lib/wuffl/actions.rb
103
+ - lib/wuffl/basic_elements.rb
104
+ - lib/wuffl/image_actions.rb
105
+ - lib/wuffl/wuffl_interface.rb
106
+ homepage: https://github.com/BluePeony/wuffl
107
+ licenses:
108
+ - MIT
109
+ metadata: {}
110
+ post_install_message:
111
+ rdoc_options: []
112
+ require_paths:
113
+ - lib
114
+ required_ruby_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '1.9'
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ requirements: []
125
+ rubygems_version: 3.4.8
126
+ signing_key:
127
+ specification_version: 4
128
+ summary: image viewer with options to put an image either to the folder 'Selected'
129
+ or the folder 'Deleted' to preselect them for further use
130
+ test_files: []