1 /*
2 	Copyright (C) 2004, 2005 Stephen Bach
3 	This file is part of the Viewglob package.
4 
5 	Viewglob is free software; you can redistribute it and/or modify
6 	it under the terms of the GNU General Public License as published by
7 	the Free Software Foundation; either version 2 of the License, or
8 	(at your option) any later version.
9 
10 	Viewglob is distributed in the hope that it will be useful,
11 	but WITHOUT ANY WARRANTY; without even the implied warranty of
12 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 	GNU General Public License for more details.
14 
15 	You should have received a copy of the GNU General Public License
16 	along with Viewglob; if not, write to the Free Software
17 	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19 
20 #include "common.h"
21 #include "file_box.h"
22 #include "param-io.h"
23 #include "dircont.h"
24 #include <gtk/gtk.h>
25 #include <string.h>   /* For strcmp */
26 
27 /* For create_gradient(). */
28 #include <gdk-pixbuf/gdk-pixbuf.h>
29 #include <gdk-pixbuf/gdk-pixdata.h>
30 
31 /*#define WIDTH_BUFFER 5*/
32 #define WIDTH_BUFFER 0
33 #define DIR_NAME_SPACER 3
34 #define COUNT_SPACER 20
35 #define BASE_DIR_FONT_SIZE    +2
36 #define BASE_COUNT_FONT_SIZE  -1
37 /*#define BASE_COUNT_FONT_SIZE  0*/
38 
39 /* --- prototypes --- */
40 static void dircont_class_init(DirContClass* klass);
41 static void dircont_init(DirCont* dc);
42 
43 static void  dircont_size_request(GtkWidget* widget,
44 		GtkRequisition* requisition);
45 static void  dircont_size_allocate(GtkWidget *widget,
46 		GtkAllocation *allocation);
47 
48 static gboolean button_press_event(GtkWidget *widget,
49 		GdkEventButton *event, DirCont* dc);
50 gboolean enter_leave_notify_event(GtkWidget* header, GdkEventCrossing* event,
51 		DirCont* dc);
52 static gboolean header_expose_event(GtkWidget* header, GdkEventExpose* event,
53 		DirCont* dc);
54 static gboolean scrolled_window_expose_event(GtkWidget* header,
55 		GdkEventExpose* event, DirCont* dc);
56 
57 static void reset_count_layout(DirCont* dc);
58 static void scroll(DirCont* dc, gdouble pos);
59 static GdkPixbuf* create_gradient(GdkColor* color1, GdkColor* color2,
60 		gint width, gint height, gboolean horz);
61 
62 
63 /* --- variables --- */
64 static gpointer parent_class = NULL;
65 static gint header_height = 0;
66 
67 static PangoLayout* big_mask_layout = NULL;
68 static PangoLayout* small_mask_layout = NULL;
69 static PangoLayout* dev_mask_layout = NULL;
70 static GString* dir_tag_open = NULL;
71 static GString* dir_tag_close = NULL;
72 static GString* count_tag_open = NULL;
73 static GString* count_tag_close = NULL;
74 
75 
76 /* --- functions --- */
dircont_get_type(void)77 GType dircont_get_type(void) {
78 	static GType dircont_type = 0;
79 
80 	if (!dircont_type) {
81 		static const GTypeInfo dircont_info = {
82 			sizeof (DirContClass),
83 			NULL,		/* base_init */
84 			NULL,		/* base_finalize */
85 			(GClassInitFunc) dircont_class_init,
86 			NULL,		/* class_finalize */
87 			NULL,		/* class_data */
88 			sizeof (DirCont),
89 			0,		/* n_preallocs */
90 			(GInstanceInitFunc) dircont_init,
91 		};
92 		dircont_type = g_type_register_static (GTK_TYPE_VBOX, "DirCont",
93 				&dircont_info, 0);
94 	}
95 
96 	return dircont_type;
97 }
98 
99 
dircont_class_init(DirContClass * class)100 static void dircont_class_init(DirContClass* class) {
101 	GObjectClass *object_class;
102 	GtkWidgetClass *widget_class;
103 
104 
105 	object_class = G_OBJECT_CLASS(class);
106 	widget_class = GTK_WIDGET_CLASS(class);
107 
108 	/* Figure out how high the header should be. */
109 	gint height;
110 	GtkWidget* area = gtk_drawing_area_new();
111 	PangoLayout* layout = gtk_widget_create_pango_layout(area, NULL);
112 	gchar* markup = g_strconcat(
113 			dir_tag_open->str, "<b>",
114 			"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZZ12345"
115 			"</b>", dir_tag_close->str, NULL);
116 	pango_layout_set_markup(layout, markup, -1);
117 	g_free(markup);
118 	pango_layout_get_pixel_size(layout, NULL, &height);
119 	header_height += height;
120 	markup = g_strconcat(
121 			count_tag_open->str, "<b>",
122 			"0123456789()",
123 			"</b>", count_tag_close->str, NULL);
124 	pango_layout_set_markup(layout, markup, -1);
125 	g_free(markup);
126 	pango_layout_get_pixel_size(layout, NULL, &height);
127 	header_height += height;
128 	g_object_unref(G_OBJECT(layout));
129 	gtk_widget_destroy(area);
130 	/*header_height += 3;*/ /* For spacing */
131 
132 	if (!dir_tag_open)
133 		dir_tag_open = g_string_new(NULL);
134 	if (!dir_tag_close)
135 		dir_tag_close = g_string_new(NULL);
136 	if (!count_tag_open)
137 		count_tag_open = g_string_new(NULL);
138 	if (!count_tag_close)
139 		count_tag_close = g_string_new(NULL);
140 
141 	parent_class = g_type_class_peek_parent(class);
142 	widget_class->size_request = dircont_size_request;
143 	widget_class->size_allocate = dircont_size_allocate;
144 }
145 
146 
dircont_new(void)147 GtkWidget* dircont_new (void) {
148 	return g_object_new (DIRCONT_TYPE, NULL);
149 }
150 
151 
dircont_init(DirCont * dc)152 static void dircont_init(DirCont* dc) {
153 	GTK_WIDGET_SET_FLAGS(dc, GTK_NO_WINDOW);
154 
155 	GtkBox* box = GTK_BOX(dc);
156 	gtk_box_set_homogeneous(box, FALSE);
157 
158 	dc->name = g_string_new(NULL);
159 	dc->rank = dc->old_rank = -1;
160 	dc->marked = FALSE;
161 	dc->selected = g_string_new(NULL);
162 	dc->total = g_string_new(NULL);
163 	dc->hidden = g_string_new(NULL);
164 	dc->score = 0;
165 	dc->is_pwd = FALSE;
166 	dc->is_highlighted = FALSE;
167 	dc->is_active = FALSE;
168 
169 	/* The header is a drawing area. */
170 	dc->header = gtk_drawing_area_new();
171 	gtk_widget_set_size_request(dc->header, -1, header_height);
172 //	gtk_widget_set_state(dc->header, GTK_STATE_ACTIVE);
173 	gtk_widget_show(dc->header);
174 
175 	/* Create initial layouts. */
176 	dc->name_layout = gtk_widget_create_pango_layout(dc->header, NULL);
177 	dc->counts_layout = gtk_widget_create_pango_layout(dc->header, NULL);
178 
179 	/* If the scrolled window is not in an event box, it's not possible to
180 	   paint along its whole height (don't know why), which looks crumby.
181 	   This widget serves no further function. */
182 	dc->paint_event_box = gtk_event_box_new();
183 	gtk_widget_show(dc->paint_event_box);
184 
185 	/* Setup the scrolled window (for the file box) */
186 	dc->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
187 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(dc->scrolled_window),
188 			GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
189 
190 	/* Create the file box. */
191 	dc->file_box = file_box_new();
192 	wrap_box_set_hspacing(WRAP_BOX(dc->file_box), 3);
193 	gtk_container_set_border_width(GTK_CONTAINER(dc->file_box), 2);
194 	wrap_box_set_line_justify(WRAP_BOX(dc->file_box), GTK_JUSTIFY_LEFT);
195 	gtk_widget_show(dc->file_box);
196 
197 	g_signal_connect(G_OBJECT(dc->header), "expose-event",
198 			G_CALLBACK(header_expose_event), dc);
199 	g_signal_connect(G_OBJECT(dc->scrolled_window), "expose-event",
200 			G_CALLBACK(scrolled_window_expose_event), dc);
201 	g_signal_connect(G_OBJECT(dc->header), "button-press-event",
202 			G_CALLBACK(button_press_event), dc);
203 
204 	gtk_widget_add_events(dc->header,
205 			GDK_ENTER_NOTIFY_MASK |
206 			GDK_LEAVE_NOTIFY_MASK |
207 			GDK_BUTTON_PRESS_MASK |
208 			GDK_BUTTON_RELEASE_MASK);
209 
210 	g_signal_connect(G_OBJECT(dc->header), "enter-notify-event",
211 			G_CALLBACK(enter_leave_notify_event), dc);
212 	g_signal_connect(G_OBJECT(dc->header), "leave-notify-event",
213 			G_CALLBACK(enter_leave_notify_event), dc);
214 
215 	/* Initialize the layout. */
216 	dircont_set_name(dc, "<unset>");
217 	dircont_set_counts(dc, "0", "0", "0");
218 
219 	/* Pack 'em in. */
220 	gtk_scrolled_window_add_with_viewport(
221 			GTK_SCROLLED_WINDOW(dc->scrolled_window), dc->file_box);
222 	gtk_box_pack_start(box, dc->header, FALSE, FALSE, 0);
223 	gtk_container_add(GTK_CONTAINER(dc->paint_event_box), dc->scrolled_window);
224 	gtk_box_pack_start(box, dc->paint_event_box, TRUE, TRUE, 0);
225 }
226 
227 
enter_leave_notify_event(GtkWidget * header,GdkEventCrossing * event,DirCont * dc)228 gboolean enter_leave_notify_event(GtkWidget* header, GdkEventCrossing* event,
229 		DirCont* dc) {
230 
231 	if (!dc->is_active && !dc->is_restricted) {
232 		gboolean do_repaint = FALSE;
233 
234 		if (event->type == GDK_ENTER_NOTIFY) {
235 			do_repaint = dc->is_highlighted != TRUE;
236 			dc->is_highlighted = TRUE;
237 		}
238 		else if (event->type == GDK_LEAVE_NOTIFY) {
239 			do_repaint = dc->is_highlighted != FALSE;
240 			dc->is_highlighted = FALSE;
241 		}
242 
243 		if (do_repaint)
244 			dircont_repaint_header(dc);
245 	}
246 
247 	return FALSE;
248 }
249 
250 
251 /* Put the position of a known changed widget in the middle of the scrolled
252    window. */
dircont_scroll_to_changed(DirCont * dc)253 void dircont_scroll_to_changed(DirCont* dc) {
254 	g_return_if_fail(dc != NULL);
255 	g_return_if_fail(IS_DIRCONT(dc));
256 
257 	FileBox* fb = FILE_BOX(dc->file_box);
258 
259 	if (fb->changed_fi) {
260 
261 		gdouble page_inc, pos;
262 
263 		gint y = fb->changed_fi->widget->allocation.y;
264 
265 		GtkAdjustment* vadj= gtk_scrolled_window_get_vadjustment(
266 				GTK_SCROLLED_WINDOW(dc->scrolled_window));
267 
268 		page_inc = vadj->page_increment;
269 		pos = (gdouble)y - page_inc/2;
270 
271 		scroll(dc, pos);
272 	}
273 }
274 
275 
276 /* Move the scrolled window to the given position, clamping correctly. */
scroll(DirCont * dc,gdouble pos)277 static void scroll(DirCont* dc, gdouble pos) {
278 
279 	gdouble current, page_inc, step_inc, upper, lower;
280 
281 	GtkAdjustment* vadj = gtk_scrolled_window_get_vadjustment(
282 			GTK_SCROLLED_WINDOW(dc->scrolled_window));
283 
284 	current = gtk_adjustment_get_value(vadj);
285 	page_inc = vadj->page_increment;
286 	step_inc = vadj->step_increment;
287 	lower = vadj->lower;
288 
289 	/* Otherwise we scroll down into a page of black. */
290 	upper = vadj->upper - page_inc - step_inc;
291 
292 	pos = CLAMP(pos, lower, upper);
293 
294 	if (pos != current)
295 		gtk_adjustment_set_value(vadj, pos);
296 }
297 
298 
dircont_size_request(GtkWidget * widget,GtkRequisition * requisition)299 static void dircont_size_request(GtkWidget* widget,
300 		GtkRequisition* requisition) {
301 	DirCont* dc;
302 	GtkRequisition header_req = { 0, 0 }, paint_box_req = { 0, 0 };
303 
304 	dc = DIRCONT(widget);
305 
306 	if (GTK_WIDGET_VISIBLE(dc->header))
307 		gtk_widget_size_request(dc->header, &header_req);
308 	if (GTK_WIDGET_VISIBLE(dc->paint_event_box))
309 		gtk_widget_size_request(dc->paint_event_box, &paint_box_req);
310 
311 	requisition->width = header_req.width + paint_box_req.width +
312 		GTK_CONTAINER(dc)->border_width * 2;
313 	requisition->height = header_req.height + paint_box_req.height +
314 		GTK_CONTAINER(dc)->border_width * 2;
315 
316 	requisition->width = MIN(requisition->width,
317 			dc->optimal_width - WIDTH_BUFFER);
318 }
319 
320 
dircont_size_allocate(GtkWidget * widget,GtkAllocation * allocation)321 static void dircont_size_allocate(GtkWidget* widget,
322 		GtkAllocation* allocation) {
323 	DirCont* dc;
324 	GtkRequisition child_requisition;
325 	GtkAllocation child_allocation;
326 
327 	dc = DIRCONT(widget);
328 
329 	child_allocation.x = allocation->x + GTK_CONTAINER(dc)->border_width;
330 	child_allocation.width = MAX(1,(gint) allocation->width -
331 			(gint) GTK_CONTAINER(dc)->border_width * 2);
332 
333 	/* Header */
334 	gtk_widget_get_child_requisition(dc->header,
335 			&child_requisition);
336 	child_allocation.height = MIN(child_requisition.height,
337 			allocation->height);
338 	child_allocation.y = allocation->y + GTK_CONTAINER(dc)->border_width;
339 	gtk_widget_size_allocate (dc->header, &child_allocation);
340 
341 	/* Paint box (scrolled window and file box) */
342 	child_allocation.y += child_allocation.height;
343 	child_allocation.height = MAX(1,
344 			allocation->height - child_allocation.height);
345 	gtk_widget_size_allocate (dc->paint_event_box, &child_allocation);
346 }
347 
348 
349 /* Set the sizes of the directory heading font and the file count font. */
dircont_set_sizing(gint modifier)350 void dircont_set_sizing(gint modifier) {
351 	g_return_if_fail(modifier <= 10);
352 	g_return_if_fail(modifier >= -10);
353 
354 	gint dir_size, count_size;
355 	gint i;
356 
357 	/* Instantiate these if need be. */
358 	if (!dir_tag_open)
359 		dir_tag_open = g_string_new(NULL);
360 	if (!dir_tag_close)
361 		dir_tag_close = g_string_new(NULL);
362 	if (!count_tag_open)
363 		count_tag_open = g_string_new(NULL);
364 	if (!count_tag_close)
365 		count_tag_close = g_string_new(NULL);
366 
367 	dir_tag_open = g_string_truncate(dir_tag_open, 0);
368 	dir_tag_close = g_string_truncate(dir_tag_close, 0);
369 	count_tag_open = g_string_truncate(count_tag_open, 0);
370 	count_tag_close = g_string_truncate(count_tag_close, 0);
371 
372 	dir_size = BASE_DIR_FONT_SIZE + modifier;
373 	count_size = BASE_COUNT_FONT_SIZE + modifier;
374 
375 	/* Directory label sizing. */
376 	if (dir_size > 0) {
377 		for (i = 0; i < dir_size; i++) {
378 			dir_tag_open = g_string_append(dir_tag_open, "<big>");
379 			dir_tag_close = g_string_append(dir_tag_close, "</big>");
380 		}
381 	}
382 	else if (dir_size < 0) {
383 		for (i = 0; i > dir_size; i--) {
384 			dir_tag_open = g_string_append(dir_tag_open, "<small>");
385 			dir_tag_close = g_string_append(dir_tag_close, "</small>");
386 		}
387 	}
388 
389 	/* File count label sizing. */
390 	if (count_size > 0) {
391 		for (i = 0; i < count_size; i++) {
392 			count_tag_open = g_string_append(count_tag_open, "<big>");
393 			count_tag_close = g_string_append(count_tag_close, "</big>");
394 		}
395 	}
396 	else if (count_size < 0) {
397 		for (i = 0; i > count_size; i--) {
398 			count_tag_open = g_string_append(count_tag_open, "<small>");
399 			count_tag_close = g_string_append(count_tag_close, "</small>");
400 		}
401 	}
402 }
403 
404 
dircont_set_name(DirCont * dc,const gchar * name)405 void dircont_set_name(DirCont* dc, const gchar* name) {
406 	g_return_if_fail(dc != NULL);
407 	g_return_if_fail(IS_DIRCONT(dc));
408 	g_return_if_fail(name != NULL);
409 
410 	if (!STREQ(name, dc->name->str)) {
411 
412 		dc->name = g_string_assign(dc->name, name);
413 
414 		gchar* escaped;
415 		gchar* utf8;
416 		gchar* markup;
417 		gsize len;
418 
419 		utf8 = g_filename_to_utf8(
420 				dc->name->str, dc->name->len, NULL, &len, NULL);
421 		escaped = g_markup_escape_text(utf8, len);
422 		markup = g_strconcat(
423 				dir_tag_open->str, "<b>",
424 				escaped,
425 				"</b>", dir_tag_close->str, NULL);
426 
427 		pango_layout_set_markup(dc->name_layout, markup, -1);
428 
429 		g_free(markup);
430 		g_free(escaped);
431 		g_free(utf8);
432 	}
433 }
434 
435 
dircont_set_counts(DirCont * dc,const gchar * selected,const gchar * total,const gchar * hidden)436 void dircont_set_counts(DirCont* dc,
437 		const gchar* selected, const gchar* total, const gchar* hidden) {
438 	g_return_if_fail(dc != NULL);
439 	g_return_if_fail(IS_DIRCONT(dc));
440 	g_return_if_fail(selected != NULL);
441 	g_return_if_fail(total != NULL);
442 	g_return_if_fail(hidden != NULL);
443 
444 	gboolean changed = FALSE;
445 
446 	/* Figure out if anything needs to be changed. */
447 	if ( (changed |= !STREQ(selected, dc->selected->str)) )
448 		dc->selected = g_string_assign(dc->selected, selected);
449 	if ( (changed |= !STREQ(total, dc->total->str)) ) {
450 		dc->is_restricted = STREQ(total, "0");
451 		dc->total = g_string_assign(dc->total, total);
452 	}
453 	if ( (changed |= !STREQ(hidden, dc->hidden->str)) )
454 		dc->hidden = g_string_assign(dc->hidden, hidden);
455 
456 	if (changed)
457 		reset_count_layout(dc);
458 }
459 
460 
reset_count_layout(DirCont * dc)461 static void reset_count_layout(DirCont* dc) {
462 	g_return_if_fail(dc != NULL);
463 
464 	gchar* markup;
465 
466 	if (dc->is_restricted) {
467 		markup = g_strconcat(count_tag_open->str,
468 				"(Restricted)",
469 				count_tag_close->str, NULL);
470 	}
471 	else {
472 		markup = g_strconcat(count_tag_open->str, "<b>",
473 				dc->selected->str, " selected, ",
474 				dc->total->str, " total (",
475 				dc->hidden->str, " hidden)",
476 				"</b>", count_tag_close->str, NULL);
477 	}
478 	pango_layout_set_markup(dc->counts_layout, markup, -1);
479 	g_free(markup);
480 }
481 
482 
dircont_repaint_header(DirCont * dc)483 void dircont_repaint_header(DirCont* dc) {
484 	/* Make sure the header gets redrawn. */
485 	if (dc->header->window) {
486 		GdkRectangle rect = { 0, 0, 0, 0, };
487 		rect.width = dc->header->allocation.width;
488 		rect.height = dc->header->allocation.height;
489 		gdk_window_invalidate_rect(dc->header->window, &rect, FALSE);
490 	}
491 }
492 
493 
dircont_mark(DirCont * dc,gint new_rank)494 void dircont_mark(DirCont* dc, gint new_rank) {
495 	g_return_if_fail(dc != NULL);
496 	g_return_if_fail(IS_DIRCONT(dc));
497 
498 	dc->marked = TRUE;
499 	dc->old_rank = dc->rank;
500 	dc->rank = new_rank;
501 }
502 
503 
dircont_is_new(const DirCont * dc)504 gboolean dircont_is_new(const DirCont* dc) {
505 	g_return_val_if_fail(dc != NULL, TRUE);
506 	g_return_val_if_fail(IS_DIRCONT(dc), TRUE);
507 
508 	return dc->old_rank < 0;
509 }
510 
511 
dircont_set_active(DirCont * dc,gboolean setting)512 void dircont_set_active(DirCont* dc, gboolean setting) {
513 	g_return_if_fail(dc != NULL);
514 	g_return_if_fail(IS_DIRCONT(dc));
515 
516 	if (setting != dc->is_active) {
517 		dc->is_active = setting;
518 		reset_count_layout(dc);
519 	}
520 
521 	if (dc->is_active) {
522 		gtk_widget_set_state(dc->header, GTK_STATE_SELECTED);
523 		gtk_widget_show(dc->scrolled_window);
524 		dc->is_highlighted = FALSE;
525 	}
526 	else {
527 		gtk_widget_hide(dc->scrolled_window);
528 		if (dc->is_restricted)
529 			gtk_widget_set_state(dc->header, GTK_STATE_INSENSITIVE);
530 		else
531 			gtk_widget_set_state(dc->header, GTK_STATE_ACTIVE);
532 	}
533 }
534 
535 
dircont_set_pwd(DirCont * dc,gboolean setting)536 void dircont_set_pwd(DirCont* dc, gboolean setting) {
537 	g_return_if_fail(dc != NULL);
538 	g_return_if_fail(IS_DIRCONT(dc));
539 
540 	if (setting != dc->is_pwd)
541 		dc->is_pwd = setting;
542 }
543 
544 
545 /* Remove all memory associated with this DirCont. */
dircont_free(DirCont * dc)546 void dircont_free(DirCont* dc) {  //FIXME
547 	g_return_if_fail(dc != NULL);
548 	g_return_if_fail(IS_DIRCONT(dc));
549 
550 	GtkWidget* widget;
551 
552 
553 	widget = GTK_WIDGET(dc);
554 	gtk_widget_hide(widget);
555 
556 	/* This also gets the FItems associated with the file box. */
557 	file_box_destroy(FILE_BOX(dc->file_box));
558 
559 	g_string_free(dc->name, TRUE);
560 	g_string_free(dc->selected, TRUE);
561 	g_string_free(dc->total, TRUE);
562 	g_string_free(dc->hidden, TRUE);
563 
564 	g_object_unref(dc->name_layout);
565 	g_object_unref(dc->counts_layout);
566 
567 	/* Should take care of everything else. */
568 	gtk_widget_destroy(widget);
569 }
570 
571 
572 /* Colour that thin line of space between the window and the scroll bar. */
scrolled_window_expose_event(GtkWidget * scrollwin,GdkEventExpose * event,DirCont * dc)573 static gboolean scrolled_window_expose_event(GtkWidget* scrollwin,
574 		GdkEventExpose *event, DirCont* dc) {
575 
576 	if (dc->is_active && scrollwin->window) {
577 		/* Colour the whole area of the scrolled window, just to be sure. */
578 		GdkGC* light_gc =
579 			scrollwin->style->light_gc[GTK_WIDGET_STATE(dc->header)];
580 		gdk_draw_rectangle(
581 				scrollwin->window,
582 				light_gc,
583 				TRUE,
584 				0, 0,
585 				scrollwin->allocation.width,
586 				scrollwin->allocation.height);
587 	}
588 
589 	return FALSE;
590 }
591 
592 
593 /* Paint the header in various ways, depending on the state of the dc. */
header_expose_event(GtkWidget * header,GdkEventExpose * event,DirCont * dc)594 static gboolean header_expose_event(GtkWidget* header, GdkEventExpose* event,
595 		DirCont* dc) {
596 
597 	static GdkPixbuf* active_grad = NULL;
598 	static GdkPixbuf* highlight_grad = NULL;
599 
600 
601 	GdkGC* name_gc;
602 	GdkGC* counts_gc;
603 
604 	GdkGC* fg_gc = header->style->fg_gc[GTK_WIDGET_STATE(header)];
605 	GdkGC* bg_gc = header->style->bg_gc[GTK_WIDGET_STATE(header)];
606 //	GdkGC* light_gc = header->style->light_gc[GTK_WIDGET_STATE(header)];
607 //	GdkGC* dark_gc = header->style->dark_gc[GTK_WIDGET_STATE(header)];
608 //	GdkGC* mid_gc = header->style->mid_gc[GTK_WIDGET_STATE(header)];
609 //	GdkGC* text_gc = header->style->text_gc[GTK_WIDGET_STATE(header)];
610 //	GdkGC* base_gc = header->style->base_gc[GTK_WIDGET_STATE(header)];
611 //	GdkGC* text_aa_gc = header->style->text_aa_gc[GTK_WIDGET_STATE(header)];
612 
613 	gint name_width, name_height, counts_width, counts_height;
614 
615 	pango_layout_get_pixel_size(dc->name_layout,
616 			&name_width, &name_height);
617 	pango_layout_get_pixel_size(dc->counts_layout,
618 			&counts_width, &counts_height);
619 
620 	if (dc->is_active) {
621 
622 		name_gc = fg_gc;
623 		counts_gc = fg_gc;
624 
625 		/* Remake the gradient if necessary. */
626 		if (!active_grad || gdk_pixbuf_get_width(active_grad) !=
627 				header->allocation.width) {
628 			if (active_grad)
629 				g_object_unref(active_grad);
630 			active_grad = create_gradient(
631 					&header->style->dark[GTK_STATE_SELECTED],
632 					&header->style->light[GTK_STATE_SELECTED],
633 					header->allocation.width,
634 					header->allocation.height, FALSE);
635 		}
636 
637 		gdk_draw_pixbuf(
638 				header->window,
639 				bg_gc,
640 				active_grad,
641 				0, 0, 0, 0,
642 				header->allocation.width, header->allocation.height,
643 				GDK_RGB_DITHER_NONE, 0, 0);
644 	}
645 	else {
646 		if (dc->is_highlighted) {
647 
648 			name_gc = fg_gc;
649 			counts_gc = fg_gc;
650 
651 			/* Remake the gradient if necessary. */
652 			if (!highlight_grad || gdk_pixbuf_get_width(highlight_grad) !=
653 					header->allocation.width) {
654 				if (highlight_grad)
655 					g_object_unref(highlight_grad);
656 				highlight_grad = create_gradient(
657 							&header->style->dark[GTK_STATE_ACTIVE],
658 							&header->style->bg[GTK_STATE_ACTIVE],
659 							header->allocation.width,
660 							header->allocation.height, FALSE);
661 			}
662 
663 			gdk_draw_pixbuf(
664 					header->window,
665 					bg_gc,
666 					highlight_grad,
667 					0, 0, 0, 0,
668 					header->allocation.width, header->allocation.height,
669 					GDK_RGB_DITHER_NONE, 0, 0);
670 		}
671 		else {
672 
673 			name_gc = fg_gc;
674 			counts_gc = fg_gc;
675 
676 			gdk_draw_rectangle(
677 					header->window,
678 					bg_gc,
679 					TRUE,
680 					0, 0,
681 					header->allocation.width, header->allocation.height);
682 		}
683 	}
684 
685 	/* Draw separator. */
686 	if (!dc->is_active && dc->rank != 0) {
687 		gdk_draw_rectangle(
688 				header->window,
689 				fg_gc,
690 				TRUE,
691 				0, 0,
692 				header->allocation.width, 1);
693 	}
694 
695 //	if (dc->is_pwd) {
696 //
697 //		gdk_draw_layout(
698 //				header->window,
699 //				counts_gc,
700 //				3, name_height,
701 //				dc->counts_layout);
702 //
703 //		GdkPoint points[] = {
704 //			{ DIR_NAME_SPACER, name_height },
705 //			{ DIR_NAME_SPACER, name_height + counts_height - 2 },
706 //			{ DIR_NAME_SPACER + COUNT_SPACER/2, name_height + counts_height/2 },
707 //		};
708 //
709 //		gdk_draw_polygon(
710 //				header->window,
711 //				fg_gc,
712 //				TRUE,
713 //				points,
714 //				3);
715 //	}
716 
717 	/* Draw name */
718 	gdk_draw_layout(
719 			header->window,
720 			name_gc,
721 			DIR_NAME_SPACER, 0,
722 			dc->name_layout);
723 
724 	/* Draw file counts */
725 	gdk_draw_layout(
726 			header->window,
727 			counts_gc,
728 			COUNT_SPACER, name_height,
729 			dc->counts_layout);
730 
731 
732 	/* If this is the active dc, show the masks as well. */
733 	if (dc->is_active) {
734 		gint mask_width, mask_height;
735 
736 		/* Current mask. */
737 		pango_layout_get_pixel_size(big_mask_layout, &mask_width, NULL);
738 
739 		if (3*DIR_NAME_SPACER + name_width + mask_width <
740 				header->allocation.width && big_mask_layout) {
741 			/* Use the big mask layout. */
742 			gdk_draw_layout(
743 					header->window,
744 					fg_gc,
745 					header->allocation.width - mask_width - DIR_NAME_SPACER,
746 					2,
747 					big_mask_layout);
748 		}
749 		else if (small_mask_layout) {
750 			/* Use the small mask layout. */
751 			pango_layout_get_pixel_size(small_mask_layout, &mask_width, NULL);
752 			gdk_draw_layout(
753 					header->window,
754 					fg_gc,
755 					header->allocation.width - mask_width - COUNT_SPACER,
756 					name_height,
757 					small_mask_layout);
758 		}
759 
760 		/* Developing mask. */
761 		pango_layout_get_pixel_size(dev_mask_layout,
762 				&mask_width, &mask_height);
763 
764 		if (mask_height && mask_width
765 				&& mask_height <= header->allocation.height) {
766 			gdk_draw_rectangle(
767 					header->window,
768 					fg_gc,
769 					TRUE,
770 					(header->allocation.width - mask_width)/2 - 2,
771 					(header->allocation.height - mask_height)/2,
772 					mask_width + 4, mask_height);
773 
774 			gdk_draw_layout(
775 					header->window,
776 					bg_gc,
777 					(header->allocation.width - mask_width)/2,
778 					(header->allocation.height - mask_height)/2,
779 					dev_mask_layout);
780 		}
781 	}
782 
783 	return TRUE;
784 }
785 
786 
button_press_event(GtkWidget * widget,GdkEventButton * event,DirCont * dc)787 static gboolean button_press_event(GtkWidget *widget, GdkEventButton *event,
788 		DirCont* dc) {
789 	g_return_val_if_fail(dc != NULL, FALSE);
790 	g_return_val_if_fail(IS_DIRCONT(dc), FALSE);
791 	g_return_val_if_fail(event != NULL, FALSE);
792 
793 	if (event->type == GDK_2BUTTON_PRESS && event->button == 1) {
794 		/* Double left-click -- write out the directory name. */
795 
796 		GString* string;
797 		string = g_string_new(dc->name->str);
798 
799 		/* Append a trailing '/' if need be. */
800 		if ( *(string->str + string->len - 1) != '/')
801 			string = g_string_append(string, "/");
802 
803 		if (!put_param(STDOUT_FILENO, P_FILE, string->str))
804 			g_warning("Could not write filename to stdout");
805 
806 		g_string_free(string, TRUE);
807 	}
808 
809 	return FALSE;
810 }
811 
812 
dircont_set_optimal_width(DirCont * dc,gint width)813 void dircont_set_optimal_width(DirCont* dc, gint width) {
814 	g_return_if_fail(dc != NULL);
815 	g_return_if_fail(IS_DIRCONT(dc));
816 	g_return_if_fail(width >= 0);
817 
818 	dc->optimal_width = width;
819 	file_box_set_optimal_width(FILE_BOX(dc->file_box),
820 			dc->optimal_width - WIDTH_BUFFER);
821 
822 	/*gtk_widget_queue_resize(GTK_WIDGET(dc->heading_event_box));*/
823 }
824 
825 
826 /* Set the new mask text and update the given DirCont (which is probably the
827    active DirCont). */
dircont_set_mask_string(DirCont * dc,const gchar * mask_str)828 void dircont_set_mask_string(DirCont* dc, const gchar* mask_str) {
829 	g_return_if_fail(mask_str != NULL);
830 
831 	gchar* utf8;
832 	gchar* escaped;
833 	gchar* markup;
834 
835 	/* Initialize the layouts if necessary. */
836 	if (!big_mask_layout || !small_mask_layout) {
837 		GtkWidget* area = gtk_drawing_area_new();
838 		big_mask_layout = gtk_widget_create_pango_layout(area, NULL);
839 		small_mask_layout = gtk_widget_create_pango_layout(area, NULL);
840 		gtk_widget_destroy(area);
841 	}
842 
843 	utf8 = g_filename_to_utf8(mask_str, -1, NULL, NULL, NULL);
844 	escaped = g_markup_escape_text(utf8, -1);
845 
846 	/* Big mask layout (when directory name is short) */
847 	markup = g_strconcat(
848 			dir_tag_open->str, "<small><b>",
849 			escaped,
850 			"</b></small>", dir_tag_close->str, NULL);
851 	pango_layout_set_markup(big_mask_layout, markup, -1);
852 	g_free(markup);
853 
854 	/* Small mask layout (when directory name is long) */
855 	markup = g_strconcat(
856 			count_tag_open->str, "<b>",
857 			"Mask: ", escaped,
858 			"</b>", count_tag_close->str, NULL);
859 	pango_layout_set_markup(small_mask_layout, markup, -1);
860 	g_free(markup);
861 
862 	g_free(escaped);
863 	g_free(utf8);
864 
865 	if (dc)
866 		dircont_repaint_header(dc);
867 }
868 
869 
870 /* Set the new developing mask text and update the given DirCont (which is
871    probably the active DirCont). */
dircont_set_dev_mask_string(DirCont * dc,const gchar * dev_mask_str)872 void dircont_set_dev_mask_string(DirCont* dc, const gchar* dev_mask_str) {
873 	g_return_if_fail(dev_mask_str != NULL);
874 
875 	gchar* utf8;
876 	gchar* escaped;
877 	gchar* markup;
878 
879 	/* Initialize the layout and colour attribute list if necessary. */
880 	if (!dev_mask_layout) {
881 		GtkWidget* area = gtk_drawing_area_new();
882 		dev_mask_layout = gtk_widget_create_pango_layout(area, NULL);
883 		gtk_widget_destroy(area);
884 	}
885 
886 	utf8 = g_filename_to_utf8(dev_mask_str, -1, NULL, NULL, NULL);
887 	escaped = g_markup_escape_text(utf8, -1);
888 
889 	markup = g_strconcat(
890 			dir_tag_open->str, "<big><b>",
891 			escaped,
892 			"</b></big>", dir_tag_close->str, NULL);
893 	pango_layout_set_markup(dev_mask_layout, markup, -1);
894 
895 	g_free(markup);
896 	g_free(escaped);
897 	g_free(utf8);
898 
899 	if (dc)
900 		dircont_repaint_header(dc);
901 }
902 
903 
904 /* Navigate the scrolled window for the given dc. */
dircont_nav(DirCont * dc,DirContNav nav)905 void dircont_nav(DirCont* dc, DirContNav nav) {
906 	g_return_if_fail(dc != NULL);
907 	g_return_if_fail(IS_DIRCONT(dc));
908 
909 	gdouble pos, page_inc;
910 
911 	GtkAdjustment* vadj= gtk_scrolled_window_get_vadjustment(
912 			GTK_SCROLLED_WINDOW(dc->scrolled_window));
913 
914 	pos = gtk_adjustment_get_value(vadj);
915 	page_inc = vadj->page_increment;
916 
917 	switch (nav) {
918 		case DCN_PGUP:
919 			pos -= page_inc;
920 			break;
921 		case DCN_PGDOWN:
922 			pos += page_inc;
923 			break;
924 		default:
925 			g_return_if_reached();
926 	}
927 
928 	/* Set the value. */
929 	scroll(dc, pos);
930 }
931 
932 
933 /* Grabbed this from xfce4.  It may have been written by any of the following
934    people:
935 	   Brian Tarricone <bjt23@cornell.edu>
936 	   Jasper Huijsmans <huysmans@users.sourceforge.net>
937 	   Benedikt Meurer <benedikt.meurer@unix-ag.uni-siegen.de> */
create_gradient(GdkColor * color1,GdkColor * color2,gint width,gint height,gboolean horz)938 static GdkPixbuf* create_gradient(GdkColor* color1, GdkColor* color2,
939 		gint width, gint height, gboolean horz) {
940 
941 	g_return_val_if_fail(color1 != NULL && color2 != NULL, NULL);
942 	g_return_val_if_fail(width > 0 && height > 0, NULL);
943 
944 	GdkPixbuf *pix;
945 	gint i, j;
946 	GdkPixdata pixdata;
947 	guint8 rgb[3];
948     GError *err = NULL;
949 
950 	pixdata.magic = GDK_PIXBUF_MAGIC_NUMBER;
951 	pixdata.length = GDK_PIXDATA_HEADER_LENGTH + (width * height * 3);
952 	pixdata.pixdata_type = GDK_PIXDATA_COLOR_TYPE_RGB |
953 		GDK_PIXDATA_SAMPLE_WIDTH_8 | GDK_PIXDATA_ENCODING_RAW;
954 	pixdata.rowstride = width * 3;
955 	pixdata.width = width;
956 	pixdata.height = height;
957 	pixdata.pixel_data = g_malloc(width * height * 3);
958 
959 	if(horz) {
960 		for(i = 0; i < width; i++) {
961 			rgb[0] = (color1->red +
962 					(i * (color2->red - color1->red) / width)) >> 8;
963 			rgb[1] = (color1->green +
964 					(i * (color2->green - color1->green) / width)) >> 8;
965 			rgb[2] = (color1->blue +
966 					(i * (color2->blue - color1->blue) / width)) >> 8;
967 			memcpy(pixdata.pixel_data + (i * 3), rgb, 3);
968 		}
969 
970 		for(i = 1; i < height; i++) {
971 			memcpy(pixdata.pixel_data + (i * pixdata.rowstride),
972 					pixdata.pixel_data, pixdata.rowstride);
973 		}
974 	}
975 	else {
976 		for(i = 0; i < height; i++) {
977 			rgb[0] = (color1->red +
978 					(i * (color2->red - color1->red) / height)) >> 8;
979 			rgb[1] = (color1->green
980 					+ (i * (color2->green - color1->green) / height)) >> 8;
981 			rgb[2] = (color1->blue
982 					+ (i * (color2->blue - color1->blue) / height)) >> 8;
983 			for(j = 0; j < width; j++) {
984 				memcpy(pixdata.pixel_data + (i * pixdata.rowstride) + (j * 3),
985 						rgb, 3);
986 			}
987 		}
988 	}
989 
990 	pix = gdk_pixbuf_from_pixdata(&pixdata, TRUE, &err);
991 	if (!pix) {
992 		g_warning("Unable to create color gradient: %s", err->message);
993 		g_error_free(err);
994 	}
995 
996 	return pix;
997 }
998 
999