2323
2424#include "nemo-preview-image.h"
2525#include "nemo-file-attributes.h"
26+ #include "nemo-icon-info.h"
2627#include <glib/gi18n.h>
2728
2829#define RESIZE_DEBOUNCE_MS 150
@@ -34,6 +35,7 @@ struct _NemoPreviewImage {
3435};
3536
3637typedef struct {
38+ GtkWidget * frame ;
3739 GtkWidget * drawing_area ;
3840 GtkWidget * message_label ;
3941 NemoFile * file ;
@@ -48,6 +50,9 @@ typedef struct {
4850
4951 /* Current surface to draw */
5052 cairo_surface_t * current_surface ;
53+
54+ /* Track if showing an icon vs image */
55+ gboolean showing_icon ;
5156} NemoPreviewImagePrivate ;
5257
5358G_DEFINE_TYPE_WITH_PRIVATE (NemoPreviewImage , nemo_preview_image , GTK_TYPE_BOX )
@@ -101,6 +106,17 @@ nemo_preview_image_init (NemoPreviewImage *preview)
101106 priv -> current_height = 0 ;
102107 priv -> current_pixbuf = NULL ;
103108 priv -> current_surface = NULL ;
109+ priv -> showing_icon = FALSE;
110+
111+ /* Create frame to hold drawing area */
112+ priv -> frame = gtk_frame_new (NULL );
113+ gtk_frame_set_shadow_type (GTK_FRAME (priv -> frame ), GTK_SHADOW_IN );
114+ gtk_widget_set_halign (priv -> frame , GTK_ALIGN_FILL );
115+ gtk_widget_set_valign (priv -> frame , GTK_ALIGN_FILL );
116+ gtk_widget_set_hexpand (priv -> frame , TRUE);
117+ gtk_widget_set_vexpand (priv -> frame , TRUE);
118+
119+ gtk_box_pack_start (GTK_BOX (preview ), priv -> frame , TRUE, TRUE, 0 );
104120
105121 /* Create drawing area widget */
106122 priv -> drawing_area = gtk_drawing_area_new ();
@@ -110,7 +126,7 @@ nemo_preview_image_init (NemoPreviewImage *preview)
110126 gtk_widget_set_vexpand (priv -> drawing_area , TRUE);
111127 g_signal_connect (priv -> drawing_area , "draw" ,
112128 G_CALLBACK (on_drawing_area_draw ), preview );
113- gtk_box_pack_start ( GTK_BOX ( preview ), priv -> drawing_area , TRUE, TRUE, 0 );
129+ gtk_container_add ( GTK_CONTAINER ( priv -> frame ), priv -> drawing_area );
114130
115131 /* Create message label (hidden by default) */
116132 priv -> message_label = gtk_label_new ("" );
@@ -120,6 +136,8 @@ nemo_preview_image_init (NemoPreviewImage *preview)
120136 "dim-label" );
121137 gtk_box_pack_start (GTK_BOX (preview ), priv -> message_label , TRUE, TRUE, 0 );
122138
139+ gtk_container_set_border_width (GTK_CONTAINER (preview ), 4 );
140+ gtk_widget_show_all (GTK_WIDGET (preview ));
123141 /* Connect size-allocate signal for resize handling */
124142 g_signal_connect (preview , "size-allocate" ,
125143 G_CALLBACK (on_size_allocate ), NULL );
@@ -173,9 +191,30 @@ on_drawing_area_draw (GtkWidget *widget,
173191 x_offset = (widget_width - surface_width ) / 2.0 ;
174192 y_offset = (widget_height - surface_height ) / 2.0 ;
175193
194+ /* Draw the image first */
176195 cairo_set_source_surface (cr , priv -> current_surface , x_offset , y_offset );
177196 cairo_paint (cr );
178197
198+ /* If showing an image (not an icon), draw a border on top of it */
199+ if (!priv -> showing_icon ) {
200+ GtkStyleContext * style_context ;
201+ GdkRGBA border_color ;
202+ gdouble border_width = 1.0 ;
203+ gdouble inset = 0.5 ; /* Inset border slightly inside image bounds */
204+
205+ /* Get the border color from the frame's style context */
206+ style_context = gtk_widget_get_style_context (priv -> frame );
207+ gtk_style_context_get_border_color (style_context , GTK_STATE_FLAG_NORMAL , & border_color );
208+
209+ /* Draw border rectangle inset from the image edge */
210+ cairo_set_source_rgba (cr , border_color .red , border_color .green ,
211+ border_color .blue , border_color .alpha );
212+ cairo_set_line_width (cr , border_width );
213+ cairo_rectangle (cr , x_offset + inset , y_offset + inset ,
214+ surface_width - (inset * 2 ), surface_height - (inset * 2 ));
215+ cairo_stroke (cr );
216+ }
217+
179218 return TRUE;
180219}
181220
@@ -196,6 +235,100 @@ is_image_file (NemoFile *file)
196235 return is_image ;
197236}
198237
238+ static void
239+ load_icon_at_size (NemoPreviewImage * widget ,
240+ gint width ,
241+ gint height )
242+ {
243+ NemoPreviewImagePrivate * priv ;
244+ NemoIconInfo * icon_info = NULL ;
245+ GtkIconTheme * icon_theme ;
246+ GdkPixbuf * icon_pixbuf = NULL ;
247+ cairo_surface_t * surface = NULL ;
248+ gint ui_scale ;
249+ gint icon_size ;
250+ const char * icon_name ;
251+ GError * error = NULL ;
252+
253+ priv = nemo_preview_image_get_instance_private (widget );
254+
255+ if (priv -> file == NULL ) {
256+ return ;
257+ }
258+
259+ if (width <= 1 || height <= 1 ) {
260+ return ;
261+ }
262+
263+ ui_scale = gtk_widget_get_scale_factor (GTK_WIDGET (widget ));
264+
265+ /* Calculate icon size - use the smaller dimension to fit in the space */
266+ icon_size = MIN (width , height ) * ui_scale ;
267+
268+ /* Get the icon info from the file */
269+ icon_info = nemo_file_get_icon (priv -> file , icon_size , 0 , ui_scale , 0 );
270+
271+ if (icon_info != NULL && icon_info -> icon_name != NULL ) {
272+ icon_name = icon_info -> icon_name ;
273+ icon_theme = gtk_icon_theme_get_default ();
274+
275+ /* Load the icon at the exact size we need */
276+ icon_pixbuf = gtk_icon_theme_load_icon_for_scale (icon_theme ,
277+ icon_name ,
278+ icon_size / ui_scale ,
279+ ui_scale ,
280+ GTK_ICON_LOOKUP_FORCE_SIZE ,
281+ & error );
282+
283+ if (icon_pixbuf != NULL ) {
284+ /* Save pixbuf for quick scaling during resize */
285+ if (priv -> current_pixbuf != NULL ) {
286+ g_object_unref (priv -> current_pixbuf );
287+ }
288+ priv -> current_pixbuf = g_object_ref (icon_pixbuf );
289+
290+ surface = gdk_cairo_surface_create_from_pixbuf (icon_pixbuf , ui_scale , NULL );
291+
292+ if (surface != NULL ) {
293+ /* Replace old surface with new one */
294+ if (priv -> current_surface != NULL ) {
295+ cairo_surface_destroy (priv -> current_surface );
296+ }
297+ priv -> current_surface = surface ;
298+ gtk_widget_show (priv -> drawing_area );
299+ gtk_widget_queue_draw (priv -> drawing_area );
300+ }
301+
302+ g_object_unref (icon_pixbuf );
303+ gtk_widget_hide (priv -> message_label );
304+ } else {
305+ /* Failed to load icon */
306+ if (error != NULL ) {
307+ g_warning ("Failed to load icon '%s': %s" , icon_name , error -> message );
308+ g_error_free (error );
309+ }
310+ gtk_label_set_text (GTK_LABEL (priv -> message_label ),
311+ _ ("(Failed to load icon)" ));
312+ gtk_widget_show (priv -> message_label );
313+ gtk_widget_hide (priv -> drawing_area );
314+ }
315+
316+ nemo_icon_info_unref (icon_info );
317+ } else {
318+ /* No icon info available */
319+ if (icon_info != NULL ) {
320+ nemo_icon_info_unref (icon_info );
321+ }
322+ gtk_label_set_text (GTK_LABEL (priv -> message_label ),
323+ _ ("(No icon available)" ));
324+ gtk_widget_show (priv -> message_label );
325+ gtk_widget_hide (priv -> drawing_area );
326+ }
327+
328+ priv -> current_width = width ;
329+ priv -> current_height = height ;
330+ }
331+
199332static void
200333load_image_at_size (NemoPreviewImage * widget ,
201334 gint width ,
@@ -342,7 +475,12 @@ on_resize_timeout (gpointer user_data)
342475 priv -> resize_timeout_id = 0 ;
343476
344477 gtk_widget_get_allocation (GTK_WIDGET (widget ), & allocation );
345- load_image_at_size (widget , allocation .width , allocation .height );
478+
479+ if (priv -> showing_icon ) {
480+ load_icon_at_size (widget , allocation .width , allocation .height );
481+ } else {
482+ load_image_at_size (widget , allocation .width , allocation .height );
483+ }
346484
347485 return G_SOURCE_REMOVE ;
348486}
@@ -434,53 +572,14 @@ nemo_preview_image_set_file (NemoPreviewImage *widget,
434572
435573 if (is_image_file (file )) {
436574 /* Load the image at current widget size */
575+ priv -> showing_icon = FALSE;
437576 gtk_widget_get_allocation (GTK_WIDGET (widget ), & allocation );
438577 load_image_at_size (widget , allocation .width , allocation .height );
439- } else if (nemo_file_is_directory (file )) {
440- /* Show folder icon via nemo_file API */
441- GdkPixbuf * icon_pixbuf ;
442- cairo_surface_t * surface ;
443- gint ui_scale = gtk_widget_get_scale_factor (GTK_WIDGET (widget ));
444-
445- icon_pixbuf = nemo_file_get_icon_pixbuf (file , 64 , TRUE, ui_scale , 0 );
446- if (icon_pixbuf != NULL ) {
447- surface = gdk_cairo_surface_create_from_pixbuf (icon_pixbuf , ui_scale , NULL );
448- if (surface != NULL ) {
449- if (priv -> current_surface != NULL ) {
450- cairo_surface_destroy (priv -> current_surface );
451- }
452- priv -> current_surface = surface ;
453- }
454- g_object_unref (icon_pixbuf );
455- }
456-
457- gtk_label_set_text (GTK_LABEL (priv -> message_label ), _ ("(Folder)" ));
458- gtk_widget_show (priv -> drawing_area );
459- gtk_widget_queue_draw (priv -> drawing_area );
460- gtk_widget_show (priv -> message_label );
461578 } else {
462- /* Non-image file: show file icon */
463- GdkPixbuf * icon_pixbuf ;
464- cairo_surface_t * surface ;
465- gint ui_scale = gtk_widget_get_scale_factor (GTK_WIDGET (widget ));
466-
467- icon_pixbuf = nemo_file_get_icon_pixbuf (file , 64 , TRUE, ui_scale , 0 );
468- if (icon_pixbuf != NULL ) {
469- surface = gdk_cairo_surface_create_from_pixbuf (icon_pixbuf , ui_scale , NULL );
470- if (surface != NULL ) {
471- if (priv -> current_surface != NULL ) {
472- cairo_surface_destroy (priv -> current_surface );
473- }
474- priv -> current_surface = surface ;
475- }
476- g_object_unref (icon_pixbuf );
477- }
478-
479- gtk_label_set_text (GTK_LABEL (priv -> message_label ),
480- _ ("(Not an image file)" ));
481- gtk_widget_show (priv -> drawing_area );
482- gtk_widget_queue_draw (priv -> drawing_area );
483- gtk_widget_show (priv -> message_label );
579+ /* Load folder or file icon at current widget size */
580+ priv -> showing_icon = TRUE;
581+ gtk_widget_get_allocation (GTK_WIDGET (widget ), & allocation );
582+ load_icon_at_size (widget , allocation .width , allocation .height );
484583 }
485584 }
486585}
@@ -519,4 +618,5 @@ nemo_preview_image_clear (NemoPreviewImage *widget)
519618 gtk_widget_hide (priv -> message_label );
520619 priv -> current_width = 0 ;
521620 priv -> current_height = 0 ;
621+ priv -> showing_icon = FALSE;
522622}
0 commit comments