mirror of https://github.com/MidnightCommander/mc
324 lines
7.1 KiB
C
324 lines
7.1 KiB
C
/* Module for creating a shaped window with text (for desktop icons)
|
|
*
|
|
* Copyright (C) 1998 the Free Software Foundation
|
|
*
|
|
* Author: Federico Mena <federico@nuclecu.unam.mx>
|
|
*/
|
|
|
|
#include <gnome.h>
|
|
#include <string.h>
|
|
#include "gdesktop.h"
|
|
#include <gdk/gdkx.h>
|
|
/* The spacing between the cute little icon and the text */
|
|
#define SPACING 2
|
|
|
|
/*
|
|
* Most of this code was yanked from the gtktooltips module in Gtk+.
|
|
* I have tweaked it a bit for MC's purposes - Federico
|
|
*/
|
|
|
|
struct text_info {
|
|
GList *rows;
|
|
GdkFont *font;
|
|
int width;
|
|
int height;
|
|
int baseline_skip;
|
|
};
|
|
|
|
|
|
static void
|
|
free_string (gpointer data, gpointer user_data)
|
|
{
|
|
if (data)
|
|
g_free (data);
|
|
}
|
|
|
|
static void
|
|
text_info_free (struct text_info *ti)
|
|
{
|
|
g_list_foreach (ti->rows, free_string, NULL);
|
|
g_list_free (ti->rows);
|
|
g_free (ti);
|
|
}
|
|
|
|
static struct text_info *
|
|
layout_text (GtkWidget *widget, char *text)
|
|
{
|
|
struct text_info *ti;
|
|
char *row_end, *row_text, *break_pos;
|
|
int i, row_width, window_width;
|
|
size_t len;
|
|
|
|
ti = g_new (struct text_info, 1);
|
|
|
|
ti->rows = NULL;
|
|
ti->font = widget->style->font;
|
|
ti->width = 0;
|
|
ti->height = 0;
|
|
|
|
ti->baseline_skip = ti->font->ascent + ti->font->descent;
|
|
|
|
window_width = 0;
|
|
|
|
while (*text) {
|
|
row_end = strchr (text, '\n');
|
|
if (!row_end)
|
|
row_end = strchr (text, '\0');
|
|
|
|
len = row_end - text + 1;
|
|
row_text = g_new (char, len);
|
|
memcpy (row_text, text, len - 1);
|
|
row_text[len - 1] = '\0';
|
|
|
|
/* Adjust the window's width or shorten the row until
|
|
* it fits in the window.
|
|
*/
|
|
|
|
while (1) {
|
|
row_width = gdk_string_width (ti->font, row_text);
|
|
if (!window_width) {
|
|
/* make an initial guess at window's width */
|
|
|
|
if (row_width > (gdk_screen_width () / 12))
|
|
window_width = gdk_screen_width () / 12;
|
|
else
|
|
window_width = row_width;
|
|
}
|
|
|
|
if (row_width <= window_width)
|
|
break;
|
|
|
|
if (strchr (row_text, ' ')) {
|
|
/* the row is currently too wide, but we have
|
|
* blanks in the row so we can break it into
|
|
* smaller pieces
|
|
*/
|
|
|
|
int avg_width = row_width / strlen (row_text);
|
|
|
|
i = window_width;
|
|
|
|
if (avg_width != 0)
|
|
i /= avg_width;
|
|
|
|
if ((size_t) i >= len)
|
|
i = len - 1;
|
|
|
|
break_pos = strchr (row_text + i, ' ');
|
|
if (!break_pos) {
|
|
break_pos = row_text+ i;
|
|
while (*--break_pos != ' ');
|
|
}
|
|
|
|
*break_pos = '\0';
|
|
} else {
|
|
/* We can't break this row into any smaller
|
|
* pieces, so we have no choice but to widen
|
|
* the window
|
|
*
|
|
* For MC, we may want to modify the code above
|
|
* so that it can also split the string on the
|
|
* slahes of filenames.
|
|
*/
|
|
|
|
window_width = row_width;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (row_width > ti->width)
|
|
ti->width = row_width;
|
|
|
|
ti->rows = g_list_append (ti->rows, row_text);
|
|
ti->height += ti->baseline_skip;
|
|
|
|
text += strlen (row_text);
|
|
if (!*text)
|
|
break;
|
|
|
|
if (text[0] == '\n' && text[1]) {
|
|
/* end of paragraph and there is more text to come */
|
|
ti->rows = g_list_append (ti->rows, NULL);
|
|
ti->height += ti->baseline_skip / 2;
|
|
}
|
|
|
|
text++; /* skip blank or newline */
|
|
}
|
|
|
|
return ti;
|
|
}
|
|
|
|
static void
|
|
paint_text (struct text_info *ti, GdkDrawable *drawable, GdkGC *gc, int x_ofs, int y_ofs)
|
|
{
|
|
int y, w;
|
|
GList *item;
|
|
|
|
y = y_ofs + ti->font->ascent;
|
|
|
|
for (item = ti->rows; item; item = item->next) {
|
|
if (item->data) {
|
|
w = gdk_string_width (ti->font, item->data);
|
|
gdk_draw_string (drawable, ti->font, gc, x_ofs + (ti->width - w) / 2, y, item->data);
|
|
y += ti->baseline_skip;
|
|
} else
|
|
y += ti->baseline_skip / 2;
|
|
}
|
|
}
|
|
|
|
static void
|
|
set_window_text (GtkWidget *window, GdkImlibImage *im, char *text)
|
|
{
|
|
GdkPixmap *pixmap;
|
|
GdkPixmap *im_pixmap;
|
|
GdkBitmap *mask;
|
|
GdkBitmap *im_mask;
|
|
struct text_info *ti;
|
|
GdkColor color;
|
|
GdkGC *gc;
|
|
int width, height;
|
|
|
|
ti = layout_text (window, text);
|
|
|
|
width = MAX (ti->width, im->rgb_width);
|
|
height = im->rgb_height + SPACING + ti->height;
|
|
|
|
/* pixmap */
|
|
|
|
pixmap = gdk_pixmap_new (window->window, width, height, gdk_imlib_get_visual ()->depth);
|
|
|
|
gc = gdk_gc_new (pixmap);
|
|
|
|
gdk_color_white (gdk_imlib_get_colormap (), &color);
|
|
gdk_gc_set_foreground (gc, &color);
|
|
gdk_draw_rectangle (pixmap, gc, TRUE, 0, 0, width, height);
|
|
|
|
im_pixmap = gdk_imlib_move_image (im);
|
|
gdk_window_copy_area (pixmap, gc,
|
|
(width - im->rgb_width) / 2,
|
|
0,
|
|
im_pixmap,
|
|
0, 0,
|
|
im->rgb_width, im->rgb_height);
|
|
gdk_imlib_free_pixmap (im_pixmap);
|
|
|
|
/* mask */
|
|
|
|
mask = gdk_pixmap_new (window->window, width, height, 1);
|
|
|
|
gc = gdk_gc_new (mask);
|
|
|
|
color.pixel = 0;
|
|
gdk_gc_set_foreground (gc, &color);
|
|
gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
|
|
|
|
im_mask = gdk_imlib_move_mask (im);
|
|
if (im_mask) {
|
|
gdk_window_copy_area (mask, gc,
|
|
(width - im->rgb_width) / 2,
|
|
0,
|
|
im_mask,
|
|
0, 0,
|
|
im->rgb_width, im->rgb_height);
|
|
gdk_imlib_free_bitmap (im_mask);
|
|
}
|
|
|
|
color.pixel = 1;
|
|
gdk_gc_set_foreground (gc, &color);
|
|
paint_text (ti, mask, gc,
|
|
(width - ti->width) / 2,
|
|
im->rgb_height + SPACING);
|
|
|
|
gdk_gc_destroy (gc);
|
|
|
|
/* set contents */
|
|
|
|
gtk_widget_set_usize (window, width, height);
|
|
gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
|
|
gdk_window_shape_combine_mask (window->window, mask, 0, 0);
|
|
|
|
gdk_pixmap_unref (pixmap);
|
|
gdk_pixmap_unref (mask);
|
|
|
|
text_info_free (ti);
|
|
}
|
|
|
|
GtkWidget *
|
|
create_transparent_text_window (char *file, char *text, int extra_events)
|
|
{
|
|
GtkWidget *window;
|
|
GdkImlibImage *im;
|
|
static GdkCursor *cursor;
|
|
|
|
if (!g_file_exists (file))
|
|
return NULL;
|
|
|
|
im = gdk_imlib_load_image (file);
|
|
if (!im)
|
|
return NULL;
|
|
|
|
gtk_widget_push_visual (gdk_imlib_get_visual ());
|
|
gtk_widget_push_colormap (gdk_imlib_get_colormap ());
|
|
|
|
window = gtk_window_new (GTK_WINDOW_POPUP);
|
|
gtk_widget_set_events (window, gtk_widget_get_events (window) | extra_events);
|
|
|
|
gtk_widget_pop_colormap ();
|
|
gtk_widget_pop_visual ();
|
|
|
|
gtk_widget_realize (window);
|
|
|
|
gdk_imlib_render (im, im->rgb_width, im->rgb_height);
|
|
|
|
set_window_text (window, im, text);
|
|
|
|
gdk_imlib_destroy_image (im);
|
|
|
|
if (!cursor)
|
|
cursor = gdk_cursor_new (GDK_TOP_LEFT_ARROW); /* FIXME: this is never freed */
|
|
|
|
gdk_window_set_cursor (window->window, cursor);
|
|
|
|
return window;
|
|
}
|
|
|
|
GtkWidget *
|
|
make_transparent_window (char *file)
|
|
{
|
|
GdkImlibImage *im;
|
|
GtkWidget *window;
|
|
XSetWindowAttributes xwa;
|
|
|
|
if (!g_file_exists (file))
|
|
return NULL;
|
|
|
|
im = gdk_imlib_load_image (file);
|
|
if (!im)
|
|
return NULL;
|
|
|
|
gtk_widget_push_visual (gdk_imlib_get_visual ());
|
|
gtk_widget_push_colormap (gdk_imlib_get_colormap ());
|
|
|
|
window = gtk_window_new (GTK_WINDOW_POPUP);
|
|
gtk_widget_pop_colormap ();
|
|
gtk_widget_pop_visual ();
|
|
|
|
gtk_widget_realize (window);
|
|
|
|
xwa.save_under = True;
|
|
XChangeWindowAttributes (GDK_WINDOW_XDISPLAY (window->window),
|
|
GDK_WINDOW_XWINDOW (window->window),
|
|
CWSaveUnder, &xwa);
|
|
|
|
gtk_widget_set_usize (window, im->rgb_width, im->rgb_height);
|
|
|
|
/* All of the following 3 lines should not be required, only
|
|
* gdk_imlib_apply_image, but is is buggy.
|
|
*/
|
|
gdk_imlib_render (im, im->rgb_width, im->rgb_height);
|
|
gdk_window_set_back_pixmap (window->window, gdk_imlib_move_image (im), FALSE);
|
|
gdk_window_shape_combine_mask (window->window, gdk_imlib_move_mask (im), 0, 0);
|
|
|
|
return window;
|
|
}
|