summaryrefslogtreecommitdiff
path: root/UToolTips.pas
diff options
context:
space:
mode:
Diffstat (limited to 'UToolTips.pas')
-rw-r--r--UToolTips.pas401
1 files changed, 401 insertions, 0 deletions
diff --git a/UToolTips.pas b/UToolTips.pas
new file mode 100644
index 0000000..7a79075
--- /dev/null
+++ b/UToolTips.pas
@@ -0,0 +1,401 @@
+(*
+ Tux Commander - UToolTips - ToolTips utils and classes (used as the tree hints)
+ Copyright (C) 2008 Tomas Bzatek <tbzatek@users.sourceforge.net>
+ This unit is based on an idea from Gnome File Manager by Miroslav Bajtos <mirco@matfyz.cz>
+ Check for updates on tuxcmd.sourceforge.net
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*)
+unit UToolTips;
+
+interface
+
+uses glib2, gdk2, gtk2, pango;
+
+procedure FileListTipsInstall(ATreeView: PGtkTreeView);
+procedure FileListTipsEnable;
+procedure FileListTipsDisable;
+procedure FileListTipsHide;
+
+implementation
+
+uses SysUtils, DateUtils, Classes, GTKForms, GTKView, GTKControls, UMain, UEngines, UCore, UConfig,
+ UCoreUtils;
+
+var tip_window: PGtkWindow;
+ tip_label: PGtkLabel;
+ timer_id: gint;
+ tips_enabled: boolean;
+ data_row: PGtkTreePath;
+ data_column: PGtkTreeViewColumn;
+ data_panel: PGtkTreeView;
+ tips_timer: PGTimer;
+
+
+function event_handler(widget: PGtkWidget; event: PGdkEvent; user_data: gpointer): gboolean; cdecl; forward;
+function on_leave_notify(widget: PGtkWidget; event: PGdkEventCrossing; user_data: gpointer): gboolean; cdecl; forward;
+function show_tip_widget(user_data: gpointer): gboolean; cdecl; forward;
+procedure create_widgets; forward;
+function tip_event_handler(widget: PGtkWidget; event: PGdkEvent; user_data: gpointer): gboolean; cdecl; forward;
+function tips_button_press_event(widget: PGtkWidget; event: PGdkEventButton; user_data: gpointer):gboolean; cdecl; forward;
+
+
+
+procedure FileListTipsInstall(ATreeView: PGtkTreeView);
+begin
+ g_signal_connect_after(ATreeView, 'event-after', G_CALLBACK(@event_handler), nil);
+ g_signal_connect(ATreeView, 'leave-notify-event', G_CALLBACK(@on_leave_notify), nil);
+end;
+
+procedure file_list_tips_show_tip(AListView: PGtkTreeView; row: PGtkTreePath; column: PGtkTreeViewColumn);
+var Timeout: integer;
+ is_active: gboolean;
+ ms: gulong;
+ sec: gdouble;
+begin
+ if not tips_enabled then Exit;
+ FileListTipsHide;
+ g_object_get(G_OBJECT(FMain.FWidget), 'is-active', @is_active, nil);
+ if not is_active then Exit;
+ if Assigned(data_row) and (data_row <> row) then gtk_tree_path_free(data_row);
+ data_panel := AListView;
+ data_row := row;
+ data_column := column;
+ Timeout := ConstFileListTipsDelay;
+ if tips_timer <> nil then begin
+ sec := g_timer_elapsed(tips_timer, @ms);
+ if (sec < 1) and (ms < ConstFileListTipsDelayNeighbour * 1000) then Timeout := 1;
+// DebugMsg(['Time elapsed = ', Double(sec), ':', integer(ms div 1000)]);
+ end;
+ timer_id := gtk_timeout_add(Timeout, show_tip_widget, nil);
+end;
+
+
+function event_handler(widget: PGtkWidget; event: PGdkEvent; user_data: gpointer): gboolean; cdecl;
+var view: PGtkTreeView;
+ column: PGtkTreeViewColumn;
+ path: PGtkTreePath;
+begin
+ Result := False;
+ view := GTK_TREE_VIEW(widget);
+ if event^.any.window <> gtk_tree_view_get_bin_window(view) then Exit;
+ case event^._type of
+ GDK_KEY_PRESS, GDK_SCROLL:
+ begin
+ FileListTipsHide;
+ if tips_timer <> nil then begin
+ g_timer_destroy(tips_timer);
+ tips_timer := nil;
+ end;
+ Result := True;
+ end;
+ GDK_FOCUS_CHANGE: begin
+ Result := True;
+ if event^.focus_change._in <> 1 then FileListTipsHide;
+ end;
+ GDK_MOTION_NOTIFY: begin
+ Result := True;
+ if not gtk_tree_view_get_path_at_pos(view, Trunc(event^.motion.x), Trunc(event^.motion.y), path, column, nil, nil)
+ then begin
+ FileListTipsHide;
+ if tips_timer <> nil then begin
+ g_timer_destroy(tips_timer);
+ tips_timer := nil;
+ end;
+ Exit;
+ end;
+ if Assigned(path) and Assigned(column) then file_list_tips_show_tip(view, path, column);
+ end;
+ end;
+end;
+
+procedure draw_tip_widget(panel: PGtkTreeView; const text: Pgchar; x, y: integer);
+var requisition: TGtkRequisition;
+ widget: PGtkWidget;
+ w, scr_w, wx, wy: Integer;
+ FontDesc: PPangoFontDescription;
+ hadjustment: PGtkAdjustment;
+begin
+ try
+
+ if not Assigned(tip_window) then create_widgets;
+ if not ConfUseSystemFont then begin
+ FontDesc := pango_font_description_from_string(PChar(ConfPanelFont));
+ gtk_widget_modify_font(PGtkWidget(tip_label), FontDesc);
+ end else gtk_widget_modify_font(PGtkWidget(tip_label), nil);
+
+ gtk_label_set_markup(GTK_LABEL(tip_label), Text);
+ widget := GTK_WIDGET(panel);
+ hadjustment := gtk_tree_view_get_hadjustment(panel);
+ x := x - Trunc(gtk_adjustment_get_value(hadjustment));
+
+ gdk_window_get_origin(widget^.window, @wx, @wy);
+ x := x + wx;
+ y := y + wy;
+ scr_w := gdk_screen_width;
+
+ gtk_label_set_line_wrap(tip_label, False);
+ gtk_widget_size_request(GTK_WIDGET(tip_window), @requisition);
+ if x + requisition.width > scr_w {PGtkWidget(panel)^.allocation.width} then begin
+ gtk_label_set_line_wrap(tip_label, True);
+ gtk_widget_size_request(GTK_WIDGET(tip_window), @requisition);
+ end;
+
+ w := requisition.width;
+ if (x + w) > scr_w then x := scr_w - w
+ else if x < 0 then x := 0;
+ gtk_window_move(tip_window, x, y);
+ except
+ on E: Exception do DebugMsg(['*** Exception raised in function draw_tip_widget: (', E.ClassName, '): ', E.Message]);
+ end;
+end;
+
+function gtk_tooltips_paint_window(tip_window: PGtkWidget): gboolean; cdecl;
+begin
+ gtk_paint_flat_box(tip_window^.style, tip_window^.window, GTK_STATE_NORMAL, GTK_SHADOW_OUT,
+ nil, GTK_WIDGET(tip_window), 'tooltip', 0, 0, -1, -1);
+ Result := False;
+end;
+
+
+procedure create_widgets;
+begin
+ if Assigned(tip_window) then Exit;
+ tip_window := GTK_WINDOW(gtk_window_new(GTK_WINDOW_POPUP));
+ gtk_widget_set_app_paintable(GTK_WIDGET(tip_window), True);
+ gtk_window_set_resizable(tip_window, False);
+ gtk_widget_set_name(GTK_WIDGET(tip_window), 'gtk-tooltips');
+ gtk_container_set_border_width(GTK_CONTAINER(tip_window), 4);
+ gtk_widget_set_events(GTK_WIDGET(tip_window), GDK_POINTER_MOTION_MASK or GDK_BUTTON_PRESS_MASK or GDK_KEY_PRESS_MASK or GDK_FOCUS_CHANGE_MASK or GDK_SCROLL_MASK);
+ g_signal_connect(tip_window, 'event', G_CALLBACK(@tip_event_handler), nil);
+ g_signal_connect_swapped(tip_window, 'expose_event', G_CALLBACK(@gtk_tooltips_paint_window), tip_window);
+ g_signal_connect_swapped(tip_window, 'button-press-event', G_CALLBACK(@tips_button_press_event), tip_window);
+
+ tip_label := GTK_LABEL(gtk_label_new(nil));
+ gtk_misc_set_alignment(GTK_MISC(tip_label), 0, 0.5);
+ gtk_widget_show(GTK_WIDGET(tip_label));
+
+ gtk_container_add(GTK_CONTAINER(tip_window), GTK_WIDGET(tip_label));
+end;
+
+procedure FileListTipsHide;
+begin
+ if not Assigned(tip_window) then Exit;
+ if Assigned(data_row) then gtk_tree_path_free(data_row);
+ data_row := nil;
+ data_column := nil;
+
+ if GTK_WIDGET_VISIBLE(tip_window) then begin
+ gtk_widget_hide(GTK_WIDGET(tip_window));
+ if tips_timer = nil then tips_timer := g_timer_new;
+ g_timer_start(tips_timer);
+// gdk_beep();
+// DebugMsg(['FileListTipsHide, Setting last_hide_time to ', last_hide_time]);
+ end;
+
+ if timer_id <> 0 then begin
+ gtk_timeout_remove(timer_id);
+ timer_id := 0;
+ end;
+end;
+
+function show_tip_widget(user_data: gpointer): gboolean; cdecl;
+var Text: PChar;
+ Data: PDataItem;
+ AListView: TGTKListView;
+
+ function ColumnFits(ColNo, ColumnWidth: integer): boolean;
+ var Layout: PPangoLayout;
+ FontDesc: PPangoFontDescription;
+ w, h: Integer;
+ begin
+ Text := Data^.ColumnData[FMain.ColumnSortIDs[ColNo + 1] - 1];
+ Layout := gtk_widget_create_pango_layout(AListView.FWidget, Text);
+ if ConfDirsInBold and Data^.IsDir then begin
+ Text := PChar(Format('<span weight="bold">%s</span>', [QuoteMarkupStr(Text)]));
+ pango_layout_set_markup(Layout, Text, Length(Text));
+ end;
+ if not ConfUseSystemFont then begin
+ FontDesc := pango_font_description_from_string(PChar(ConfPanelFont));
+ pango_layout_set_font_description(Layout, FontDesc);
+ end;
+ pango_layout_get_pixel_size(Layout, @w, @h);
+ g_object_unref(Layout);
+ Result := ColumnWidth > w;
+ end;
+
+var Col, ColID: Integer;
+ Rect: TGdkRectangle;
+ iter: TGtkTreeIter;
+ DataList: TList;
+ TreePath: PGtkTreePath;
+ IsFNameExtColumn, b: boolean;
+begin
+ Result := False;
+ try
+
+ if not gtk_tree_model_get_iter(gtk_tree_view_get_model(data_panel), @iter, data_row) then Exit;
+ if Pointer(data_panel) = Pointer(FMain.LeftListView.FWidget) then begin
+ DataList := LeftPanelData;
+ AListView := FMain.LeftListView;
+ end else begin
+ DataList := RightPanelData;
+ AListView := FMain.RightListView;
+ end;
+
+ if not Application.GTKVersion_2_0_5_Up then gtk_tree_model_get(gtk_tree_view_get_model(data_panel), @iter, 0, @Data, -1)
+ else begin
+ TreePath := gtk_tree_path_new_from_string(PChar(IntToStr(gtk_tree_path_get_indices(data_row)^)));
+ if not Assigned(TreePath) then Exit;
+ AListView.ConvertPathToChild(TreePath);
+ if (TreePath = nil) or (gtk_tree_path_get_indices(TreePath)^ > DataList.Count - 1) then begin
+ FileListTipsHide;
+ Exit;
+ end;
+ Data := DataList[gtk_tree_path_get_indices(TreePath)^];
+ gtk_tree_path_free(TreePath);
+ end;
+ if (Data = nil) or Data^.UpDir then begin
+ FileListTipsHide;
+ Exit;
+ end;
+
+ Col := gtk_tree_view_column_get_sort_column_id(data_column);
+ ColID := FMain.ColumnSortIDs[Col + 1];
+ IsFNameExtColumn := ((ColID = 1) and (ColID < AListView.Columns.Count - 1) and (FMain.ColumnSortIDs[Col + 2] = 3)) or
+ ((ColID = 3) and (ColID > 0) and (FMain.ColumnSortIDs[Col] = 1));
+ gtk_tree_view_get_cell_area(data_panel, data_row, data_column, @rect);
+ if (Col = 0) and ConfUseFileTypeIcons then begin
+ Inc(Rect.x, ConfRowHeightReal + 2);
+ Dec(Rect.width, ConfRowHeightReal + 2);
+ end;
+
+ // Determine if the text fits into the cell area
+ b := ColumnFits(Col, Rect.width - 10);
+ if IsFNameExtColumn then begin
+ gtk_tree_view_get_cell_area(data_panel, data_row, gtk_tree_view_get_column(data_panel, Col - Ord(ColID = 3) + Ord(ColID = 1)), @rect);
+ if ((Col - Ord(ColID = 3) + Ord(ColID = 1)) = 0) and ConfUseFileTypeIcons then begin
+ Inc(Rect.x, ConfRowHeightReal + 2);
+ Dec(Rect.width, ConfRowHeightReal + 2);
+ end;
+ b := b and ColumnFits(Col - Ord(ColID = 3) + Ord(ColID = 1), Rect.width - 10);
+ end;
+
+ if b then begin
+ FileListTipsHide;
+ Exit;
+ end;
+
+ if IsFNameExtColumn then begin
+ Text := PChar(ANSIToUTF8(Data^.AName));
+ gtk_tree_view_get_cell_area(data_panel, data_row, gtk_tree_view_get_column(data_panel, Col - Ord(ColID = 3)), @rect);
+ if ((Col - Ord(ColID = 3)) = 0) and ConfUseFileTypeIcons then begin
+ Inc(Rect.x, ConfRowHeightReal + 2);
+ Dec(Rect.width, ConfRowHeightReal + 2);
+ end;
+ end else Text := Data^.ColumnData[FMain.ColumnSortIDs[Col + 1] - 1];
+ Text := PChar(QuoteMarkupStr(Text));
+
+ if ConfDirsInBold and Data^.IsDir then Text := PChar(Format('<span weight="bold">%s</span>', [Text]));
+ draw_tip_widget(data_panel, Text, Rect.x, Rect.y + Rect.height + 2);
+// if Assigned(text) then g_free(text);
+ if not GTK_WIDGET_VISIBLE(GTK_WIDGET(tip_window)) then gtk_widget_show(GTK_WIDGET(tip_window));
+
+ except
+ on E: Exception do DebugMsg(['*** Exception raised in function show_tip_widget(user_data: gpointer): gboolean (', E.ClassName, '): ', E.Message]);
+ end;
+end;
+
+procedure FileListTipsEnable;
+begin
+ tips_enabled := True and (not ConfDisableFileTips);
+end;
+
+procedure FileListTipsDisable;
+begin
+ tips_enabled := True;
+ FileListTipsHide;
+end;
+
+procedure file_list_tips_hide_tip;
+begin
+ FileListTipsHide;
+end;
+
+function tip_event_handler(widget: PGtkWidget; event: PGdkEvent; user_data: gpointer): gboolean; cdecl;
+begin
+ Result := True;
+ case event^._type of
+ GDK_KEY_PRESS, {GDK_BUTTON_PRESS, }GDK_SCROLL, GDK_FOCUS_CHANGE{, GDK_MOTION_NOTIFY}:
+ begin
+ FileListTipsHide;
+ if tips_timer <> nil then begin
+ g_timer_destroy(tips_timer);
+ tips_timer := nil;
+ end;
+ Exit;
+ end;
+ end;
+ Result := False;
+end;
+
+function on_leave_notify(widget: PGtkWidget; event: PGdkEventCrossing; user_data: gpointer): gboolean; cdecl;
+begin
+ if Assigned(tip_window) and (not GTK_WIDGET_VISIBLE(tip_window))
+ then begin
+ FileListTipsHide;
+ if tips_timer <> nil then begin
+ g_timer_destroy(tips_timer);
+ tips_timer := nil;
+ end;
+ end;
+ Result := False;
+end;
+
+function tips_button_press_event(widget: PGtkWidget; event: PGdkEventButton; user_data: gpointer): gboolean; cdecl;
+var FSelection: PGtkTreeSelection;
+begin
+ if not Assigned(data_panel) then Exit;
+ if not Assigned(data_row) then begin
+ FileListTipsHide;
+ if tips_timer <> nil then begin
+ g_timer_destroy(tips_timer);
+ tips_timer := nil;
+ end;
+ Exit;
+ end;
+
+ FMain.LastClick := Now;
+ gtk_tree_view_set_cursor(data_panel, data_row, nil, False);
+ FSelection := gtk_tree_view_get_selection(data_panel);
+ gtk_tree_selection_select_path(FSelection, data_row);
+ Result := False;
+
+ FileListTipsHide;
+ if tips_timer <> nil then begin
+ g_timer_destroy(tips_timer);
+ tips_timer := nil;
+ end;
+
+ gtk_widget_grab_focus(PGtkWidget(data_panel));
+end;
+
+
+
+initialization
+ timer_id := 0;
+ data_row := nil;
+ tips_timer := nil;
+end.