@@ -13,7 +13,8 @@ module gtk_app |
| 13 | 13 | gtk_label_new, gtk_label_set_text, gtk_widget_set_halign, & |
| 14 | 14 | GTK_ALIGN_START, gtk_progress_bar_new, gtk_progress_bar_set_fraction, & |
| 15 | 15 | gtk_progress_bar_set_text, gtk_progress_bar_set_show_text, & |
| 16 | | - gtk_widget_set_visible |
| 16 | + gtk_widget_set_visible, & |
| 17 | + gtk_button_new, gtk_button_set_icon_name |
| 17 | 18 | use g, only: g_application_run, g_idle_add |
| 18 | 19 | use treemap_widget, only: create_treemap_widget, set_scan_path, register_navigation_callback, & |
| 19 | 20 | register_key_handler, register_quit_callback, mark_initial_scan_complete |
@@ -91,7 +92,7 @@ contains |
| 91 | 92 | ! Callback when application activates (startup) |
| 92 | 93 | subroutine on_activate(app, user_data) bind(c) |
| 93 | 94 | type(c_ptr), value :: app, user_data |
| 94 | | - type(c_ptr) :: drawing_area, main_box, toolbar, scan_btn, quit_btn, status_bar, breadcrumb_bar |
| 95 | + type(c_ptr) :: drawing_area, main_box, toolbar, open_dir_btn, scan_btn, status_bar, breadcrumb_bar |
| 95 | 96 | character(len=512) :: scan_path |
| 96 | 97 | integer(c_int) :: idle_id |
| 97 | 98 | |
@@ -124,18 +125,19 @@ contains |
| 124 | 125 | ! Create toolbar (horizontal box) |
| 125 | 126 | toolbar = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5_c_int) |
| 126 | 127 | |
| 128 | + ! Create Open Directory button with folder icon |
| 129 | + open_dir_btn = gtk_button_new() |
| 130 | + call gtk_button_set_icon_name(open_dir_btn, "folder-open"//c_null_char) |
| 131 | + call g_signal_connect(open_dir_btn, "clicked"//c_null_char, & |
| 132 | + c_funloc(on_open_dir_clicked), c_null_ptr) |
| 133 | + call gtk_box_append(toolbar, open_dir_btn) |
| 134 | + |
| 127 | 135 | ! Create Scan button |
| 128 | 136 | scan_btn = gtk_button_new_with_label("Scan"//c_null_char) |
| 129 | 137 | call g_signal_connect(scan_btn, "clicked"//c_null_char, & |
| 130 | 138 | c_funloc(on_scan_clicked), c_null_ptr) |
| 131 | 139 | call gtk_box_append(toolbar, scan_btn) |
| 132 | 140 | |
| 133 | | - ! Create Quit button |
| 134 | | - quit_btn = gtk_button_new_with_label("Quit"//c_null_char) |
| 135 | | - call g_signal_connect(quit_btn, "clicked"//c_null_char, & |
| 136 | | - c_funloc(on_quit_clicked), c_null_ptr) |
| 137 | | - call gtk_box_append(toolbar, quit_btn) |
| 138 | | - |
| 139 | 141 | ! Create progress bar (always visible but starts at 0%) |
| 140 | 142 | ! Place it in toolbar, expanded to fill remaining space (pushes to right) |
| 141 | 143 | progress_bar_ptr = gtk_progress_bar_new() |
@@ -212,18 +214,96 @@ contains |
| 212 | 214 | print *, "Window size: ", DEFAULT_WIDTH, "x", DEFAULT_HEIGHT |
| 213 | 215 | end subroutine on_activate |
| 214 | 216 | |
| 217 | + ! Callback when Open Directory button is clicked |
| 218 | + ! NOTE: Uses system command for file picking until GTK4 file dialog bindings are available |
| 219 | + subroutine on_open_dir_clicked(button, user_data) bind(c) |
| 220 | + type(c_ptr), value :: button, user_data |
| 221 | + character(len=1024) :: selected_path |
| 222 | + integer :: status |
| 223 | + |
| 224 | + print *, "Open Directory button clicked!" |
| 225 | + |
| 226 | + ! Call helper to show native file picker |
| 227 | + call show_native_directory_picker(selected_path, status) |
| 228 | + |
| 229 | + if (status == 0 .and. len_trim(selected_path) > 0) then |
| 230 | + print *, "Selected directory: ", trim(selected_path) |
| 231 | + |
| 232 | + ! Update global scan path |
| 233 | + global_scan_path = selected_path |
| 234 | + call set_scan_path(selected_path) |
| 235 | + |
| 236 | + ! TODO: Trigger rescan here |
| 237 | + print *, "TODO: Trigger rescan of: ", trim(selected_path) |
| 238 | + else |
| 239 | + print *, "Directory selection cancelled or failed" |
| 240 | + end if |
| 241 | + end subroutine on_open_dir_clicked |
| 242 | + |
| 215 | 243 | ! Callback when Scan button is clicked |
| 216 | 244 | subroutine on_scan_clicked(button, user_data) bind(c) |
| 217 | 245 | type(c_ptr), value :: button, user_data |
| 218 | 246 | print *, "Scan button clicked! (Directory chooser coming soon...)" |
| 219 | 247 | end subroutine on_scan_clicked |
| 220 | 248 | |
| 221 | | - ! Callback when Quit button is clicked |
| 222 | | - subroutine on_quit_clicked(button, user_data) bind(c) |
| 223 | | - type(c_ptr), value :: button, user_data |
| 224 | | - print *, "Quit button clicked" |
| 225 | | - call sniffly_app_quit() |
| 226 | | - end subroutine on_quit_clicked |
| 249 | + ! Show native OS directory picker using system commands |
| 250 | + ! This is a workaround until GTK4 file dialog bindings are available |
| 251 | + subroutine show_native_directory_picker(path, status) |
| 252 | + character(len=*), intent(out) :: path |
| 253 | + integer, intent(out) :: status |
| 254 | + character(len=2048) :: command, temp_file |
| 255 | + integer :: unit, ios |
| 256 | + logical :: file_exists |
| 257 | + |
| 258 | + path = "" |
| 259 | + status = -1 |
| 260 | + |
| 261 | + ! Create temp file for output |
| 262 | + temp_file = "/tmp/sniffly_picker.txt" |
| 263 | + |
| 264 | + ! Platform-specific command |
| 265 | +#ifdef __APPLE__ |
| 266 | + ! macOS: Use osascript to show native folder picker |
| 267 | + command = 'osascript -e ''POSIX path of (choose folder with prompt "Select directory to scan:")'' > ' & |
| 268 | + // trim(temp_file) // ' 2>&1' |
| 269 | +#else |
| 270 | + ! Linux: Try zenity, fallback to kdialog |
| 271 | + command = 'zenity --file-selection --directory > ' // trim(temp_file) // & |
| 272 | + ' 2>&1 || kdialog --getexistingdirectory . > ' // trim(temp_file) // ' 2>&1' |
| 273 | +#endif |
| 274 | + |
| 275 | + print *, "Executing: ", trim(command) |
| 276 | + |
| 277 | + ! Execute command |
| 278 | + call execute_command_line(trim(command), exitstat=status) |
| 279 | + |
| 280 | + ! Read result from temp file |
| 281 | + inquire(file=trim(temp_file), exist=file_exists) |
| 282 | + if (file_exists) then |
| 283 | + open(newunit=unit, file=trim(temp_file), status='old', action='read', iostat=ios) |
| 284 | + if (ios == 0) then |
| 285 | + read(unit, '(A)', iostat=ios) path |
| 286 | + close(unit) |
| 287 | + |
| 288 | + ! Remove temp file |
| 289 | + call execute_command_line('rm -f ' // trim(temp_file)) |
| 290 | + |
| 291 | + ! Trim whitespace and check if valid |
| 292 | + path = trim(adjustl(path)) |
| 293 | + if (len_trim(path) > 0) then |
| 294 | + status = 0 |
| 295 | + print *, "Got path: ", trim(path) |
| 296 | + else |
| 297 | + status = 1 |
| 298 | + end if |
| 299 | + else |
| 300 | + close(unit) |
| 301 | + status = 1 |
| 302 | + end if |
| 303 | + else |
| 304 | + status = 1 |
| 305 | + end if |
| 306 | + end subroutine show_native_directory_picker |
| 227 | 307 | |
| 228 | 308 | ! Update status bar with scan information |
| 229 | 309 | subroutine sniffly_update_status(message) |