wuffl 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: []