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