diff options
| author | Tomas Bzatek <tbzatek@users.sourceforge.net> | 2008-06-07 20:34:49 +0200 |
|---|---|---|
| committer | Tomas Bzatek <tbzatek@users.sourceforge.net> | 2008-06-07 20:34:49 +0200 |
| commit | ecde167da74c86bc047aaf84c5e548cf65a5da98 (patch) | |
| tree | a015dfda84f28a65811e3aa0d369f8f211ec8c60 /UToolTips.pas | |
| download | tuxcmd-ecde167da74c86bc047aaf84c5e548cf65a5da98.tar.xz | |
Initial commitv0.6.36release-0.6.36-dev
Diffstat (limited to 'UToolTips.pas')
| -rw-r--r-- | UToolTips.pas | 401 |
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. |
