tdf#122527 dot symbols in chart menubutton not working
a few problems here
*) each symbol was drawn on top of the previous one instead of clearing it
*) some spurious placeholder text in the menu entry
*) m_nSymbolType not set correctly on activation of a submenu entry
*) under gtk3 adding entries to the submenus didn't inform the
menubutton of their addition
*) we can drop m_xMenu because set_item_sensitive can be used
instead, which didn't exist at the initial time of writing
Change-Id: Id339992c4192f3782fddfd56cb3e9b67cfcbe2a2
Reviewed-on: https://gerrit.libreoffice.org/65977
Tested-by: Jenkins
Reviewed-by: Caolán McNamara <caolanm@redhat.com>
Tested-by: Caolán McNamara <caolanm@redhat.com>
diff --git a/cui/source/inc/cuitabline.hxx b/cui/source/inc/cuitabline.hxx
index 639ad64..284a0e6 100644
--- a/cui/source/inc/cuitabline.hxx
+++ b/cui/source/inc/cuitabline.hxx
@@ -148,7 +148,6 @@ private:
std::unique_ptr<weld::MetricSpinButton> m_xMtrEndWidth;
std::unique_ptr<weld::CheckButton> m_xTsbCenterEnd;
std::unique_ptr<weld::CheckButton> m_xCbxSynchronize;
std::unique_ptr<weld::Menu> m_xMenu;
std::unique_ptr<weld::Menu> m_xGalleryMenu;
std::unique_ptr<weld::Menu> m_xSymbolsMenu;
std::unique_ptr<weld::CustomWeld> m_xCtlPreview;
diff --git a/cui/source/tabpages/tpline.cxx b/cui/source/tabpages/tpline.cxx
index c71f5de0..1fb0614 100644
--- a/cui/source/tabpages/tpline.cxx
+++ b/cui/source/tabpages/tpline.cxx
@@ -108,7 +108,6 @@ SvxLineTabPage::SvxLineTabPage(TabPageParent pParent, const SfxItemSet& rInAttrs
, m_xMtrEndWidth(m_xBuilder->weld_metric_spin_button("MTR_FLD_END_WIDTH", FieldUnit::CM))
, m_xTsbCenterEnd(m_xBuilder->weld_check_button("TSB_CENTER_END"))
, m_xCbxSynchronize(m_xBuilder->weld_check_button("CBX_SYNCHRONIZE"))
, m_xMenu(m_xBuilder->weld_menu("menuSELECT"))
, m_xCtlPreview(new weld::CustomWeld(*m_xBuilder, "CTL_PREVIEW", m_aCtlPreview))
, m_xFLEdgeStyle(m_xBuilder->weld_widget("FL_EDGE_STYLE"))
, m_xGridEdgeCaps(m_xBuilder->weld_widget("gridEDGE_CAPS"))
@@ -1410,7 +1409,7 @@ IMPL_LINK_NOARG(SvxLineTabPage, MenuCreateHdl_Impl, weld::ToggleButton&, void)
aBitmap.Scale(nScale, nScale);
}
pVD->SetOutputSizePixel(aBitmap.GetSizePixel(), false);
pVD->SetOutputSizePixel(aBitmap.GetSizePixel());
pVD->DrawBitmapEx(Point(), aBitmap);
m_xGalleryMenu->append(pInfo->sItemId, *pUIName, *pVD);
}
@@ -1422,7 +1421,7 @@ IMPL_LINK_NOARG(SvxLineTabPage, MenuCreateHdl_Impl, weld::ToggleButton&, void)
}
if (m_aGrfNames.empty())
m_xMenu->set_sensitive("gallery", false);
m_xSymbolMB->set_item_sensitive("gallery", false);
}
if (!m_xSymbolsMenu && m_pSymbolList)
@@ -1494,15 +1493,15 @@ IMPL_LINK_NOARG(SvxLineTabPage, MenuCreateHdl_Impl, weld::ToggleButton&, void)
double(MAX_BMP_HEIGHT) / static_cast<double>(aSize.Height());
aBitmapEx.Scale(nScale, nScale);
}
pVD->SetOutputSizePixel(aBitmapEx.GetSizePixel(), false);
pVD->SetOutputSizePixel(aBitmapEx.GetSizePixel());
pVD->DrawBitmapEx(Point(), aBitmapEx);
m_xSymbolsMenu->append(pInfo->sItemId, "foo", *pVD);
m_xSymbolsMenu->append(pInfo->sItemId, "", *pVD);
}
pInvisibleSquare=pPage->RemoveObject(0);
SdrObject::Free(pInvisibleSquare);
if (m_aGrfNames.empty())
m_xMenu->set_sensitive("symbols", false);
m_xSymbolMB->set_item_sensitive("symbols", false);
}
}
}
@@ -1522,10 +1521,12 @@ IMPL_LINK(SvxLineTabPage, GraphicHdl_Impl, const OString&, rIdent, void)
{
SvxBmpItemInfo* pInfo = m_aGalleryBrushItems[sNumber.toUInt32()].get();
pGraphic = pInfo->pBrushItem->GetGraphic();
m_nSymbolType = SVX_SYMBOLTYPE_BRUSHITEM;
}
else if (rIdent.startsWith("symbol", &sNumber))
{
SvxBmpItemInfo* pInfo = m_aSymbolBrushItems[sNumber.toUInt32()].get();
m_nSymbolType = sNumber.toUInt32();
SvxBmpItemInfo* pInfo = m_aSymbolBrushItems[m_nSymbolType].get();
pGraphic = pInfo->pBrushItem->GetGraphic();
}
else if (rIdent == "automatic")
@@ -1559,7 +1560,7 @@ IMPL_LINK(SvxLineTabPage, GraphicHdl_Impl, const OString&, rIdent, void)
return;
}
if(pGraphic)
if (pGraphic)
{
Size aSize = SvxNumberFormat::GetGraphicSizeMM100(pGraphic);
aSize = OutputDevice::LogicToLogic(aSize, MapMode(MapUnit::Map100thMM), MapMode(m_ePoolUnit));
diff --git a/vcl/unx/gtk3/gtk3gtkinst.cxx b/vcl/unx/gtk3/gtk3gtkinst.cxx
index ef712f5..265a921 100644
--- a/vcl/unx/gtk3/gtk3gtkinst.cxx
+++ b/vcl/unx/gtk3/gtk3gtkinst.cxx
@@ -1894,145 +1894,6 @@ public:
}
};
class GtkInstanceMenu : public MenuHelper, public virtual weld::Menu
{
protected:
OString m_sActivated;
private:
virtual void signal_activate(GtkMenuItem* pItem) override
{
const gchar* pStr = gtk_buildable_get_name(GTK_BUILDABLE(pItem));
m_sActivated = OString(pStr, pStr ? strlen(pStr) : 0);
}
public:
GtkInstanceMenu(GtkMenu* pMenu, bool bTakeOwnership)
: MenuHelper(pMenu, bTakeOwnership)
{
}
virtual OString popup_at_rect(weld::Widget* pParent, const tools::Rectangle &rRect) override
{
m_sActivated.clear();
GtkInstanceWidget* pGtkWidget = dynamic_cast<GtkInstanceWidget*>(pParent);
assert(pGtkWidget);
GtkWidget* pWidget = pGtkWidget->getWidget();
gtk_menu_attach_to_widget(m_pMenu, pWidget, nullptr);
//run in a sub main loop because we need to keep vcl PopupMenu alive to use
//it during DispatchCommand, returning now to the outer loop causes the
//launching PopupMenu to be destroyed, instead run the subloop here
//until the gtk menu is destroyed
GMainLoop* pLoop = g_main_loop_new(nullptr, true);
gulong nSignalId = g_signal_connect_swapped(G_OBJECT(m_pMenu), "deactivate", G_CALLBACK(g_main_loop_quit), pLoop);
#if GTK_CHECK_VERSION(3,22,0)
if (gtk_check_version(3, 22, 0) == nullptr)
{
GdkRectangle aRect{static_cast<int>(rRect.Left()), static_cast<int>(rRect.Top()),
static_cast<int>(rRect.GetWidth()), static_cast<int>(rRect.GetHeight())};
if (AllSettings::GetLayoutRTL())
aRect.x = gtk_widget_get_allocated_width(pWidget) - aRect.width - 1 - aRect.x;
gtk_menu_popup_at_rect(m_pMenu, gtk_widget_get_window(pWidget), &aRect, GDK_GRAVITY_NORTH_WEST, GDK_GRAVITY_NORTH_WEST, nullptr);
}
else
#else
(void) rRect;
#endif
{
guint nButton;
guint32 nTime;
//typically there is an event, and we can then distinguish if this was
//launched from the keyboard (gets auto-mnemoniced) or the mouse (which
//doesn't)
GdkEvent *pEvent = gtk_get_current_event();
if (pEvent)
{
gdk_event_get_button(pEvent, &nButton);
nTime = gdk_event_get_time(pEvent);
}
else
{
nButton = 0;
nTime = GtkSalFrame::GetLastInputEventTime();
}
gtk_menu_popup(m_pMenu, nullptr, nullptr, nullptr, nullptr, nButton, nTime);
}
if (g_main_loop_is_running(pLoop))
{
gdk_threads_leave();
g_main_loop_run(pLoop);
gdk_threads_enter();
}
g_main_loop_unref(pLoop);
g_signal_handler_disconnect(m_pMenu, nSignalId);
return m_sActivated;
}
virtual void set_sensitive(const OString& rIdent, bool bSensitive) override
{
set_item_sensitive(rIdent, bSensitive);
}
virtual void set_active(const OString& rIdent, bool bActive) override
{
set_item_active(rIdent, bActive);
}
virtual void show(const OString& rIdent, bool bShow) override
{
show_item(rIdent, bShow);
}
virtual void insert(int pos, const OUString& rId, const OUString& rStr,
const OUString* pIconName, VirtualDevice* pImageSufface,
bool bCheck) override
{
GtkWidget* pImage = nullptr;
if (pIconName)
{
GdkPixbuf* pixbuf = load_icon_by_name(*pIconName);
if (!pixbuf)
{
pImage = gtk_image_new_from_pixbuf(pixbuf);
g_object_unref(pixbuf);
}
}
else if (pImageSufface)
pImage = gtk_image_new_from_surface(get_underlying_cairo_surface(*pImageSufface));
GtkWidget *pItem;
if (pImage)
{
GtkWidget *pBox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
GtkWidget *pLabel = gtk_label_new(MapToGtkAccelerator(rStr).getStr());
pItem = bCheck ? gtk_check_menu_item_new() : gtk_menu_item_new();
gtk_container_add(GTK_CONTAINER(pBox), pImage);
gtk_container_add(GTK_CONTAINER(pBox), pLabel);
gtk_container_add(GTK_CONTAINER(pItem), pBox);
gtk_widget_show_all(pItem);
}
else
{
pItem = bCheck ? gtk_check_menu_item_new_with_label(MapToGtkAccelerator(rStr).getStr())
: gtk_menu_item_new_with_label(MapToGtkAccelerator(rStr).getStr());
}
gtk_buildable_set_name(GTK_BUILDABLE(pItem), OUStringToOString(rId, RTL_TEXTENCODING_UTF8).getStr());
gtk_menu_shell_append(GTK_MENU_SHELL(m_pMenu), pItem);
gtk_widget_show(pItem);
add_to_map(GTK_MENU_ITEM(pItem));
if (pos != -1)
gtk_menu_reorder_child(m_pMenu, pItem, pos);
}
};
class GtkInstanceSizeGroup : public weld::SizeGroup
{
private:
@@ -4233,6 +4094,190 @@ public:
}
};
class GtkInstanceMenu : public MenuHelper, public virtual weld::Menu
{
protected:
OString m_sActivated;
GtkInstanceMenuButton* m_pTopLevelMenuButton;
private:
virtual void signal_activate(GtkMenuItem* pItem) override
{
const gchar* pStr = gtk_buildable_get_name(GTK_BUILDABLE(pItem));
m_sActivated = OString(pStr, pStr ? strlen(pStr) : 0);
}
public:
GtkInstanceMenu(GtkMenu* pMenu, bool bTakeOwnership)
: MenuHelper(pMenu, bTakeOwnership)
, m_pTopLevelMenuButton(nullptr)
{
// tdf#122527 if we're welding a submenu of a menu of a MenuButton,
// then find that MenuButton parent so that when adding items to this
// menu we can inform the MenuButton of their addition
GtkMenu* pTopLevelMenu = pMenu;
while (true)
{
GtkWidget* pAttached = gtk_menu_get_attach_widget(pTopLevelMenu);
if (!pAttached || !GTK_IS_MENU_ITEM(pAttached))
break;
GtkWidget* pParent = gtk_widget_get_parent(pAttached);
if (!pParent || !GTK_IS_MENU(pParent))
break;
pTopLevelMenu = GTK_MENU(pParent);
}
if (pTopLevelMenu != pMenu)
{
GtkWidget* pAttached = gtk_menu_get_attach_widget(pTopLevelMenu);
if (pAttached && GTK_IS_MENU_BUTTON(pAttached))
{
void* pData = g_object_get_data(G_OBJECT(pAttached), "g-lo-GtkInstanceButton");
m_pTopLevelMenuButton = dynamic_cast<GtkInstanceMenuButton*>(static_cast<GtkInstanceButton*>(pData));
}
}
}
virtual OString popup_at_rect(weld::Widget* pParent, const tools::Rectangle &rRect) override
{
m_sActivated.clear();
GtkInstanceWidget* pGtkWidget = dynamic_cast<GtkInstanceWidget*>(pParent);
assert(pGtkWidget);
GtkWidget* pWidget = pGtkWidget->getWidget();
gtk_menu_attach_to_widget(m_pMenu, pWidget, nullptr);
//run in a sub main loop because we need to keep vcl PopupMenu alive to use
//it during DispatchCommand, returning now to the outer loop causes the
//launching PopupMenu to be destroyed, instead run the subloop here
//until the gtk menu is destroyed
GMainLoop* pLoop = g_main_loop_new(nullptr, true);
gulong nSignalId = g_signal_connect_swapped(G_OBJECT(m_pMenu), "deactivate", G_CALLBACK(g_main_loop_quit), pLoop);
#if GTK_CHECK_VERSION(3,22,0)
if (gtk_check_version(3, 22, 0) == nullptr)
{
GdkRectangle aRect{static_cast<int>(rRect.Left()), static_cast<int>(rRect.Top()),
static_cast<int>(rRect.GetWidth()), static_cast<int>(rRect.GetHeight())};
if (AllSettings::GetLayoutRTL())
aRect.x = gtk_widget_get_allocated_width(pWidget) - aRect.width - 1 - aRect.x;
gtk_menu_popup_at_rect(m_pMenu, gtk_widget_get_window(pWidget), &aRect, GDK_GRAVITY_NORTH_WEST, GDK_GRAVITY_NORTH_WEST, nullptr);
}
else
#else
(void) rRect;
#endif
{
guint nButton;
guint32 nTime;
//typically there is an event, and we can then distinguish if this was
//launched from the keyboard (gets auto-mnemoniced) or the mouse (which
//doesn't)
GdkEvent *pEvent = gtk_get_current_event();
if (pEvent)
{
gdk_event_get_button(pEvent, &nButton);
nTime = gdk_event_get_time(pEvent);
}
else
{
nButton = 0;
nTime = GtkSalFrame::GetLastInputEventTime();
}
gtk_menu_popup(m_pMenu, nullptr, nullptr, nullptr, nullptr, nButton, nTime);
}
if (g_main_loop_is_running(pLoop))
{
gdk_threads_leave();
g_main_loop_run(pLoop);
gdk_threads_enter();
}
g_main_loop_unref(pLoop);
g_signal_handler_disconnect(m_pMenu, nSignalId);
return m_sActivated;
}
virtual void set_sensitive(const OString& rIdent, bool bSensitive) override
{
set_item_sensitive(rIdent, bSensitive);
}
virtual void set_active(const OString& rIdent, bool bActive) override
{
set_item_active(rIdent, bActive);
}
virtual void show(const OString& rIdent, bool bShow) override
{
show_item(rIdent, bShow);
}
virtual void insert(int pos, const OUString& rId, const OUString& rStr,
const OUString* pIconName, VirtualDevice* pImageSufface,
bool bCheck) override
{
GtkWidget* pImage = nullptr;
if (pIconName)
{
GdkPixbuf* pixbuf = load_icon_by_name(*pIconName);
if (!pixbuf)
{
pImage = gtk_image_new_from_pixbuf(pixbuf);
g_object_unref(pixbuf);
}
}
else if (pImageSufface)
{
cairo_surface_t* surface = get_underlying_cairo_surface(*pImageSufface);
Size aSize(pImageSufface->GetOutputSizePixel());
cairo_surface_t* target = cairo_surface_create_similar(surface,
cairo_surface_get_content(surface),
aSize.Width(),
aSize.Height());
cairo_t* cr = cairo_create(target);
cairo_set_source_surface(cr, surface, 0, 0);
cairo_paint(cr);
cairo_destroy(cr);
pImage = gtk_image_new_from_surface(target);
cairo_surface_destroy(target);
}
GtkWidget *pItem;
if (pImage)
{
GtkWidget *pBox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
GtkWidget *pLabel = gtk_label_new(MapToGtkAccelerator(rStr).getStr());
pItem = bCheck ? gtk_check_menu_item_new() : gtk_menu_item_new();
gtk_container_add(GTK_CONTAINER(pBox), pImage);
gtk_container_add(GTK_CONTAINER(pBox), pLabel);
gtk_container_add(GTK_CONTAINER(pItem), pBox);
gtk_widget_show_all(pItem);
}
else
{
pItem = bCheck ? gtk_check_menu_item_new_with_label(MapToGtkAccelerator(rStr).getStr())
: gtk_menu_item_new_with_label(MapToGtkAccelerator(rStr).getStr());
}
gtk_buildable_set_name(GTK_BUILDABLE(pItem), OUStringToOString(rId, RTL_TEXTENCODING_UTF8).getStr());
gtk_menu_shell_append(GTK_MENU_SHELL(m_pMenu), pItem);
gtk_widget_show(pItem);
GtkMenuItem* pMenuItem = GTK_MENU_ITEM(pItem);
add_to_map(pMenuItem);
if (m_pTopLevelMenuButton)
m_pTopLevelMenuButton->add_to_map(pMenuItem);
if (pos != -1)
gtk_menu_reorder_child(m_pMenu, pItem, pos);
}
};
class GtkInstanceRadioButton : public GtkInstanceToggleButton, public virtual weld::RadioButton
{
public: